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