]> Pileus Git - ~andy/linux/blob - tools/perf/util/sort.c
Merge tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64...
[~andy/linux] / tools / perf / util / sort.c
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 }