Home | History | Annotate | Download | only in util
      1 #include "sort.h"
      2 #include "hist.h"
      3 #include "symbol.h"
      4 
      5 regex_t		parent_regex;
      6 const char	default_parent_pattern[] = "^sys_|^do_page_fault";
      7 const char	*parent_pattern = default_parent_pattern;
      8 const char	default_sort_order[] = "comm,dso,symbol";
      9 const char	*sort_order = default_sort_order;
     10 regex_t		ignore_callees_regex;
     11 int		have_ignore_callees = 0;
     12 int		sort__need_collapse = 0;
     13 int		sort__has_parent = 0;
     14 int		sort__has_sym = 0;
     15 enum sort_mode	sort__mode = SORT_MODE__NORMAL;
     16 
     17 enum sort_type	sort__first_dimension;
     18 
     19 LIST_HEAD(hist_entry__sort_list);
     20 
     21 static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
     22 {
     23 	int n;
     24 	va_list ap;
     25 
     26 	va_start(ap, fmt);
     27 	n = vsnprintf(bf, size, fmt, ap);
     28 	if (symbol_conf.field_sep && n > 0) {
     29 		char *sep = bf;
     30 
     31 		while (1) {
     32 			sep = strchr(sep, *symbol_conf.field_sep);
     33 			if (sep == NULL)
     34 				break;
     35 			*sep = '.';
     36 		}
     37 	}
     38 	va_end(ap);
     39 
     40 	if (n >= (int)size)
     41 		return size - 1;
     42 	return n;
     43 }
     44 
     45 static int64_t cmp_null(void *l, void *r)
     46 {
     47 	if (!l && !r)
     48 		return 0;
     49 	else if (!l)
     50 		return -1;
     51 	else
     52 		return 1;
     53 }
     54 
     55 /* --sort pid */
     56 
     57 static int64_t
     58 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
     59 {
     60 	return right->thread->tid - left->thread->tid;
     61 }
     62 
     63 static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
     64 				       size_t size, unsigned int width)
     65 {
     66 	return repsep_snprintf(bf, size, "%*s:%5d", width - 6,
     67 			      self->thread->comm ?: "", self->thread->tid);
     68 }
     69 
     70 struct sort_entry sort_thread = {
     71 	.se_header	= "Command:  Pid",
     72 	.se_cmp		= sort__thread_cmp,
     73 	.se_snprintf	= hist_entry__thread_snprintf,
     74 	.se_width_idx	= HISTC_THREAD,
     75 };
     76 
     77 /* --sort comm */
     78 
     79 static int64_t
     80 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
     81 {
     82 	return right->thread->tid - left->thread->tid;
     83 }
     84 
     85 static int64_t
     86 sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
     87 {
     88 	char *comm_l = left->thread->comm;
     89 	char *comm_r = right->thread->comm;
     90 
     91 	if (!comm_l || !comm_r)
     92 		return cmp_null(comm_l, comm_r);
     93 
     94 	return strcmp(comm_l, comm_r);
     95 }
     96 
     97 static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
     98 				     size_t size, unsigned int width)
     99 {
    100 	return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
    101 }
    102 
    103 struct sort_entry sort_comm = {
    104 	.se_header	= "Command",
    105 	.se_cmp		= sort__comm_cmp,
    106 	.se_collapse	= sort__comm_collapse,
    107 	.se_snprintf	= hist_entry__comm_snprintf,
    108 	.se_width_idx	= HISTC_COMM,
    109 };
    110 
    111 /* --sort dso */
    112 
    113 static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
    114 {
    115 	struct dso *dso_l = map_l ? map_l->dso : NULL;
    116 	struct dso *dso_r = map_r ? map_r->dso : NULL;
    117 	const char *dso_name_l, *dso_name_r;
    118 
    119 	if (!dso_l || !dso_r)
    120 		return cmp_null(dso_l, dso_r);
    121 
    122 	if (verbose) {
    123 		dso_name_l = dso_l->long_name;
    124 		dso_name_r = dso_r->long_name;
    125 	} else {
    126 		dso_name_l = dso_l->short_name;
    127 		dso_name_r = dso_r->short_name;
    128 	}
    129 
    130 	return strcmp(dso_name_l, dso_name_r);
    131 }
    132 
    133 static int64_t
    134 sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
    135 {
    136 	return _sort__dso_cmp(left->ms.map, right->ms.map);
    137 }
    138 
    139 static int _hist_entry__dso_snprintf(struct map *map, char *bf,
    140 				     size_t size, unsigned int width)
    141 {
    142 	if (map && map->dso) {
    143 		const char *dso_name = !verbose ? map->dso->short_name :
    144 			map->dso->long_name;
    145 		return repsep_snprintf(bf, size, "%-*s", width, dso_name);
    146 	}
    147 
    148 	return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
    149 }
    150 
    151 static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
    152 				    size_t size, unsigned int width)
    153 {
    154 	return _hist_entry__dso_snprintf(self->ms.map, bf, size, width);
    155 }
    156 
    157 struct sort_entry sort_dso = {
    158 	.se_header	= "Shared Object",
    159 	.se_cmp		= sort__dso_cmp,
    160 	.se_snprintf	= hist_entry__dso_snprintf,
    161 	.se_width_idx	= HISTC_DSO,
    162 };
    163 
    164 /* --sort symbol */
    165 
    166 static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
    167 {
    168 	u64 ip_l, ip_r;
    169 
    170 	if (!sym_l || !sym_r)
    171 		return cmp_null(sym_l, sym_r);
    172 
    173 	if (sym_l == sym_r)
    174 		return 0;
    175 
    176 	ip_l = sym_l->start;
    177 	ip_r = sym_r->start;
    178 
    179 	return (int64_t)(ip_r - ip_l);
    180 }
    181 
    182 static int64_t
    183 sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
    184 {
    185 	if (!left->ms.sym && !right->ms.sym)
    186 		return right->level - left->level;
    187 
    188 	return _sort__sym_cmp(left->ms.sym, right->ms.sym);
    189 }
    190 
    191 static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
    192 				     u64 ip, char level, char *bf, size_t size,
    193 				     unsigned int width)
    194 {
    195 	size_t ret = 0;
    196 
    197 	if (verbose) {
    198 		char o = map ? dso__symtab_origin(map->dso) : '!';
    199 		ret += repsep_snprintf(bf, size, "%-#*llx %c ",
    200 				       BITS_PER_LONG / 4 + 2, ip, o);
    201 	}
    202 
    203 	ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
    204 	if (sym && map) {
    205 		if (map->type == MAP__VARIABLE) {
    206 			ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
    207 			ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
    208 					ip - map->unmap_ip(map, sym->start));
    209 			ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
    210 				       width - ret, "");
    211 		} else {
    212 			ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
    213 					       width - ret,
    214 					       sym->name);
    215 		}
    216 	} else {
    217 		size_t len = BITS_PER_LONG / 4;
    218 		ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
    219 				       len, ip);
    220 		ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
    221 				       width - ret, "");
    222 	}
    223 
    224 	return ret;
    225 }
    226 
    227 static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
    228 				    size_t size, unsigned int width)
    229 {
    230 	return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip,
    231 					 self->level, bf, size, width);
    232 }
    233 
    234 struct sort_entry sort_sym = {
    235 	.se_header	= "Symbol",
    236 	.se_cmp		= sort__sym_cmp,
    237 	.se_snprintf	= hist_entry__sym_snprintf,
    238 	.se_width_idx	= HISTC_SYMBOL,
    239 };
    240 
    241 /* --sort srcline */
    242 
    243 static int64_t
    244 sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
    245 {
    246 	return (int64_t)(right->ip - left->ip);
    247 }
    248 
    249 static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf,
    250 					size_t size,
    251 					unsigned int width __maybe_unused)
    252 {
    253 	FILE *fp = NULL;
    254 	char cmd[PATH_MAX + 2], *path = self->srcline, *nl;
    255 	size_t line_len;
    256 
    257 	if (path != NULL)
    258 		goto out_path;
    259 
    260 	if (!self->ms.map)
    261 		goto out_ip;
    262 
    263 	if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10))
    264 		goto out_ip;
    265 
    266 	snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64,
    267 		 self->ms.map->dso->long_name, self->ip);
    268 	fp = popen(cmd, "r");
    269 	if (!fp)
    270 		goto out_ip;
    271 
    272 	if (getline(&path, &line_len, fp) < 0 || !line_len)
    273 		goto out_ip;
    274 	self->srcline = strdup(path);
    275 	if (self->srcline == NULL)
    276 		goto out_ip;
    277 
    278 	nl = strchr(self->srcline, '\n');
    279 	if (nl != NULL)
    280 		*nl = '\0';
    281 	path = self->srcline;
    282 out_path:
    283 	if (fp)
    284 		pclose(fp);
    285 	return repsep_snprintf(bf, size, "%s", path);
    286 out_ip:
    287 	if (fp)
    288 		pclose(fp);
    289 	return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip);
    290 }
    291 
    292 struct sort_entry sort_srcline = {
    293 	.se_header	= "Source:Line",
    294 	.se_cmp		= sort__srcline_cmp,
    295 	.se_snprintf	= hist_entry__srcline_snprintf,
    296 	.se_width_idx	= HISTC_SRCLINE,
    297 };
    298 
    299 /* --sort parent */
    300 
    301 static int64_t
    302 sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
    303 {
    304 	struct symbol *sym_l = left->parent;
    305 	struct symbol *sym_r = right->parent;
    306 
    307 	if (!sym_l || !sym_r)
    308 		return cmp_null(sym_l, sym_r);
    309 
    310 	return strcmp(sym_l->name, sym_r->name);
    311 }
    312 
    313 static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
    314 				       size_t size, unsigned int width)
    315 {
    316 	return repsep_snprintf(bf, size, "%-*s", width,
    317 			      self->parent ? self->parent->name : "[other]");
    318 }
    319 
    320 struct sort_entry sort_parent = {
    321 	.se_header	= "Parent symbol",
    322 	.se_cmp		= sort__parent_cmp,
    323 	.se_snprintf	= hist_entry__parent_snprintf,
    324 	.se_width_idx	= HISTC_PARENT,
    325 };
    326 
    327 /* --sort cpu */
    328 
    329 static int64_t
    330 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
    331 {
    332 	return right->cpu - left->cpu;
    333 }
    334 
    335 static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
    336 				       size_t size, unsigned int width)
    337 {
    338 	return repsep_snprintf(bf, size, "%*d", width, self->cpu);
    339 }
    340 
    341 struct sort_entry sort_cpu = {
    342 	.se_header      = "CPU",
    343 	.se_cmp	        = sort__cpu_cmp,
    344 	.se_snprintf    = hist_entry__cpu_snprintf,
    345 	.se_width_idx	= HISTC_CPU,
    346 };
    347 
    348 /* sort keys for branch stacks */
    349 
    350 static int64_t
    351 sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
    352 {
    353 	return _sort__dso_cmp(left->branch_info->from.map,
    354 			      right->branch_info->from.map);
    355 }
    356 
    357 static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf,
    358 				    size_t size, unsigned int width)
    359 {
    360 	return _hist_entry__dso_snprintf(self->branch_info->from.map,
    361 					 bf, size, width);
    362 }
    363 
    364 static int64_t
    365 sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
    366 {
    367 	return _sort__dso_cmp(left->branch_info->to.map,
    368 			      right->branch_info->to.map);
    369 }
    370 
    371 static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf,
    372 				       size_t size, unsigned int width)
    373 {
    374 	return _hist_entry__dso_snprintf(self->branch_info->to.map,
    375 					 bf, size, width);
    376 }
    377 
    378 static int64_t
    379 sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
    380 {
    381 	struct addr_map_symbol *from_l = &left->branch_info->from;
    382 	struct addr_map_symbol *from_r = &right->branch_info->from;
    383 
    384 	if (!from_l->sym && !from_r->sym)
    385 		return right->level - left->level;
    386 
    387 	return _sort__sym_cmp(from_l->sym, from_r->sym);
    388 }
    389 
    390 static int64_t
    391 sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
    392 {
    393 	struct addr_map_symbol *to_l = &left->branch_info->to;
    394 	struct addr_map_symbol *to_r = &right->branch_info->to;
    395 
    396 	if (!to_l->sym && !to_r->sym)
    397 		return right->level - left->level;
    398 
    399 	return _sort__sym_cmp(to_l->sym, to_r->sym);
    400 }
    401 
    402 static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
    403 					 size_t size, unsigned int width)
    404 {
    405 	struct addr_map_symbol *from = &self->branch_info->from;
    406 	return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
    407 					 self->level, bf, size, width);
    408 
    409 }
    410 
    411 static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
    412 				       size_t size, unsigned int width)
    413 {
    414 	struct addr_map_symbol *to = &self->branch_info->to;
    415 	return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
    416 					 self->level, bf, size, width);
    417 
    418 }
    419 
    420 struct sort_entry sort_dso_from = {
    421 	.se_header	= "Source Shared Object",
    422 	.se_cmp		= sort__dso_from_cmp,
    423 	.se_snprintf	= hist_entry__dso_from_snprintf,
    424 	.se_width_idx	= HISTC_DSO_FROM,
    425 };
    426 
    427 struct sort_entry sort_dso_to = {
    428 	.se_header	= "Target Shared Object",
    429 	.se_cmp		= sort__dso_to_cmp,
    430 	.se_snprintf	= hist_entry__dso_to_snprintf,
    431 	.se_width_idx	= HISTC_DSO_TO,
    432 };
    433 
    434 struct sort_entry sort_sym_from = {
    435 	.se_header	= "Source Symbol",
    436 	.se_cmp		= sort__sym_from_cmp,
    437 	.se_snprintf	= hist_entry__sym_from_snprintf,
    438 	.se_width_idx	= HISTC_SYMBOL_FROM,
    439 };
    440 
    441 struct sort_entry sort_sym_to = {
    442 	.se_header	= "Target Symbol",
    443 	.se_cmp		= sort__sym_to_cmp,
    444 	.se_snprintf	= hist_entry__sym_to_snprintf,
    445 	.se_width_idx	= HISTC_SYMBOL_TO,
    446 };
    447 
    448 static int64_t
    449 sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
    450 {
    451 	const unsigned char mp = left->branch_info->flags.mispred !=
    452 					right->branch_info->flags.mispred;
    453 	const unsigned char p = left->branch_info->flags.predicted !=
    454 					right->branch_info->flags.predicted;
    455 
    456 	return mp || p;
    457 }
    458 
    459 static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf,
    460 				    size_t size, unsigned int width){
    461 	static const char *out = "N/A";
    462 
    463 	if (self->branch_info->flags.predicted)
    464 		out = "N";
    465 	else if (self->branch_info->flags.mispred)
    466 		out = "Y";
    467 
    468 	return repsep_snprintf(bf, size, "%-*s", width, out);
    469 }
    470 
    471 /* --sort daddr_sym */
    472 static int64_t
    473 sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
    474 {
    475 	uint64_t l = 0, r = 0;
    476 
    477 	if (left->mem_info)
    478 		l = left->mem_info->daddr.addr;
    479 	if (right->mem_info)
    480 		r = right->mem_info->daddr.addr;
    481 
    482 	return (int64_t)(r - l);
    483 }
    484 
    485 static int hist_entry__daddr_snprintf(struct hist_entry *self, char *bf,
    486 				    size_t size, unsigned int width)
    487 {
    488 	uint64_t addr = 0;
    489 	struct map *map = NULL;
    490 	struct symbol *sym = NULL;
    491 
    492 	if (self->mem_info) {
    493 		addr = self->mem_info->daddr.addr;
    494 		map = self->mem_info->daddr.map;
    495 		sym = self->mem_info->daddr.sym;
    496 	}
    497 	return _hist_entry__sym_snprintf(map, sym, addr, self->level, bf, size,
    498 					 width);
    499 }
    500 
    501 static int64_t
    502 sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
    503 {
    504 	struct map *map_l = NULL;
    505 	struct map *map_r = NULL;
    506 
    507 	if (left->mem_info)
    508 		map_l = left->mem_info->daddr.map;
    509 	if (right->mem_info)
    510 		map_r = right->mem_info->daddr.map;
    511 
    512 	return _sort__dso_cmp(map_l, map_r);
    513 }
    514 
    515 static int hist_entry__dso_daddr_snprintf(struct hist_entry *self, char *bf,
    516 				    size_t size, unsigned int width)
    517 {
    518 	struct map *map = NULL;
    519 
    520 	if (self->mem_info)
    521 		map = self->mem_info->daddr.map;
    522 
    523 	return _hist_entry__dso_snprintf(map, bf, size, width);
    524 }
    525 
    526 static int64_t
    527 sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
    528 {
    529 	union perf_mem_data_src data_src_l;
    530 	union perf_mem_data_src data_src_r;
    531 
    532 	if (left->mem_info)
    533 		data_src_l = left->mem_info->data_src;
    534 	else
    535 		data_src_l.mem_lock = PERF_MEM_LOCK_NA;
    536 
    537 	if (right->mem_info)
    538 		data_src_r = right->mem_info->data_src;
    539 	else
    540 		data_src_r.mem_lock = PERF_MEM_LOCK_NA;
    541 
    542 	return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
    543 }
    544 
    545 static int hist_entry__locked_snprintf(struct hist_entry *self, char *bf,
    546 				    size_t size, unsigned int width)
    547 {
    548 	const char *out;
    549 	u64 mask = PERF_MEM_LOCK_NA;
    550 
    551 	if (self->mem_info)
    552 		mask = self->mem_info->data_src.mem_lock;
    553 
    554 	if (mask & PERF_MEM_LOCK_NA)
    555 		out = "N/A";
    556 	else if (mask & PERF_MEM_LOCK_LOCKED)
    557 		out = "Yes";
    558 	else
    559 		out = "No";
    560 
    561 	return repsep_snprintf(bf, size, "%-*s", width, out);
    562 }
    563 
    564 static int64_t
    565 sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
    566 {
    567 	union perf_mem_data_src data_src_l;
    568 	union perf_mem_data_src data_src_r;
    569 
    570 	if (left->mem_info)
    571 		data_src_l = left->mem_info->data_src;
    572 	else
    573 		data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
    574 
    575 	if (right->mem_info)
    576 		data_src_r = right->mem_info->data_src;
    577 	else
    578 		data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
    579 
    580 	return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
    581 }
    582 
    583 static const char * const tlb_access[] = {
    584 	"N/A",
    585 	"HIT",
    586 	"MISS",
    587 	"L1",
    588 	"L2",
    589 	"Walker",
    590 	"Fault",
    591 };
    592 #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
    593 
    594 static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf,
    595 				    size_t size, unsigned int width)
    596 {
    597 	char out[64];
    598 	size_t sz = sizeof(out) - 1; /* -1 for null termination */
    599 	size_t l = 0, i;
    600 	u64 m = PERF_MEM_TLB_NA;
    601 	u64 hit, miss;
    602 
    603 	out[0] = '\0';
    604 
    605 	if (self->mem_info)
    606 		m = self->mem_info->data_src.mem_dtlb;
    607 
    608 	hit = m & PERF_MEM_TLB_HIT;
    609 	miss = m & PERF_MEM_TLB_MISS;
    610 
    611 	/* already taken care of */
    612 	m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
    613 
    614 	for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) {
    615 		if (!(m & 0x1))
    616 			continue;
    617 		if (l) {
    618 			strcat(out, " or ");
    619 			l += 4;
    620 		}
    621 		strncat(out, tlb_access[i], sz - l);
    622 		l += strlen(tlb_access[i]);
    623 	}
    624 	if (*out == '\0')
    625 		strcpy(out, "N/A");
    626 	if (hit)
    627 		strncat(out, " hit", sz - l);
    628 	if (miss)
    629 		strncat(out, " miss", sz - l);
    630 
    631 	return repsep_snprintf(bf, size, "%-*s", width, out);
    632 }
    633 
    634 static int64_t
    635 sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
    636 {
    637 	union perf_mem_data_src data_src_l;
    638 	union perf_mem_data_src data_src_r;
    639 
    640 	if (left->mem_info)
    641 		data_src_l = left->mem_info->data_src;
    642 	else
    643 		data_src_l.mem_lvl = PERF_MEM_LVL_NA;
    644 
    645 	if (right->mem_info)
    646 		data_src_r = right->mem_info->data_src;
    647 	else
    648 		data_src_r.mem_lvl = PERF_MEM_LVL_NA;
    649 
    650 	return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
    651 }
    652 
    653 static const char * const mem_lvl[] = {
    654 	"N/A",
    655 	"HIT",
    656 	"MISS",
    657 	"L1",
    658 	"LFB",
    659 	"L2",
    660 	"L3",
    661 	"Local RAM",
    662 	"Remote RAM (1 hop)",
    663 	"Remote RAM (2 hops)",
    664 	"Remote Cache (1 hop)",
    665 	"Remote Cache (2 hops)",
    666 	"I/O",
    667 	"Uncached",
    668 };
    669 #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
    670 
    671 static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf,
    672 				    size_t size, unsigned int width)
    673 {
    674 	char out[64];
    675 	size_t sz = sizeof(out) - 1; /* -1 for null termination */
    676 	size_t i, l = 0;
    677 	u64 m =  PERF_MEM_LVL_NA;
    678 	u64 hit, miss;
    679 
    680 	if (self->mem_info)
    681 		m  = self->mem_info->data_src.mem_lvl;
    682 
    683 	out[0] = '\0';
    684 
    685 	hit = m & PERF_MEM_LVL_HIT;
    686 	miss = m & PERF_MEM_LVL_MISS;
    687 
    688 	/* already taken care of */
    689 	m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
    690 
    691 	for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) {
    692 		if (!(m & 0x1))
    693 			continue;
    694 		if (l) {
    695 			strcat(out, " or ");
    696 			l += 4;
    697 		}
    698 		strncat(out, mem_lvl[i], sz - l);
    699 		l += strlen(mem_lvl[i]);
    700 	}
    701 	if (*out == '\0')
    702 		strcpy(out, "N/A");
    703 	if (hit)
    704 		strncat(out, " hit", sz - l);
    705 	if (miss)
    706 		strncat(out, " miss", sz - l);
    707 
    708 	return repsep_snprintf(bf, size, "%-*s", width, out);
    709 }
    710 
    711 static int64_t
    712 sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
    713 {
    714 	union perf_mem_data_src data_src_l;
    715 	union perf_mem_data_src data_src_r;
    716 
    717 	if (left->mem_info)
    718 		data_src_l = left->mem_info->data_src;
    719 	else
    720 		data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
    721 
    722 	if (right->mem_info)
    723 		data_src_r = right->mem_info->data_src;
    724 	else
    725 		data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
    726 
    727 	return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
    728 }
    729 
    730 static const char * const snoop_access[] = {
    731 	"N/A",
    732 	"None",
    733 	"Miss",
    734 	"Hit",
    735 	"HitM",
    736 };
    737 #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
    738 
    739 static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf,
    740 				    size_t size, unsigned int width)
    741 {
    742 	char out[64];
    743 	size_t sz = sizeof(out) - 1; /* -1 for null termination */
    744 	size_t i, l = 0;
    745 	u64 m = PERF_MEM_SNOOP_NA;
    746 
    747 	out[0] = '\0';
    748 
    749 	if (self->mem_info)
    750 		m = self->mem_info->data_src.mem_snoop;
    751 
    752 	for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {
    753 		if (!(m & 0x1))
    754 			continue;
    755 		if (l) {
    756 			strcat(out, " or ");
    757 			l += 4;
    758 		}
    759 		strncat(out, snoop_access[i], sz - l);
    760 		l += strlen(snoop_access[i]);
    761 	}
    762 
    763 	if (*out == '\0')
    764 		strcpy(out, "N/A");
    765 
    766 	return repsep_snprintf(bf, size, "%-*s", width, out);
    767 }
    768 
    769 struct sort_entry sort_mispredict = {
    770 	.se_header	= "Branch Mispredicted",
    771 	.se_cmp		= sort__mispredict_cmp,
    772 	.se_snprintf	= hist_entry__mispredict_snprintf,
    773 	.se_width_idx	= HISTC_MISPREDICT,
    774 };
    775 
    776 static u64 he_weight(struct hist_entry *he)
    777 {
    778 	return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
    779 }
    780 
    781 static int64_t
    782 sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
    783 {
    784 	return he_weight(left) - he_weight(right);
    785 }
    786 
    787 static int hist_entry__local_weight_snprintf(struct hist_entry *self, char *bf,
    788 				    size_t size, unsigned int width)
    789 {
    790 	return repsep_snprintf(bf, size, "%-*llu", width, he_weight(self));
    791 }
    792 
    793 struct sort_entry sort_local_weight = {
    794 	.se_header	= "Local Weight",
    795 	.se_cmp		= sort__local_weight_cmp,
    796 	.se_snprintf	= hist_entry__local_weight_snprintf,
    797 	.se_width_idx	= HISTC_LOCAL_WEIGHT,
    798 };
    799 
    800 static int64_t
    801 sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
    802 {
    803 	return left->stat.weight - right->stat.weight;
    804 }
    805 
    806 static int hist_entry__global_weight_snprintf(struct hist_entry *self, char *bf,
    807 					      size_t size, unsigned int width)
    808 {
    809 	return repsep_snprintf(bf, size, "%-*llu", width, self->stat.weight);
    810 }
    811 
    812 struct sort_entry sort_global_weight = {
    813 	.se_header	= "Weight",
    814 	.se_cmp		= sort__global_weight_cmp,
    815 	.se_snprintf	= hist_entry__global_weight_snprintf,
    816 	.se_width_idx	= HISTC_GLOBAL_WEIGHT,
    817 };
    818 
    819 struct sort_entry sort_mem_daddr_sym = {
    820 	.se_header	= "Data Symbol",
    821 	.se_cmp		= sort__daddr_cmp,
    822 	.se_snprintf	= hist_entry__daddr_snprintf,
    823 	.se_width_idx	= HISTC_MEM_DADDR_SYMBOL,
    824 };
    825 
    826 struct sort_entry sort_mem_daddr_dso = {
    827 	.se_header	= "Data Object",
    828 	.se_cmp		= sort__dso_daddr_cmp,
    829 	.se_snprintf	= hist_entry__dso_daddr_snprintf,
    830 	.se_width_idx	= HISTC_MEM_DADDR_SYMBOL,
    831 };
    832 
    833 struct sort_entry sort_mem_locked = {
    834 	.se_header	= "Locked",
    835 	.se_cmp		= sort__locked_cmp,
    836 	.se_snprintf	= hist_entry__locked_snprintf,
    837 	.se_width_idx	= HISTC_MEM_LOCKED,
    838 };
    839 
    840 struct sort_entry sort_mem_tlb = {
    841 	.se_header	= "TLB access",
    842 	.se_cmp		= sort__tlb_cmp,
    843 	.se_snprintf	= hist_entry__tlb_snprintf,
    844 	.se_width_idx	= HISTC_MEM_TLB,
    845 };
    846 
    847 struct sort_entry sort_mem_lvl = {
    848 	.se_header	= "Memory access",
    849 	.se_cmp		= sort__lvl_cmp,
    850 	.se_snprintf	= hist_entry__lvl_snprintf,
    851 	.se_width_idx	= HISTC_MEM_LVL,
    852 };
    853 
    854 struct sort_entry sort_mem_snoop = {
    855 	.se_header	= "Snoop",
    856 	.se_cmp		= sort__snoop_cmp,
    857 	.se_snprintf	= hist_entry__snoop_snprintf,
    858 	.se_width_idx	= HISTC_MEM_SNOOP,
    859 };
    860 
    861 struct sort_dimension {
    862 	const char		*name;
    863 	struct sort_entry	*entry;
    864 	int			taken;
    865 };
    866 
    867 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
    868 
    869 static struct sort_dimension common_sort_dimensions[] = {
    870 	DIM(SORT_PID, "pid", sort_thread),
    871 	DIM(SORT_COMM, "comm", sort_comm),
    872 	DIM(SORT_DSO, "dso", sort_dso),
    873 	DIM(SORT_SYM, "symbol", sort_sym),
    874 	DIM(SORT_PARENT, "parent", sort_parent),
    875 	DIM(SORT_CPU, "cpu", sort_cpu),
    876 	DIM(SORT_SRCLINE, "srcline", sort_srcline),
    877 	DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
    878 	DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
    879 };
    880 
    881 #undef DIM
    882 
    883 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
    884 
    885 static struct sort_dimension bstack_sort_dimensions[] = {
    886 	DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
    887 	DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
    888 	DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
    889 	DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
    890 	DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
    891 };
    892 
    893 #undef DIM
    894 
    895 #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
    896 
    897 static struct sort_dimension memory_sort_dimensions[] = {
    898 	DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
    899 	DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
    900 	DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
    901 	DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
    902 	DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
    903 	DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
    904 };
    905 
    906 #undef DIM
    907 
    908 static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx)
    909 {
    910 	if (sd->taken)
    911 		return;
    912 
    913 	if (sd->entry->se_collapse)
    914 		sort__need_collapse = 1;
    915 
    916 	if (list_empty(&hist_entry__sort_list))
    917 		sort__first_dimension = idx;
    918 
    919 	list_add_tail(&sd->entry->list, &hist_entry__sort_list);
    920 	sd->taken = 1;
    921 }
    922 
    923 int sort_dimension__add(const char *tok)
    924 {
    925 	unsigned int i;
    926 
    927 	for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
    928 		struct sort_dimension *sd = &common_sort_dimensions[i];
    929 
    930 		if (strncasecmp(tok, sd->name, strlen(tok)))
    931 			continue;
    932 
    933 		if (sd->entry == &sort_parent) {
    934 			int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
    935 			if (ret) {
    936 				char err[BUFSIZ];
    937 
    938 				regerror(ret, &parent_regex, err, sizeof(err));
    939 				pr_err("Invalid regex: %s\n%s", parent_pattern, err);
    940 				return -EINVAL;
    941 			}
    942 			sort__has_parent = 1;
    943 		} else if (sd->entry == &sort_sym) {
    944 			sort__has_sym = 1;
    945 		}
    946 
    947 		__sort_dimension__add(sd, i);
    948 		return 0;
    949 	}
    950 
    951 	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
    952 		struct sort_dimension *sd = &bstack_sort_dimensions[i];
    953 
    954 		if (strncasecmp(tok, sd->name, strlen(tok)))
    955 			continue;
    956 
    957 		if (sort__mode != SORT_MODE__BRANCH)
    958 			return -EINVAL;
    959 
    960 		if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
    961 			sort__has_sym = 1;
    962 
    963 		__sort_dimension__add(sd, i + __SORT_BRANCH_STACK);
    964 		return 0;
    965 	}
    966 
    967 	for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
    968 		struct sort_dimension *sd = &memory_sort_dimensions[i];
    969 
    970 		if (strncasecmp(tok, sd->name, strlen(tok)))
    971 			continue;
    972 
    973 		if (sort__mode != SORT_MODE__MEMORY)
    974 			return -EINVAL;
    975 
    976 		if (sd->entry == &sort_mem_daddr_sym)
    977 			sort__has_sym = 1;
    978 
    979 		__sort_dimension__add(sd, i + __SORT_MEMORY_MODE);
    980 		return 0;
    981 	}
    982 
    983 	return -ESRCH;
    984 }
    985 
    986 int setup_sorting(void)
    987 {
    988 	char *tmp, *tok, *str = strdup(sort_order);
    989 	int ret = 0;
    990 
    991 	if (str == NULL) {
    992 		error("Not enough memory to setup sort keys");
    993 		return -ENOMEM;
    994 	}
    995 
    996 	for (tok = strtok_r(str, ", ", &tmp);
    997 			tok; tok = strtok_r(NULL, ", ", &tmp)) {
    998 		ret = sort_dimension__add(tok);
    999 		if (ret == -EINVAL) {
   1000 			error("Invalid --sort key: `%s'", tok);
   1001 			break;
   1002 		} else if (ret == -ESRCH) {
   1003 			error("Unknown --sort key: `%s'", tok);
   1004 			break;
   1005 		}
   1006 	}
   1007 
   1008 	free(str);
   1009 	return ret;
   1010 }
   1011 
   1012 static void sort_entry__setup_elide(struct sort_entry *self,
   1013 				    struct strlist *list,
   1014 				    const char *list_name, FILE *fp)
   1015 {
   1016 	if (list && strlist__nr_entries(list) == 1) {
   1017 		if (fp != NULL)
   1018 			fprintf(fp, "# %s: %s\n", list_name,
   1019 				strlist__entry(list, 0)->s);
   1020 		self->elide = true;
   1021 	}
   1022 }
   1023 
   1024 void sort__setup_elide(FILE *output)
   1025 {
   1026 	sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
   1027 				"dso", output);
   1028 	sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list,
   1029 				"comm", output);
   1030 	sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list,
   1031 				"symbol", output);
   1032 
   1033 	if (sort__mode == SORT_MODE__BRANCH) {
   1034 		sort_entry__setup_elide(&sort_dso_from,
   1035 					symbol_conf.dso_from_list,
   1036 					"dso_from", output);
   1037 		sort_entry__setup_elide(&sort_dso_to,
   1038 					symbol_conf.dso_to_list,
   1039 					"dso_to", output);
   1040 		sort_entry__setup_elide(&sort_sym_from,
   1041 					symbol_conf.sym_from_list,
   1042 					"sym_from", output);
   1043 		sort_entry__setup_elide(&sort_sym_to,
   1044 					symbol_conf.sym_to_list,
   1045 					"sym_to", output);
   1046 	} else if (sort__mode == SORT_MODE__MEMORY) {
   1047 		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
   1048 					"symbol_daddr", output);
   1049 		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
   1050 					"dso_daddr", output);
   1051 		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
   1052 					"mem", output);
   1053 		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
   1054 					"local_weight", output);
   1055 		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
   1056 					"tlb", output);
   1057 		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
   1058 					"snoop", output);
   1059 	}
   1060 
   1061 }
   1062