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