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