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