]> Pileus Git - ~andy/linux/blob - tools/perf/util/sort.c
Merge branch 'ttm-fixes-3.13' of git://people.freedesktop.org/~thomash/linux into...
[~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 enum sort_mode  sort__mode = SORT_MODE__NORMAL;
17
18 enum sort_type  sort__first_dimension;
19
20 LIST_HEAD(hist_entry__sort_list);
21
22 static 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
46 static 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 /* --sort pid */
57
58 static int64_t
59 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
60 {
61         return right->thread->tid - left->thread->tid;
62 }
63
64 static 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
72 struct 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 /* --sort comm */
80
81 static int64_t
82 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
83 {
84         /* Compare the addr that should be unique among comm */
85         return comm__str(right->comm) - comm__str(left->comm);
86 }
87
88 static int64_t
89 sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
90 {
91         /* Compare the addr that should be unique among comm */
92         return comm__str(right->comm) - comm__str(left->comm);
93 }
94
95 static 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
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 *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
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         int64_t ret;
184
185         if (!left->ms.sym && !right->ms.sym)
186                 return right->level - left->level;
187
188         /*
189          * comparing symbol address alone is not enough since it's a
190          * relative address within a dso.
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
199 static 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
235 static 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
242 struct 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 /* --sort srcline */
250
251 static int64_t
252 sort__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
275 static 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
282 struct 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 /* --sort parent */
290
291 static int64_t
292 sort__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
303 static 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
310 struct 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 /* --sort cpu */
318
319 static int64_t
320 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
321 {
322         return right->cpu - left->cpu;
323 }
324
325 static 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
331 struct 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 /* sort keys for branch stacks */
339
340 static int64_t
341 sort__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
347 static 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
354 static int64_t
355 sort__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
361 static 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
368 static int64_t
369 sort__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
380 static int64_t
381 sort__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
392 static 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
401 static 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
410 struct 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
417 struct 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
424 struct 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
431 struct 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
438 static int64_t
439 sort__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
449 static 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 /* --sort daddr_sym */
462 static int64_t
463 sort__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
475 static 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
491 static int64_t
492 sort__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
505 static 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
516 static int64_t
517 sort__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
535 static 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
554 static int64_t
555 sort__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
573 static 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
584 static 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; /* -1 for null termination */
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         /* already taken care of */
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
624 static int64_t
625 sort__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
643 static 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
661 static 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; /* -1 for null termination */
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         /* already taken care of */
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
701 static int64_t
702 sort__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
720 static 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
729 static 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; /* -1 for null termination */
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
759 struct 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
766 static u64 he_weight(struct hist_entry *he)
767 {
768         return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
769 }
770
771 static int64_t
772 sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
773 {
774         return he_weight(left) - he_weight(right);
775 }
776
777 static 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
783 struct 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
790 static int64_t
791 sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
792 {
793         return left->stat.weight - right->stat.weight;
794 }
795
796 static 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
802 struct 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
809 struct 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
816 struct 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
823 struct 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
830 struct 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
837 struct 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
844 struct 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
851 static int64_t
852 sort__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
858 static 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
868 struct 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
875 static int64_t
876 sort__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
882 static 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
893 struct 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
900 static int64_t
901 sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
902 {
903         return left->transaction - right->transaction;
904 }
905
906 static inline char *add_str(char *p, const char *str)
907 {
908         strcpy(p, str);
909         return p + strlen(str);
910 }
911
912 static 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
928 int 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; /* :XX<space> */
938         return len;
939 }
940
941 static 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
965 struct 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
972 struct 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
980 static 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
997 static 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
1011 static 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
1022 static 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
1037 int 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
1100 int 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
1126 static 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
1138 void 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          * It makes no sense to elide all of sort entries.
1179          * Just revert them to show up again.
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 }