1#include "sort.h"
2#include "hist.h"
3#include "comm.h"
4#include "symbol.h"
5
6regex_t parent_regex;
7const char default_parent_pattern[] = "^sys_|^do_page_fault";
8const char *parent_pattern = default_parent_pattern;
9const char default_sort_order[] = "comm,dso,symbol";
10const char *sort_order = default_sort_order;
11regex_t ignore_callees_regex;
12int have_ignore_callees = 0;
13int sort__need_collapse = 0;
14int sort__has_parent = 0;
15int sort__has_sym = 0;
16enum sort_mode sort__mode = SORT_MODE__NORMAL;
17
18enum sort_type sort__first_dimension;
19
20LIST_HEAD(hist_entry__sort_list);
21
22static 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
46static 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
57
58static int64_t
59sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
60{
61 return right->thread->tid - left->thread->tid;
62}
63
64static 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
72struct 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
80
81static int64_t
82sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
83{
84
85 return comm__str(right->comm) - comm__str(left->comm);
86}
87
88static int64_t
89sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
90{
91
92 return comm__str(right->comm) - comm__str(left->comm);
93}
94
95static 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
101struct 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
110
111static 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
131static int64_t
132sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
133{
134 return _sort__dso_cmp(left->ms.map, right->ms.map);
135}
136
137static 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
149static 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
155struct 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
163
164static 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
180static int64_t
181sort__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
190
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
199static 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
235static 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
242struct 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
250
251static int64_t
252sort__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
275static 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
282struct 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
290
291static int64_t
292sort__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
303static 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
310struct 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
318
319static int64_t
320sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
321{
322 return right->cpu - left->cpu;
323}
324
325static 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
331struct 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
339
340static int64_t
341sort__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
347static 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
354static int64_t
355sort__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
361static 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
368static int64_t
369sort__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
380static int64_t
381sort__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
392static 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
401static 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
410struct 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
417struct 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
424struct 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
431struct 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
438static int64_t
439sort__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
449static 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
462static int64_t
463sort__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
475static 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
491static int64_t
492sort__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
505static 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
516static int64_t
517sort__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
535static 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
554static int64_t
555sort__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
573static 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
584static 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;
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
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
624static int64_t
625sort__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
643static 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
661static 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;
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
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
701static int64_t
702sort__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
720static 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
729static 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;
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
759struct 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
766static u64 he_weight(struct hist_entry *he)
767{
768 return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
769}
770
771static int64_t
772sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
773{
774 return he_weight(left) - he_weight(right);
775}
776
777static 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
783struct 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
790static int64_t
791sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
792{
793 return left->stat.weight - right->stat.weight;
794}
795
796static 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
802struct 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
809struct 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
816struct 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
823struct 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
830struct 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
837struct 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
844struct 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
851static int64_t
852sort__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
858static 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
868struct 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
875static int64_t
876sort__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
882static 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
893struct 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
900static int64_t
901sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
902{
903 return left->transaction - right->transaction;
904}
905
906static inline char *add_str(char *p, const char *str)
907{
908 strcpy(p, str);
909 return p + strlen(str);
910}
911
912static 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
928int 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;
938 return len;
939}
940
941static 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
965struct 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
972struct 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
980static 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
997static 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
1011static 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
1022static 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
1037int 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
1100int 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
1126static 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
1138void 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
1179
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