sort.c 31.7 KB
Newer Older
1
#include "sort.h"
2
#include "hist.h"
3
#include "comm.h"
4
#include "symbol.h"
5
#include "evsel.h"
6 7

regex_t		parent_regex;
8 9 10
const char	default_parent_pattern[] = "^sys_|^do_page_fault";
const char	*parent_pattern = default_parent_pattern;
const char	default_sort_order[] = "comm,dso,symbol";
11 12 13 14 15
const char	default_branch_sort_order[] = "comm,dso_from,symbol_from,dso_to,symbol_to";
const char	default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
const char	default_top_sort_order[] = "dso,symbol";
const char	default_diff_sort_order[] = "dso,symbol";
const char	*sort_order;
16 17
regex_t		ignore_callees_regex;
int		have_ignore_callees = 0;
18 19
int		sort__need_collapse = 0;
int		sort__has_parent = 0;
N
Namhyung Kim 已提交
20
int		sort__has_sym = 0;
N
Namhyung Kim 已提交
21
int		sort__has_dso = 0;
22
enum sort_mode	sort__mode = SORT_MODE__NORMAL;
23 24

enum sort_type	sort__first_dimension;
25 26 27

LIST_HEAD(hist_entry__sort_list);

28
static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
29 30 31 32 33
{
	int n;
	va_list ap;

	va_start(ap, fmt);
34
	n = vsnprintf(bf, size, fmt, ap);
35
	if (symbol_conf.field_sep && n > 0) {
36 37 38
		char *sep = bf;

		while (1) {
39
			sep = strchr(sep, *symbol_conf.field_sep);
40 41 42
			if (sep == NULL)
				break;
			*sep = '.';
43 44 45
		}
	}
	va_end(ap);
46 47 48

	if (n >= (int)size)
		return size - 1;
49 50 51
	return n;
}

52
static int64_t cmp_null(const void *l, const void *r)
53 54 55 56 57 58 59 60 61 62 63 64 65 66
{
	if (!l && !r)
		return 0;
	else if (!l)
		return -1;
	else
		return 1;
}

/* --sort pid */

static int64_t
sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
{
67
	return right->thread->tid - left->thread->tid;
68 69
}

70
static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,
71
				       size_t size, unsigned int width)
72
{
73
	const char *comm = thread__comm_str(he->thread);
N
Namhyung Kim 已提交
74
	return repsep_snprintf(bf, size, "%*s:%5d", width - 6,
75
			       comm ?: "", he->thread->tid);
76 77
}

78 79 80 81 82 83 84 85 86 87 88 89
struct sort_entry sort_thread = {
	.se_header	= "Command:  Pid",
	.se_cmp		= sort__thread_cmp,
	.se_snprintf	= hist_entry__thread_snprintf,
	.se_width_idx	= HISTC_THREAD,
};

/* --sort comm */

static int64_t
sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
{
90
	/* Compare the addr that should be unique among comm */
91
	return comm__str(right->comm) - comm__str(left->comm);
92 93 94 95 96
}

static int64_t
sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
{
97 98
	/* Compare the addr that should be unique among comm */
	return comm__str(right->comm) - comm__str(left->comm);
99 100
}

101
static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
102
				     size_t size, unsigned int width)
103
{
104
	return repsep_snprintf(bf, size, "%*s", width, comm__str(he->comm));
105 106
}

107 108 109 110 111 112 113 114 115 116
struct sort_entry sort_comm = {
	.se_header	= "Command",
	.se_cmp		= sort__comm_cmp,
	.se_collapse	= sort__comm_collapse,
	.se_snprintf	= hist_entry__comm_snprintf,
	.se_width_idx	= HISTC_COMM,
};

/* --sort dso */

117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
{
	struct dso *dso_l = map_l ? map_l->dso : NULL;
	struct dso *dso_r = map_r ? map_r->dso : NULL;
	const char *dso_name_l, *dso_name_r;

	if (!dso_l || !dso_r)
		return cmp_null(dso_l, dso_r);

	if (verbose) {
		dso_name_l = dso_l->long_name;
		dso_name_r = dso_r->long_name;
	} else {
		dso_name_l = dso_l->short_name;
		dso_name_r = dso_r->short_name;
	}

	return strcmp(dso_name_l, dso_name_r);
}

137
static int64_t
138 139
sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
{
140 141
	return _sort__dso_cmp(left->ms.map, right->ms.map);
}
142

143 144 145 146 147 148 149 150 151 152 153 154
static int _hist_entry__dso_snprintf(struct map *map, char *bf,
				     size_t size, unsigned int width)
{
	if (map && map->dso) {
		const char *dso_name = !verbose ? map->dso->short_name :
			map->dso->long_name;
		return repsep_snprintf(bf, size, "%-*s", width, dso_name);
	}

	return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
}

155
static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,
156 157
				    size_t size, unsigned int width)
{
158
	return _hist_entry__dso_snprintf(he->ms.map, bf, size, width);
159 160 161 162 163 164 165 166 167 168
}

struct sort_entry sort_dso = {
	.se_header	= "Shared Object",
	.se_cmp		= sort__dso_cmp,
	.se_snprintf	= hist_entry__dso_snprintf,
	.se_width_idx	= HISTC_DSO,
};

/* --sort symbol */
169

170 171 172 173 174
static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip)
{
	return (int64_t)(right_ip - left_ip);
}

175
static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
176
{
177 178
	u64 ip_l, ip_r;

179 180 181 182 183 184
	if (!sym_l || !sym_r)
		return cmp_null(sym_l, sym_r);

	if (sym_l == sym_r)
		return 0;

185 186
	ip_l = sym_l->start;
	ip_r = sym_r->start;
187 188 189 190

	return (int64_t)(ip_r - ip_l);
}

191 192
static int64_t
sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
193
{
194 195
	int64_t ret;

196
	if (!left->ms.sym && !right->ms.sym)
197
		return _sort__addr_cmp(left->ip, right->ip);
198

199 200 201 202
	/*
	 * comparing symbol address alone is not enough since it's a
	 * relative address within a dso.
	 */
N
Namhyung Kim 已提交
203 204 205 206 207
	if (!sort__has_dso) {
		ret = sort__dso_cmp(left, right);
		if (ret != 0)
			return ret;
	}
208

209
	return _sort__sym_cmp(left->ms.sym, right->ms.sym);
210 211 212 213
}

static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
				     u64 ip, char level, char *bf, size_t size,
214
				     unsigned int width)
215 216 217 218 219 220
{
	size_t ret = 0;

	if (verbose) {
		char o = map ? dso__symtab_origin(map->dso) : '!';
		ret += repsep_snprintf(bf, size, "%-#*llx %c ",
221
				       BITS_PER_LONG / 4 + 2, ip, o);
222
	}
223

224
	ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
225 226 227 228
	if (sym && map) {
		if (map->type == MAP__VARIABLE) {
			ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
			ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
229
					ip - map->unmap_ip(map, sym->start));
230 231 232 233 234 235 236 237
			ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
				       width - ret, "");
		} else {
			ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
					       width - ret,
					       sym->name);
		}
	} else {
238 239 240 241 242 243 244 245
		size_t len = BITS_PER_LONG / 4;
		ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
				       len, ip);
		ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
				       width - ret, "");
	}

	return ret;
246 247
}

248
static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,
249
				    size_t size, unsigned int width)
250
{
251 252
	return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip,
					 he->level, bf, size, width);
253
}
254

255 256 257 258 259 260
struct sort_entry sort_sym = {
	.se_header	= "Symbol",
	.se_cmp		= sort__sym_cmp,
	.se_snprintf	= hist_entry__sym_snprintf,
	.se_width_idx	= HISTC_SYMBOL,
};
261

262 263 264 265 266
/* --sort srcline */

static int64_t
sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
{
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
	if (!left->srcline) {
		if (!left->ms.map)
			left->srcline = SRCLINE_UNKNOWN;
		else {
			struct map *map = left->ms.map;
			left->srcline = get_srcline(map->dso,
					    map__rip_2objdump(map, left->ip));
		}
	}
	if (!right->srcline) {
		if (!right->ms.map)
			right->srcline = SRCLINE_UNKNOWN;
		else {
			struct map *map = right->ms.map;
			right->srcline = get_srcline(map->dso,
					    map__rip_2objdump(map, right->ip));
		}
	}
	return strcmp(left->srcline, right->srcline);
286 287
}

288
static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
289 290
					size_t size,
					unsigned int width __maybe_unused)
291
{
292
	return repsep_snprintf(bf, size, "%s", he->srcline);
293 294 295 296 297 298 299 300 301
}

struct sort_entry sort_srcline = {
	.se_header	= "Source:Line",
	.se_cmp		= sort__srcline_cmp,
	.se_snprintf	= hist_entry__srcline_snprintf,
	.se_width_idx	= HISTC_SRCLINE,
};

302 303
/* --sort parent */

304
static int64_t
305 306 307 308 309 310 311 312 313 314 315
sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
{
	struct symbol *sym_l = left->parent;
	struct symbol *sym_r = right->parent;

	if (!sym_l || !sym_r)
		return cmp_null(sym_l, sym_r);

	return strcmp(sym_l->name, sym_r->name);
}

316
static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
317
				       size_t size, unsigned int width)
318
{
319
	return repsep_snprintf(bf, size, "%-*s", width,
320
			      he->parent ? he->parent->name : "[other]");
321 322
}

323 324 325 326 327 328 329
struct sort_entry sort_parent = {
	.se_header	= "Parent symbol",
	.se_cmp		= sort__parent_cmp,
	.se_snprintf	= hist_entry__parent_snprintf,
	.se_width_idx	= HISTC_PARENT,
};

A
Arun Sharma 已提交
330 331
/* --sort cpu */

332
static int64_t
A
Arun Sharma 已提交
333 334 335 336 337
sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
{
	return right->cpu - left->cpu;
}

338 339
static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf,
				    size_t size, unsigned int width)
A
Arun Sharma 已提交
340
{
341
	return repsep_snprintf(bf, size, "%*d", width, he->cpu);
A
Arun Sharma 已提交
342 343
}

344 345 346 347 348 349 350
struct sort_entry sort_cpu = {
	.se_header      = "CPU",
	.se_cmp	        = sort__cpu_cmp,
	.se_snprintf    = hist_entry__cpu_snprintf,
	.se_width_idx	= HISTC_CPU,
};

351 352
/* sort keys for branch stacks */

353 354 355 356 357 358 359
static int64_t
sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
{
	return _sort__dso_cmp(left->branch_info->from.map,
			      right->branch_info->from.map);
}

360
static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,
361 362
				    size_t size, unsigned int width)
{
363
	return _hist_entry__dso_snprintf(he->branch_info->from.map,
364 365 366 367 368 369 370 371 372 373
					 bf, size, width);
}

static int64_t
sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
{
	return _sort__dso_cmp(left->branch_info->to.map,
			      right->branch_info->to.map);
}

374
static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,
375 376
				       size_t size, unsigned int width)
{
377
	return _hist_entry__dso_snprintf(he->branch_info->to.map,
378 379 380 381 382 383 384 385 386 387
					 bf, size, width);
}

static int64_t
sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
{
	struct addr_map_symbol *from_l = &left->branch_info->from;
	struct addr_map_symbol *from_r = &right->branch_info->from;

	if (!from_l->sym && !from_r->sym)
388
		return _sort__addr_cmp(from_l->addr, from_r->addr);
389

390
	return _sort__sym_cmp(from_l->sym, from_r->sym);
391 392 393 394 395 396 397 398 399
}

static int64_t
sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
{
	struct addr_map_symbol *to_l = &left->branch_info->to;
	struct addr_map_symbol *to_r = &right->branch_info->to;

	if (!to_l->sym && !to_r->sym)
400
		return _sort__addr_cmp(to_l->addr, to_r->addr);
401

402
	return _sort__sym_cmp(to_l->sym, to_r->sym);
403 404
}

405
static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,
406
					 size_t size, unsigned int width)
407
{
408
	struct addr_map_symbol *from = &he->branch_info->from;
409
	return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
410
					 he->level, bf, size, width);
411 412 413

}

414
static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,
415
				       size_t size, unsigned int width)
416
{
417
	struct addr_map_symbol *to = &he->branch_info->to;
418
	return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
419
					 he->level, bf, size, width);
420 421 422

}

423 424 425 426 427 428 429
struct sort_entry sort_dso_from = {
	.se_header	= "Source Shared Object",
	.se_cmp		= sort__dso_from_cmp,
	.se_snprintf	= hist_entry__dso_from_snprintf,
	.se_width_idx	= HISTC_DSO_FROM,
};

430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
struct sort_entry sort_dso_to = {
	.se_header	= "Target Shared Object",
	.se_cmp		= sort__dso_to_cmp,
	.se_snprintf	= hist_entry__dso_to_snprintf,
	.se_width_idx	= HISTC_DSO_TO,
};

struct sort_entry sort_sym_from = {
	.se_header	= "Source Symbol",
	.se_cmp		= sort__sym_from_cmp,
	.se_snprintf	= hist_entry__sym_from_snprintf,
	.se_width_idx	= HISTC_SYMBOL_FROM,
};

struct sort_entry sort_sym_to = {
	.se_header	= "Target Symbol",
	.se_cmp		= sort__sym_to_cmp,
	.se_snprintf	= hist_entry__sym_to_snprintf,
	.se_width_idx	= HISTC_SYMBOL_TO,
};

static int64_t
sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
{
	const unsigned char mp = left->branch_info->flags.mispred !=
					right->branch_info->flags.mispred;
	const unsigned char p = left->branch_info->flags.predicted !=
					right->branch_info->flags.predicted;

	return mp || p;
}

462
static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,
463 464 465
				    size_t size, unsigned int width){
	static const char *out = "N/A";

466
	if (he->branch_info->flags.predicted)
467
		out = "N";
468
	else if (he->branch_info->flags.mispred)
469 470 471 472 473
		out = "Y";

	return repsep_snprintf(bf, size, "%-*s", width, out);
}

474 475 476 477 478 479 480 481 482 483 484 485 486 487
/* --sort daddr_sym */
static int64_t
sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
{
	uint64_t l = 0, r = 0;

	if (left->mem_info)
		l = left->mem_info->daddr.addr;
	if (right->mem_info)
		r = right->mem_info->daddr.addr;

	return (int64_t)(r - l);
}

488
static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,
489 490 491 492 493 494
				    size_t size, unsigned int width)
{
	uint64_t addr = 0;
	struct map *map = NULL;
	struct symbol *sym = NULL;

495 496 497 498
	if (he->mem_info) {
		addr = he->mem_info->daddr.addr;
		map = he->mem_info->daddr.map;
		sym = he->mem_info->daddr.sym;
499
	}
500
	return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
					 width);
}

static int64_t
sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
{
	struct map *map_l = NULL;
	struct map *map_r = NULL;

	if (left->mem_info)
		map_l = left->mem_info->daddr.map;
	if (right->mem_info)
		map_r = right->mem_info->daddr.map;

	return _sort__dso_cmp(map_l, map_r);
}

518
static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,
519 520 521 522
				    size_t size, unsigned int width)
{
	struct map *map = NULL;

523 524
	if (he->mem_info)
		map = he->mem_info->daddr.map;
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547

	return _hist_entry__dso_snprintf(map, bf, size, width);
}

static int64_t
sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
{
	union perf_mem_data_src data_src_l;
	union perf_mem_data_src data_src_r;

	if (left->mem_info)
		data_src_l = left->mem_info->data_src;
	else
		data_src_l.mem_lock = PERF_MEM_LOCK_NA;

	if (right->mem_info)
		data_src_r = right->mem_info->data_src;
	else
		data_src_r.mem_lock = PERF_MEM_LOCK_NA;

	return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
}

548
static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,
549 550 551 552 553
				    size_t size, unsigned int width)
{
	const char *out;
	u64 mask = PERF_MEM_LOCK_NA;

554 555
	if (he->mem_info)
		mask = he->mem_info->data_src.mem_lock;
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596

	if (mask & PERF_MEM_LOCK_NA)
		out = "N/A";
	else if (mask & PERF_MEM_LOCK_LOCKED)
		out = "Yes";
	else
		out = "No";

	return repsep_snprintf(bf, size, "%-*s", width, out);
}

static int64_t
sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
{
	union perf_mem_data_src data_src_l;
	union perf_mem_data_src data_src_r;

	if (left->mem_info)
		data_src_l = left->mem_info->data_src;
	else
		data_src_l.mem_dtlb = PERF_MEM_TLB_NA;

	if (right->mem_info)
		data_src_r = right->mem_info->data_src;
	else
		data_src_r.mem_dtlb = PERF_MEM_TLB_NA;

	return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
}

static const char * const tlb_access[] = {
	"N/A",
	"HIT",
	"MISS",
	"L1",
	"L2",
	"Walker",
	"Fault",
};
#define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))

597
static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,
598 599 600 601 602 603 604 605 606 607
				    size_t size, unsigned int width)
{
	char out[64];
	size_t sz = sizeof(out) - 1; /* -1 for null termination */
	size_t l = 0, i;
	u64 m = PERF_MEM_TLB_NA;
	u64 hit, miss;

	out[0] = '\0';

608 609
	if (he->mem_info)
		m = he->mem_info->data_src.mem_dtlb;
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673

	hit = m & PERF_MEM_TLB_HIT;
	miss = m & PERF_MEM_TLB_MISS;

	/* already taken care of */
	m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);

	for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) {
		if (!(m & 0x1))
			continue;
		if (l) {
			strcat(out, " or ");
			l += 4;
		}
		strncat(out, tlb_access[i], sz - l);
		l += strlen(tlb_access[i]);
	}
	if (*out == '\0')
		strcpy(out, "N/A");
	if (hit)
		strncat(out, " hit", sz - l);
	if (miss)
		strncat(out, " miss", sz - l);

	return repsep_snprintf(bf, size, "%-*s", width, out);
}

static int64_t
sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
{
	union perf_mem_data_src data_src_l;
	union perf_mem_data_src data_src_r;

	if (left->mem_info)
		data_src_l = left->mem_info->data_src;
	else
		data_src_l.mem_lvl = PERF_MEM_LVL_NA;

	if (right->mem_info)
		data_src_r = right->mem_info->data_src;
	else
		data_src_r.mem_lvl = PERF_MEM_LVL_NA;

	return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
}

static const char * const mem_lvl[] = {
	"N/A",
	"HIT",
	"MISS",
	"L1",
	"LFB",
	"L2",
	"L3",
	"Local RAM",
	"Remote RAM (1 hop)",
	"Remote RAM (2 hops)",
	"Remote Cache (1 hop)",
	"Remote Cache (2 hops)",
	"I/O",
	"Uncached",
};
#define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))

674
static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,
675 676 677 678 679 680 681 682
				    size_t size, unsigned int width)
{
	char out[64];
	size_t sz = sizeof(out) - 1; /* -1 for null termination */
	size_t i, l = 0;
	u64 m =  PERF_MEM_LVL_NA;
	u64 hit, miss;

683 684
	if (he->mem_info)
		m  = he->mem_info->data_src.mem_lvl;
685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741

	out[0] = '\0';

	hit = m & PERF_MEM_LVL_HIT;
	miss = m & PERF_MEM_LVL_MISS;

	/* already taken care of */
	m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);

	for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) {
		if (!(m & 0x1))
			continue;
		if (l) {
			strcat(out, " or ");
			l += 4;
		}
		strncat(out, mem_lvl[i], sz - l);
		l += strlen(mem_lvl[i]);
	}
	if (*out == '\0')
		strcpy(out, "N/A");
	if (hit)
		strncat(out, " hit", sz - l);
	if (miss)
		strncat(out, " miss", sz - l);

	return repsep_snprintf(bf, size, "%-*s", width, out);
}

static int64_t
sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
{
	union perf_mem_data_src data_src_l;
	union perf_mem_data_src data_src_r;

	if (left->mem_info)
		data_src_l = left->mem_info->data_src;
	else
		data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;

	if (right->mem_info)
		data_src_r = right->mem_info->data_src;
	else
		data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;

	return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
}

static const char * const snoop_access[] = {
	"N/A",
	"None",
	"Miss",
	"Hit",
	"HitM",
};
#define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))

742
static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
743 744 745 746 747 748 749 750 751
				    size_t size, unsigned int width)
{
	char out[64];
	size_t sz = sizeof(out) - 1; /* -1 for null termination */
	size_t i, l = 0;
	u64 m = PERF_MEM_SNOOP_NA;

	out[0] = '\0';

752 753
	if (he->mem_info)
		m = he->mem_info->data_src.mem_snoop;
754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771

	for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {
		if (!(m & 0x1))
			continue;
		if (l) {
			strcat(out, " or ");
			l += 4;
		}
		strncat(out, snoop_access[i], sz - l);
		l += strlen(snoop_access[i]);
	}

	if (*out == '\0')
		strcpy(out, "N/A");

	return repsep_snprintf(bf, size, "%-*s", width, out);
}

772 773 774 775 776 777 778
struct sort_entry sort_mispredict = {
	.se_header	= "Branch Mispredicted",
	.se_cmp		= sort__mispredict_cmp,
	.se_snprintf	= hist_entry__mispredict_snprintf,
	.se_width_idx	= HISTC_MISPREDICT,
};

779 780 781 782 783 784 785 786 787 788 789
static u64 he_weight(struct hist_entry *he)
{
	return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
}

static int64_t
sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
{
	return he_weight(left) - he_weight(right);
}

790
static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,
791 792
				    size_t size, unsigned int width)
{
793
	return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he));
794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
}

struct sort_entry sort_local_weight = {
	.se_header	= "Local Weight",
	.se_cmp		= sort__local_weight_cmp,
	.se_snprintf	= hist_entry__local_weight_snprintf,
	.se_width_idx	= HISTC_LOCAL_WEIGHT,
};

static int64_t
sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
{
	return left->stat.weight - right->stat.weight;
}

809
static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,
810 811
					      size_t size, unsigned int width)
{
812
	return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight);
813 814 815 816 817 818 819 820 821
}

struct sort_entry sort_global_weight = {
	.se_header	= "Weight",
	.se_cmp		= sort__global_weight_cmp,
	.se_snprintf	= hist_entry__global_weight_snprintf,
	.se_width_idx	= HISTC_GLOBAL_WEIGHT,
};

822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
struct sort_entry sort_mem_daddr_sym = {
	.se_header	= "Data Symbol",
	.se_cmp		= sort__daddr_cmp,
	.se_snprintf	= hist_entry__daddr_snprintf,
	.se_width_idx	= HISTC_MEM_DADDR_SYMBOL,
};

struct sort_entry sort_mem_daddr_dso = {
	.se_header	= "Data Object",
	.se_cmp		= sort__dso_daddr_cmp,
	.se_snprintf	= hist_entry__dso_daddr_snprintf,
	.se_width_idx	= HISTC_MEM_DADDR_SYMBOL,
};

struct sort_entry sort_mem_locked = {
	.se_header	= "Locked",
	.se_cmp		= sort__locked_cmp,
	.se_snprintf	= hist_entry__locked_snprintf,
	.se_width_idx	= HISTC_MEM_LOCKED,
};

struct sort_entry sort_mem_tlb = {
	.se_header	= "TLB access",
	.se_cmp		= sort__tlb_cmp,
	.se_snprintf	= hist_entry__tlb_snprintf,
	.se_width_idx	= HISTC_MEM_TLB,
};

struct sort_entry sort_mem_lvl = {
	.se_header	= "Memory access",
	.se_cmp		= sort__lvl_cmp,
	.se_snprintf	= hist_entry__lvl_snprintf,
	.se_width_idx	= HISTC_MEM_LVL,
};

struct sort_entry sort_mem_snoop = {
	.se_header	= "Snoop",
	.se_cmp		= sort__snoop_cmp,
	.se_snprintf	= hist_entry__snoop_snprintf,
	.se_width_idx	= HISTC_MEM_SNOOP,
};

864 865 866 867 868 869 870
static int64_t
sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
{
	return left->branch_info->flags.abort !=
		right->branch_info->flags.abort;
}

871
static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf,
872 873 874 875
				    size_t size, unsigned int width)
{
	static const char *out = ".";

876
	if (he->branch_info->flags.abort)
877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894
		out = "A";
	return repsep_snprintf(bf, size, "%-*s", width, out);
}

struct sort_entry sort_abort = {
	.se_header	= "Transaction abort",
	.se_cmp		= sort__abort_cmp,
	.se_snprintf	= hist_entry__abort_snprintf,
	.se_width_idx	= HISTC_ABORT,
};

static int64_t
sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right)
{
	return left->branch_info->flags.in_tx !=
		right->branch_info->flags.in_tx;
}

895
static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf,
896 897 898 899
				    size_t size, unsigned int width)
{
	static const char *out = ".";

900
	if (he->branch_info->flags.in_tx)
901 902 903 904 905 906 907 908 909 910 911 912
		out = "T";

	return repsep_snprintf(bf, size, "%-*s", width, out);
}

struct sort_entry sort_in_tx = {
	.se_header	= "Branch in transaction",
	.se_cmp		= sort__in_tx_cmp,
	.se_snprintf	= hist_entry__in_tx_snprintf,
	.se_width_idx	= HISTC_IN_TX,
};

913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953
static int64_t
sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
{
	return left->transaction - right->transaction;
}

static inline char *add_str(char *p, const char *str)
{
	strcpy(p, str);
	return p + strlen(str);
}

static struct txbit {
	unsigned flag;
	const char *name;
	int skip_for_len;
} txbits[] = {
	{ PERF_TXN_ELISION,        "EL ",        0 },
	{ PERF_TXN_TRANSACTION,    "TX ",        1 },
	{ PERF_TXN_SYNC,           "SYNC ",      1 },
	{ PERF_TXN_ASYNC,          "ASYNC ",     0 },
	{ PERF_TXN_RETRY,          "RETRY ",     0 },
	{ PERF_TXN_CONFLICT,       "CON ",       0 },
	{ PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 },
	{ PERF_TXN_CAPACITY_READ,  "CAP-READ ",  0 },
	{ 0, NULL, 0 }
};

int hist_entry__transaction_len(void)
{
	int i;
	int len = 0;

	for (i = 0; txbits[i].name; i++) {
		if (!txbits[i].skip_for_len)
			len += strlen(txbits[i].name);
	}
	len += 4; /* :XX<space> */
	return len;
}

954
static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf,
955 956
					    size_t size, unsigned int width)
{
957
	u64 t = he->transaction;
958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984
	char buf[128];
	char *p = buf;
	int i;

	buf[0] = 0;
	for (i = 0; txbits[i].name; i++)
		if (txbits[i].flag & t)
			p = add_str(p, txbits[i].name);
	if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC)))
		p = add_str(p, "NEITHER ");
	if (t & PERF_TXN_ABORT_MASK) {
		sprintf(p, ":%" PRIx64,
			(t & PERF_TXN_ABORT_MASK) >>
			PERF_TXN_ABORT_SHIFT);
		p += strlen(p);
	}

	return repsep_snprintf(bf, size, "%-*s", width, buf);
}

struct sort_entry sort_transaction = {
	.se_header	= "Transaction                ",
	.se_cmp		= sort__transaction_cmp,
	.se_snprintf	= hist_entry__transaction_snprintf,
	.se_width_idx	= HISTC_TRANSACTION,
};

985 986 987 988 989 990
struct sort_dimension {
	const char		*name;
	struct sort_entry	*entry;
	int			taken;
};

991 992
#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }

993
static struct sort_dimension common_sort_dimensions[] = {
994 995 996 997 998 999
	DIM(SORT_PID, "pid", sort_thread),
	DIM(SORT_COMM, "comm", sort_comm),
	DIM(SORT_DSO, "dso", sort_dso),
	DIM(SORT_SYM, "symbol", sort_sym),
	DIM(SORT_PARENT, "parent", sort_parent),
	DIM(SORT_CPU, "cpu", sort_cpu),
1000
	DIM(SORT_SRCLINE, "srcline", sort_srcline),
1001 1002
	DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
	DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
1003
	DIM(SORT_TRANSACTION, "transaction", sort_transaction),
1004 1005
};

1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
#undef DIM

#define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }

static struct sort_dimension bstack_sort_dimensions[] = {
	DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
	DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
	DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
	DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
	DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
1016 1017
	DIM(SORT_IN_TX, "in_tx", sort_in_tx),
	DIM(SORT_ABORT, "abort", sort_abort),
1018 1019 1020 1021
};

#undef DIM

1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034
#define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }

static struct sort_dimension memory_sort_dimensions[] = {
	DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
	DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
	DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
	DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
	DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
	DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
};

#undef DIM

1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
struct hpp_dimension {
	const char		*name;
	struct perf_hpp_fmt	*fmt;
	int			taken;
};

#define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }

static struct hpp_dimension hpp_sort_dimensions[] = {
	DIM(PERF_HPP__OVERHEAD, "overhead"),
	DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"),
	DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
	DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
	DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
	DIM(PERF_HPP__SAMPLES, "sample"),
	DIM(PERF_HPP__PERIOD, "period"),
};

#undef DIM

1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122
struct hpp_sort_entry {
	struct perf_hpp_fmt hpp;
	struct sort_entry *se;
};

static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
			      struct perf_evsel *evsel)
{
	struct hpp_sort_entry *hse;
	size_t len;

	hse = container_of(fmt, struct hpp_sort_entry, hpp);
	len = hists__col_len(&evsel->hists, hse->se->se_width_idx);

	return scnprintf(hpp->buf, hpp->size, "%*s", len, hse->se->se_header);
}

static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
			     struct perf_hpp *hpp __maybe_unused,
			     struct perf_evsel *evsel)
{
	struct hpp_sort_entry *hse;

	hse = container_of(fmt, struct hpp_sort_entry, hpp);

	return hists__col_len(&evsel->hists, hse->se->se_width_idx);
}

static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
			     struct hist_entry *he)
{
	struct hpp_sort_entry *hse;
	size_t len;

	hse = container_of(fmt, struct hpp_sort_entry, hpp);
	len = hists__col_len(he->hists, hse->se->se_width_idx);

	return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
}

static int __sort_dimension__add_hpp(struct sort_dimension *sd)
{
	struct hpp_sort_entry *hse;

	hse = malloc(sizeof(*hse));
	if (hse == NULL) {
		pr_err("Memory allocation failed\n");
		return -1;
	}

	hse->se = sd->entry;
	hse->hpp.header = __sort__hpp_header;
	hse->hpp.width = __sort__hpp_width;
	hse->hpp.entry = __sort__hpp_entry;
	hse->hpp.color = NULL;

	hse->hpp.cmp = sd->entry->se_cmp;
	hse->hpp.collapse = sd->entry->se_collapse ? : sd->entry->se_cmp;
	hse->hpp.sort = hse->hpp.collapse;

	INIT_LIST_HEAD(&hse->hpp.list);
	INIT_LIST_HEAD(&hse->hpp.sort_list);

	perf_hpp__register_sort_field(&hse->hpp);
	return 0;
}

static int __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx)
1123 1124
{
	if (sd->taken)
1125 1126 1127 1128
		return 0;

	if (__sort_dimension__add_hpp(sd) < 0)
		return -1;
1129 1130 1131 1132 1133 1134 1135 1136 1137

	if (sd->entry->se_collapse)
		sort__need_collapse = 1;

	if (list_empty(&hist_entry__sort_list))
		sort__first_dimension = idx;

	list_add_tail(&sd->entry->list, &hist_entry__sort_list);
	sd->taken = 1;
1138 1139

	return 0;
1140 1141
}

1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
static int __hpp_dimension__add(struct hpp_dimension *hd)
{
	if (!hd->taken) {
		hd->taken = 1;

		perf_hpp__register_sort_field(hd->fmt);
	}
	return 0;
}

1152 1153 1154 1155
int sort_dimension__add(const char *tok)
{
	unsigned int i;

1156 1157
	for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
		struct sort_dimension *sd = &common_sort_dimensions[i];
1158 1159 1160

		if (strncasecmp(tok, sd->name, strlen(tok)))
			continue;
1161

1162 1163 1164 1165 1166 1167
		if (sd->entry == &sort_parent) {
			int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
			if (ret) {
				char err[BUFSIZ];

				regerror(ret, &parent_regex, err, sizeof(err));
1168 1169
				pr_err("Invalid regex: %s\n%s", parent_pattern, err);
				return -EINVAL;
1170 1171
			}
			sort__has_parent = 1;
1172
		} else if (sd->entry == &sort_sym) {
N
Namhyung Kim 已提交
1173
			sort__has_sym = 1;
N
Namhyung Kim 已提交
1174 1175
		} else if (sd->entry == &sort_dso) {
			sort__has_dso = 1;
1176 1177
		}

1178
		return __sort_dimension__add(sd, i);
1179
	}
1180

1181 1182 1183 1184 1185 1186 1187 1188 1189
	for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
		struct hpp_dimension *hd = &hpp_sort_dimensions[i];

		if (strncasecmp(tok, hd->name, strlen(tok)))
			continue;

		return __hpp_dimension__add(hd);
	}

1190 1191 1192 1193 1194 1195
	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
		struct sort_dimension *sd = &bstack_sort_dimensions[i];

		if (strncasecmp(tok, sd->name, strlen(tok)))
			continue;

1196
		if (sort__mode != SORT_MODE__BRANCH)
1197 1198 1199 1200 1201
			return -EINVAL;

		if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
			sort__has_sym = 1;

1202
		__sort_dimension__add(sd, i + __SORT_BRANCH_STACK);
1203 1204 1205
		return 0;
	}

1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221
	for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
		struct sort_dimension *sd = &memory_sort_dimensions[i];

		if (strncasecmp(tok, sd->name, strlen(tok)))
			continue;

		if (sort__mode != SORT_MODE__MEMORY)
			return -EINVAL;

		if (sd->entry == &sort_mem_daddr_sym)
			sort__has_sym = 1;

		__sort_dimension__add(sd, i + __SORT_MEMORY_MODE);
		return 0;
	}

1222 1223
	return -ESRCH;
}
1224

1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239
static const char *get_default_sort_order(void)
{
	const char *default_sort_orders[] = {
		default_sort_order,
		default_branch_sort_order,
		default_mem_sort_order,
		default_top_sort_order,
		default_diff_sort_order,
	};

	BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));

	return default_sort_orders[sort__mode];
}

1240
int setup_sorting(void)
1241
{
1242 1243
	char *tmp, *tok, *str;
	const char *sort_keys = sort_order;
1244
	int ret = 0;
1245

1246 1247 1248 1249
	if (sort_keys == NULL)
		sort_keys = get_default_sort_order();

	str = strdup(sort_keys);
1250 1251 1252 1253 1254
	if (str == NULL) {
		error("Not enough memory to setup sort keys");
		return -ENOMEM;
	}

1255 1256
	for (tok = strtok_r(str, ", ", &tmp);
			tok; tok = strtok_r(NULL, ", ", &tmp)) {
1257
		ret = sort_dimension__add(tok);
1258 1259
		if (ret == -EINVAL) {
			error("Invalid --sort key: `%s'", tok);
1260
			break;
1261
		} else if (ret == -ESRCH) {
1262
			error("Unknown --sort key: `%s'", tok);
1263
			break;
1264 1265 1266 1267
		}
	}

	free(str);
1268
	return ret;
1269
}
1270

1271
static void sort_entry__setup_elide(struct sort_entry *se,
1272 1273
				    struct strlist *list,
				    const char *list_name, FILE *fp)
1274 1275 1276 1277 1278
{
	if (list && strlist__nr_entries(list) == 1) {
		if (fp != NULL)
			fprintf(fp, "# %s: %s\n", list_name,
				strlist__entry(list, 0)->s);
1279
		se->elide = true;
1280 1281
	}
}
1282 1283 1284

void sort__setup_elide(FILE *output)
{
1285 1286
	struct sort_entry *se;

1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321
	sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
				"dso", output);
	sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list,
				"comm", output);
	sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list,
				"symbol", output);

	if (sort__mode == SORT_MODE__BRANCH) {
		sort_entry__setup_elide(&sort_dso_from,
					symbol_conf.dso_from_list,
					"dso_from", output);
		sort_entry__setup_elide(&sort_dso_to,
					symbol_conf.dso_to_list,
					"dso_to", output);
		sort_entry__setup_elide(&sort_sym_from,
					symbol_conf.sym_from_list,
					"sym_from", output);
		sort_entry__setup_elide(&sort_sym_to,
					symbol_conf.sym_to_list,
					"sym_to", output);
	} else if (sort__mode == SORT_MODE__MEMORY) {
		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
					"symbol_daddr", output);
		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
					"dso_daddr", output);
		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
					"mem", output);
		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
					"local_weight", output);
		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
					"tlb", output);
		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
					"snoop", output);
	}

1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332
	/*
	 * It makes no sense to elide all of sort entries.
	 * Just revert them to show up again.
	 */
	list_for_each_entry(se, &hist_entry__sort_list, list) {
		if (!se->elide)
			return;
	}

	list_for_each_entry(se, &hist_entry__sort_list, list)
		se->elide = false;
1333
}