xref: /openbmc/linux/tools/perf/util/sort.c (revision 8851b9f1)
1 #include "sort.h"
2 #include "hist.h"
3 #include "symbol.h"
4 
5 regex_t		parent_regex;
6 const char	default_parent_pattern[] = "^sys_|^do_page_fault";
7 const char	*parent_pattern = default_parent_pattern;
8 const char	default_sort_order[] = "comm,dso,symbol";
9 const char	*sort_order = default_sort_order;
10 int		sort__need_collapse = 0;
11 int		sort__has_parent = 0;
12 int		sort__has_sym = 0;
13 enum sort_mode	sort__mode = SORT_MODE__NORMAL;
14 
15 enum sort_type	sort__first_dimension;
16 
17 LIST_HEAD(hist_entry__sort_list);
18 
19 static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
20 {
21 	int n;
22 	va_list ap;
23 
24 	va_start(ap, fmt);
25 	n = vsnprintf(bf, size, fmt, ap);
26 	if (symbol_conf.field_sep && n > 0) {
27 		char *sep = bf;
28 
29 		while (1) {
30 			sep = strchr(sep, *symbol_conf.field_sep);
31 			if (sep == NULL)
32 				break;
33 			*sep = '.';
34 		}
35 	}
36 	va_end(ap);
37 
38 	if (n >= (int)size)
39 		return size - 1;
40 	return n;
41 }
42 
43 static int64_t cmp_null(void *l, void *r)
44 {
45 	if (!l && !r)
46 		return 0;
47 	else if (!l)
48 		return -1;
49 	else
50 		return 1;
51 }
52 
53 /* --sort pid */
54 
55 static int64_t
56 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
57 {
58 	return right->thread->pid - left->thread->pid;
59 }
60 
61 static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
62 				       size_t size, unsigned int width)
63 {
64 	return repsep_snprintf(bf, size, "%*s:%5d", width - 6,
65 			      self->thread->comm ?: "", self->thread->pid);
66 }
67 
68 struct sort_entry sort_thread = {
69 	.se_header	= "Command:  Pid",
70 	.se_cmp		= sort__thread_cmp,
71 	.se_snprintf	= hist_entry__thread_snprintf,
72 	.se_width_idx	= HISTC_THREAD,
73 };
74 
75 /* --sort comm */
76 
77 static int64_t
78 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
79 {
80 	return right->thread->pid - left->thread->pid;
81 }
82 
83 static int64_t
84 sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
85 {
86 	char *comm_l = left->thread->comm;
87 	char *comm_r = right->thread->comm;
88 
89 	if (!comm_l || !comm_r)
90 		return cmp_null(comm_l, comm_r);
91 
92 	return strcmp(comm_l, comm_r);
93 }
94 
95 static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
96 				     size_t size, unsigned int width)
97 {
98 	return repsep_snprintf(bf, size, "%*s", width, self->thread->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 *self, char *bf,
150 				    size_t size, unsigned int width)
151 {
152 	return _hist_entry__dso_snprintf(self->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 	if (!left->ms.sym && !right->ms.sym)
184 		return right->level - left->level;
185 
186 	return _sort__sym_cmp(left->ms.sym, right->ms.sym);
187 }
188 
189 static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
190 				     u64 ip, char level, char *bf, size_t size,
191 				     unsigned int width)
192 {
193 	size_t ret = 0;
194 
195 	if (verbose) {
196 		char o = map ? dso__symtab_origin(map->dso) : '!';
197 		ret += repsep_snprintf(bf, size, "%-#*llx %c ",
198 				       BITS_PER_LONG / 4 + 2, ip, o);
199 	}
200 
201 	ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
202 	if (sym && map) {
203 		if (map->type == MAP__VARIABLE) {
204 			ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
205 			ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
206 					ip - map->unmap_ip(map, sym->start));
207 			ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
208 				       width - ret, "");
209 		} else {
210 			ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
211 					       width - ret,
212 					       sym->name);
213 		}
214 	} else {
215 		size_t len = BITS_PER_LONG / 4;
216 		ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
217 				       len, ip);
218 		ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
219 				       width - ret, "");
220 	}
221 
222 	return ret;
223 }
224 
225 static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
226 				    size_t size, unsigned int width)
227 {
228 	return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip,
229 					 self->level, bf, size, width);
230 }
231 
232 struct sort_entry sort_sym = {
233 	.se_header	= "Symbol",
234 	.se_cmp		= sort__sym_cmp,
235 	.se_snprintf	= hist_entry__sym_snprintf,
236 	.se_width_idx	= HISTC_SYMBOL,
237 };
238 
239 /* --sort srcline */
240 
241 static int64_t
242 sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
243 {
244 	return (int64_t)(right->ip - left->ip);
245 }
246 
247 static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf,
248 					size_t size,
249 					unsigned int width __maybe_unused)
250 {
251 	FILE *fp = NULL;
252 	char cmd[PATH_MAX + 2], *path = self->srcline, *nl;
253 	size_t line_len;
254 
255 	if (path != NULL)
256 		goto out_path;
257 
258 	if (!self->ms.map)
259 		goto out_ip;
260 
261 	if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10))
262 		goto out_ip;
263 
264 	snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64,
265 		 self->ms.map->dso->long_name, self->ip);
266 	fp = popen(cmd, "r");
267 	if (!fp)
268 		goto out_ip;
269 
270 	if (getline(&path, &line_len, fp) < 0 || !line_len)
271 		goto out_ip;
272 	self->srcline = strdup(path);
273 	if (self->srcline == NULL)
274 		goto out_ip;
275 
276 	nl = strchr(self->srcline, '\n');
277 	if (nl != NULL)
278 		*nl = '\0';
279 	path = self->srcline;
280 out_path:
281 	if (fp)
282 		pclose(fp);
283 	return repsep_snprintf(bf, size, "%s", path);
284 out_ip:
285 	if (fp)
286 		pclose(fp);
287 	return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip);
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 *self, char *bf,
312 				       size_t size, unsigned int width)
313 {
314 	return repsep_snprintf(bf, size, "%-*s", width,
315 			      self->parent ? self->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 *self, char *bf,
334 				       size_t size, unsigned int width)
335 {
336 	return repsep_snprintf(bf, size, "%*d", width, self->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 *self, char *bf,
356 				    size_t size, unsigned int width)
357 {
358 	return _hist_entry__dso_snprintf(self->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 *self, char *bf,
370 				       size_t size, unsigned int width)
371 {
372 	return _hist_entry__dso_snprintf(self->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 right->level - left->level;
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 right->level - left->level;
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 *self, char *bf,
401 					 size_t size, unsigned int width)
402 {
403 	struct addr_map_symbol *from = &self->branch_info->from;
404 	return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
405 					 self->level, bf, size, width);
406 
407 }
408 
409 static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
410 				       size_t size, unsigned int width)
411 {
412 	struct addr_map_symbol *to = &self->branch_info->to;
413 	return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
414 					 self->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 *self, char *bf,
458 				    size_t size, unsigned int width){
459 	static const char *out = "N/A";
460 
461 	if (self->branch_info->flags.predicted)
462 		out = "N";
463 	else if (self->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 *self, 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 (self->mem_info) {
491 		addr = self->mem_info->daddr.addr;
492 		map = self->mem_info->daddr.map;
493 		sym = self->mem_info->daddr.sym;
494 	}
495 	return _hist_entry__sym_snprintf(map, sym, addr, self->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 *self, char *bf,
514 				    size_t size, unsigned int width)
515 {
516 	struct map *map = NULL;
517 
518 	if (self->mem_info)
519 		map = self->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 *self, char *bf,
544 				    size_t size, unsigned int width)
545 {
546 	const char *out;
547 	u64 mask = PERF_MEM_LOCK_NA;
548 
549 	if (self->mem_info)
550 		mask = self->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 *self, 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 (self->mem_info)
604 		m = self->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 *self, 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 (self->mem_info)
679 		m  = self->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 *self, 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 (self->mem_info)
748 		m = self->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 *self, char *bf,
786 				    size_t size, unsigned int width)
787 {
788 	return repsep_snprintf(bf, size, "%-*llu", width, he_weight(self));
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 *self, char *bf,
805 					      size_t size, unsigned int width)
806 {
807 	return repsep_snprintf(bf, size, "%-*llu", width, self->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 struct sort_dimension {
860 	const char		*name;
861 	struct sort_entry	*entry;
862 	int			taken;
863 };
864 
865 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
866 
867 static struct sort_dimension common_sort_dimensions[] = {
868 	DIM(SORT_PID, "pid", sort_thread),
869 	DIM(SORT_COMM, "comm", sort_comm),
870 	DIM(SORT_DSO, "dso", sort_dso),
871 	DIM(SORT_SYM, "symbol", sort_sym),
872 	DIM(SORT_PARENT, "parent", sort_parent),
873 	DIM(SORT_CPU, "cpu", sort_cpu),
874 	DIM(SORT_SRCLINE, "srcline", sort_srcline),
875 };
876 
877 #undef DIM
878 
879 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
880 
881 static struct sort_dimension bstack_sort_dimensions[] = {
882 	DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
883 	DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
884 	DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
885 	DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
886 	DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
887 };
888 
889 #undef DIM
890 
891 #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
892 
893 static struct sort_dimension memory_sort_dimensions[] = {
894 	DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
895 	DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
896 	DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
897 	DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
898 	DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
899 	DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
900 	DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
901 	DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
902 };
903 
904 #undef DIM
905 
906 static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx)
907 {
908 	if (sd->taken)
909 		return;
910 
911 	if (sd->entry->se_collapse)
912 		sort__need_collapse = 1;
913 
914 	if (list_empty(&hist_entry__sort_list))
915 		sort__first_dimension = idx;
916 
917 	list_add_tail(&sd->entry->list, &hist_entry__sort_list);
918 	sd->taken = 1;
919 }
920 
921 int sort_dimension__add(const char *tok)
922 {
923 	unsigned int i;
924 
925 	for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
926 		struct sort_dimension *sd = &common_sort_dimensions[i];
927 
928 		if (strncasecmp(tok, sd->name, strlen(tok)))
929 			continue;
930 
931 		if (sd->entry == &sort_parent) {
932 			int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
933 			if (ret) {
934 				char err[BUFSIZ];
935 
936 				regerror(ret, &parent_regex, err, sizeof(err));
937 				pr_err("Invalid regex: %s\n%s", parent_pattern, err);
938 				return -EINVAL;
939 			}
940 			sort__has_parent = 1;
941 		} else if (sd->entry == &sort_sym) {
942 			sort__has_sym = 1;
943 		}
944 
945 		__sort_dimension__add(sd, i);
946 		return 0;
947 	}
948 
949 	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
950 		struct sort_dimension *sd = &bstack_sort_dimensions[i];
951 
952 		if (strncasecmp(tok, sd->name, strlen(tok)))
953 			continue;
954 
955 		if (sort__mode != SORT_MODE__BRANCH)
956 			return -EINVAL;
957 
958 		if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
959 			sort__has_sym = 1;
960 
961 		__sort_dimension__add(sd, i + __SORT_BRANCH_STACK);
962 		return 0;
963 	}
964 
965 	for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
966 		struct sort_dimension *sd = &memory_sort_dimensions[i];
967 
968 		if (strncasecmp(tok, sd->name, strlen(tok)))
969 			continue;
970 
971 		if (sort__mode != SORT_MODE__MEMORY)
972 			return -EINVAL;
973 
974 		if (sd->entry == &sort_mem_daddr_sym)
975 			sort__has_sym = 1;
976 
977 		__sort_dimension__add(sd, i + __SORT_MEMORY_MODE);
978 		return 0;
979 	}
980 
981 	return -ESRCH;
982 }
983 
984 int setup_sorting(void)
985 {
986 	char *tmp, *tok, *str = strdup(sort_order);
987 	int ret = 0;
988 
989 	if (str == NULL) {
990 		error("Not enough memory to setup sort keys");
991 		return -ENOMEM;
992 	}
993 
994 	for (tok = strtok_r(str, ", ", &tmp);
995 			tok; tok = strtok_r(NULL, ", ", &tmp)) {
996 		ret = sort_dimension__add(tok);
997 		if (ret == -EINVAL) {
998 			error("Invalid --sort key: `%s'", tok);
999 			break;
1000 		} else if (ret == -ESRCH) {
1001 			error("Unknown --sort key: `%s'", tok);
1002 			break;
1003 		}
1004 	}
1005 
1006 	free(str);
1007 	return ret;
1008 }
1009 
1010 static void sort_entry__setup_elide(struct sort_entry *self,
1011 				    struct strlist *list,
1012 				    const char *list_name, FILE *fp)
1013 {
1014 	if (list && strlist__nr_entries(list) == 1) {
1015 		if (fp != NULL)
1016 			fprintf(fp, "# %s: %s\n", list_name,
1017 				strlist__entry(list, 0)->s);
1018 		self->elide = true;
1019 	}
1020 }
1021 
1022 void sort__setup_elide(FILE *output)
1023 {
1024 	sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1025 				"dso", output);
1026 	sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list,
1027 				"comm", output);
1028 	sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list,
1029 				"symbol", output);
1030 
1031 	if (sort__mode == SORT_MODE__BRANCH) {
1032 		sort_entry__setup_elide(&sort_dso_from,
1033 					symbol_conf.dso_from_list,
1034 					"dso_from", output);
1035 		sort_entry__setup_elide(&sort_dso_to,
1036 					symbol_conf.dso_to_list,
1037 					"dso_to", output);
1038 		sort_entry__setup_elide(&sort_sym_from,
1039 					symbol_conf.sym_from_list,
1040 					"sym_from", output);
1041 		sort_entry__setup_elide(&sort_sym_to,
1042 					symbol_conf.sym_to_list,
1043 					"sym_to", output);
1044 	} else if (sort__mode == SORT_MODE__MEMORY) {
1045 		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1046 					"symbol_daddr", output);
1047 		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1048 					"dso_daddr", output);
1049 		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1050 					"mem", output);
1051 		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1052 					"local_weight", output);
1053 		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1054 					"tlb", output);
1055 		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1056 					"snoop", output);
1057 	}
1058 
1059 }
1060