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