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