perf evsel: Set attr.task bit for a tracking event
[linux-2.6/btrfs-unstable.git] / tools / perf / util / sort.c
blob7a39c1ed8d37c16cfe24d9dc0a908166658903a4
1 #include <sys/mman.h>
2 #include "sort.h"
3 #include "hist.h"
4 #include "comm.h"
5 #include "symbol.h"
6 #include "evsel.h"
8 regex_t parent_regex;
9 const char default_parent_pattern[] = "^sys_|^do_page_fault";
10 const char *parent_pattern = default_parent_pattern;
11 const char default_sort_order[] = "comm,dso,symbol";
12 const char default_branch_sort_order[] = "comm,dso_from,symbol_from,dso_to,symbol_to";
13 const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
14 const char default_top_sort_order[] = "dso,symbol";
15 const char default_diff_sort_order[] = "dso,symbol";
16 const char *sort_order;
17 const char *field_order;
18 regex_t ignore_callees_regex;
19 int have_ignore_callees = 0;
20 int sort__need_collapse = 0;
21 int sort__has_parent = 0;
22 int sort__has_sym = 0;
23 int sort__has_dso = 0;
24 enum sort_mode sort__mode = SORT_MODE__NORMAL;
27 static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
29 int n;
30 va_list ap;
32 va_start(ap, fmt);
33 n = vsnprintf(bf, size, fmt, ap);
34 if (symbol_conf.field_sep && n > 0) {
35 char *sep = bf;
37 while (1) {
38 sep = strchr(sep, *symbol_conf.field_sep);
39 if (sep == NULL)
40 break;
41 *sep = '.';
44 va_end(ap);
46 if (n >= (int)size)
47 return size - 1;
48 return n;
51 static int64_t cmp_null(const void *l, const void *r)
53 if (!l && !r)
54 return 0;
55 else if (!l)
56 return -1;
57 else
58 return 1;
61 /* --sort pid */
63 static int64_t
64 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
66 return right->thread->tid - left->thread->tid;
69 static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,
70 size_t size, unsigned int width)
72 const char *comm = thread__comm_str(he->thread);
74 width = max(7U, width) - 6;
75 return repsep_snprintf(bf, size, "%5d:%-*.*s", he->thread->tid,
76 width, width, comm ?: "");
79 struct sort_entry sort_thread = {
80 .se_header = " Pid:Command",
81 .se_cmp = sort__thread_cmp,
82 .se_snprintf = hist_entry__thread_snprintf,
83 .se_width_idx = HISTC_THREAD,
86 /* --sort comm */
88 static int64_t
89 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
91 /* Compare the addr that should be unique among comm */
92 return comm__str(right->comm) - comm__str(left->comm);
95 static int64_t
96 sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
98 /* Compare the addr that should be unique among comm */
99 return comm__str(right->comm) - comm__str(left->comm);
102 static int64_t
103 sort__comm_sort(struct hist_entry *left, struct hist_entry *right)
105 return strcmp(comm__str(right->comm), comm__str(left->comm));
108 static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
109 size_t size, unsigned int width)
111 return repsep_snprintf(bf, size, "%-*.*s", width, width, comm__str(he->comm));
114 struct sort_entry sort_comm = {
115 .se_header = "Command",
116 .se_cmp = sort__comm_cmp,
117 .se_collapse = sort__comm_collapse,
118 .se_sort = sort__comm_sort,
119 .se_snprintf = hist_entry__comm_snprintf,
120 .se_width_idx = HISTC_COMM,
123 /* --sort dso */
125 static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
127 struct dso *dso_l = map_l ? map_l->dso : NULL;
128 struct dso *dso_r = map_r ? map_r->dso : NULL;
129 const char *dso_name_l, *dso_name_r;
131 if (!dso_l || !dso_r)
132 return cmp_null(dso_r, dso_l);
134 if (verbose) {
135 dso_name_l = dso_l->long_name;
136 dso_name_r = dso_r->long_name;
137 } else {
138 dso_name_l = dso_l->short_name;
139 dso_name_r = dso_r->short_name;
142 return strcmp(dso_name_l, dso_name_r);
145 static int64_t
146 sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
148 return _sort__dso_cmp(right->ms.map, left->ms.map);
151 static int _hist_entry__dso_snprintf(struct map *map, char *bf,
152 size_t size, unsigned int width)
154 if (map && map->dso) {
155 const char *dso_name = !verbose ? map->dso->short_name :
156 map->dso->long_name;
157 return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name);
160 return repsep_snprintf(bf, size, "%-*.*s", width, width, "[unknown]");
163 static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,
164 size_t size, unsigned int width)
166 return _hist_entry__dso_snprintf(he->ms.map, bf, size, width);
169 struct sort_entry sort_dso = {
170 .se_header = "Shared Object",
171 .se_cmp = sort__dso_cmp,
172 .se_snprintf = hist_entry__dso_snprintf,
173 .se_width_idx = HISTC_DSO,
176 /* --sort symbol */
178 static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip)
180 return (int64_t)(right_ip - left_ip);
183 static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
185 u64 ip_l, ip_r;
187 if (!sym_l || !sym_r)
188 return cmp_null(sym_l, sym_r);
190 if (sym_l == sym_r)
191 return 0;
193 ip_l = sym_l->start;
194 ip_r = sym_r->start;
196 return (int64_t)(ip_r - ip_l);
199 static int64_t
200 sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
202 int64_t ret;
204 if (!left->ms.sym && !right->ms.sym)
205 return _sort__addr_cmp(left->ip, right->ip);
208 * comparing symbol address alone is not enough since it's a
209 * relative address within a dso.
211 if (!sort__has_dso) {
212 ret = sort__dso_cmp(left, right);
213 if (ret != 0)
214 return ret;
217 return _sort__sym_cmp(left->ms.sym, right->ms.sym);
220 static int64_t
221 sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
223 if (!left->ms.sym || !right->ms.sym)
224 return cmp_null(left->ms.sym, right->ms.sym);
226 return strcmp(right->ms.sym->name, left->ms.sym->name);
229 static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
230 u64 ip, char level, char *bf, size_t size,
231 unsigned int width)
233 size_t ret = 0;
235 if (verbose) {
236 char o = map ? dso__symtab_origin(map->dso) : '!';
237 ret += repsep_snprintf(bf, size, "%-#*llx %c ",
238 BITS_PER_LONG / 4 + 2, ip, o);
241 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
242 if (sym && map) {
243 if (map->type == MAP__VARIABLE) {
244 ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
245 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
246 ip - map->unmap_ip(map, sym->start));
247 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
248 width - ret, "");
249 } else {
250 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
251 width - ret,
252 sym->name);
254 } else {
255 size_t len = BITS_PER_LONG / 4;
256 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
257 len, ip);
258 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
259 width - ret, "");
262 if (ret > width)
263 bf[width] = '\0';
265 return width;
268 static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,
269 size_t size, unsigned int width)
271 return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip,
272 he->level, bf, size, width);
275 struct sort_entry sort_sym = {
276 .se_header = "Symbol",
277 .se_cmp = sort__sym_cmp,
278 .se_sort = sort__sym_sort,
279 .se_snprintf = hist_entry__sym_snprintf,
280 .se_width_idx = HISTC_SYMBOL,
283 /* --sort srcline */
285 static int64_t
286 sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
288 if (!left->srcline) {
289 if (!left->ms.map)
290 left->srcline = SRCLINE_UNKNOWN;
291 else {
292 struct map *map = left->ms.map;
293 left->srcline = get_srcline(map->dso,
294 map__rip_2objdump(map, left->ip),
295 left->ms.sym, true);
298 if (!right->srcline) {
299 if (!right->ms.map)
300 right->srcline = SRCLINE_UNKNOWN;
301 else {
302 struct map *map = right->ms.map;
303 right->srcline = get_srcline(map->dso,
304 map__rip_2objdump(map, right->ip),
305 right->ms.sym, true);
308 return strcmp(right->srcline, left->srcline);
311 static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
312 size_t size, unsigned int width)
314 return repsep_snprintf(bf, size, "%-*.*s", width, width, he->srcline);
317 struct sort_entry sort_srcline = {
318 .se_header = "Source:Line",
319 .se_cmp = sort__srcline_cmp,
320 .se_snprintf = hist_entry__srcline_snprintf,
321 .se_width_idx = HISTC_SRCLINE,
324 /* --sort parent */
326 static int64_t
327 sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
329 struct symbol *sym_l = left->parent;
330 struct symbol *sym_r = right->parent;
332 if (!sym_l || !sym_r)
333 return cmp_null(sym_l, sym_r);
335 return strcmp(sym_r->name, sym_l->name);
338 static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
339 size_t size, unsigned int width)
341 return repsep_snprintf(bf, size, "%-*.*s", width, width,
342 he->parent ? he->parent->name : "[other]");
345 struct sort_entry sort_parent = {
346 .se_header = "Parent symbol",
347 .se_cmp = sort__parent_cmp,
348 .se_snprintf = hist_entry__parent_snprintf,
349 .se_width_idx = HISTC_PARENT,
352 /* --sort cpu */
354 static int64_t
355 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
357 return right->cpu - left->cpu;
360 static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf,
361 size_t size, unsigned int width)
363 return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu);
366 struct sort_entry sort_cpu = {
367 .se_header = "CPU",
368 .se_cmp = sort__cpu_cmp,
369 .se_snprintf = hist_entry__cpu_snprintf,
370 .se_width_idx = HISTC_CPU,
373 /* sort keys for branch stacks */
375 static int64_t
376 sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
378 if (!left->branch_info || !right->branch_info)
379 return cmp_null(left->branch_info, right->branch_info);
381 return _sort__dso_cmp(left->branch_info->from.map,
382 right->branch_info->from.map);
385 static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,
386 size_t size, unsigned int width)
388 if (he->branch_info)
389 return _hist_entry__dso_snprintf(he->branch_info->from.map,
390 bf, size, width);
391 else
392 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
395 static int64_t
396 sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
398 if (!left->branch_info || !right->branch_info)
399 return cmp_null(left->branch_info, right->branch_info);
401 return _sort__dso_cmp(left->branch_info->to.map,
402 right->branch_info->to.map);
405 static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,
406 size_t size, unsigned int width)
408 if (he->branch_info)
409 return _hist_entry__dso_snprintf(he->branch_info->to.map,
410 bf, size, width);
411 else
412 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
415 static int64_t
416 sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
418 struct addr_map_symbol *from_l = &left->branch_info->from;
419 struct addr_map_symbol *from_r = &right->branch_info->from;
421 if (!left->branch_info || !right->branch_info)
422 return cmp_null(left->branch_info, right->branch_info);
424 from_l = &left->branch_info->from;
425 from_r = &right->branch_info->from;
427 if (!from_l->sym && !from_r->sym)
428 return _sort__addr_cmp(from_l->addr, from_r->addr);
430 return _sort__sym_cmp(from_l->sym, from_r->sym);
433 static int64_t
434 sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
436 struct addr_map_symbol *to_l, *to_r;
438 if (!left->branch_info || !right->branch_info)
439 return cmp_null(left->branch_info, right->branch_info);
441 to_l = &left->branch_info->to;
442 to_r = &right->branch_info->to;
444 if (!to_l->sym && !to_r->sym)
445 return _sort__addr_cmp(to_l->addr, to_r->addr);
447 return _sort__sym_cmp(to_l->sym, to_r->sym);
450 static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,
451 size_t size, unsigned int width)
453 if (he->branch_info) {
454 struct addr_map_symbol *from = &he->branch_info->from;
456 return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
457 he->level, bf, size, width);
460 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
463 static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,
464 size_t size, unsigned int width)
466 if (he->branch_info) {
467 struct addr_map_symbol *to = &he->branch_info->to;
469 return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
470 he->level, bf, size, width);
473 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
476 struct sort_entry sort_dso_from = {
477 .se_header = "Source Shared Object",
478 .se_cmp = sort__dso_from_cmp,
479 .se_snprintf = hist_entry__dso_from_snprintf,
480 .se_width_idx = HISTC_DSO_FROM,
483 struct sort_entry sort_dso_to = {
484 .se_header = "Target Shared Object",
485 .se_cmp = sort__dso_to_cmp,
486 .se_snprintf = hist_entry__dso_to_snprintf,
487 .se_width_idx = HISTC_DSO_TO,
490 struct sort_entry sort_sym_from = {
491 .se_header = "Source Symbol",
492 .se_cmp = sort__sym_from_cmp,
493 .se_snprintf = hist_entry__sym_from_snprintf,
494 .se_width_idx = HISTC_SYMBOL_FROM,
497 struct sort_entry sort_sym_to = {
498 .se_header = "Target Symbol",
499 .se_cmp = sort__sym_to_cmp,
500 .se_snprintf = hist_entry__sym_to_snprintf,
501 .se_width_idx = HISTC_SYMBOL_TO,
504 static int64_t
505 sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
507 unsigned char mp, p;
509 if (!left->branch_info || !right->branch_info)
510 return cmp_null(left->branch_info, right->branch_info);
512 mp = left->branch_info->flags.mispred != right->branch_info->flags.mispred;
513 p = left->branch_info->flags.predicted != right->branch_info->flags.predicted;
514 return mp || p;
517 static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,
518 size_t size, unsigned int width){
519 static const char *out = "N/A";
521 if (he->branch_info) {
522 if (he->branch_info->flags.predicted)
523 out = "N";
524 else if (he->branch_info->flags.mispred)
525 out = "Y";
528 return repsep_snprintf(bf, size, "%-*.*s", width, width, out);
531 /* --sort daddr_sym */
532 static int64_t
533 sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
535 uint64_t l = 0, r = 0;
537 if (left->mem_info)
538 l = left->mem_info->daddr.addr;
539 if (right->mem_info)
540 r = right->mem_info->daddr.addr;
542 return (int64_t)(r - l);
545 static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,
546 size_t size, unsigned int width)
548 uint64_t addr = 0;
549 struct map *map = NULL;
550 struct symbol *sym = NULL;
552 if (he->mem_info) {
553 addr = he->mem_info->daddr.addr;
554 map = he->mem_info->daddr.map;
555 sym = he->mem_info->daddr.sym;
557 return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
558 width);
561 static int64_t
562 sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
564 struct map *map_l = NULL;
565 struct map *map_r = NULL;
567 if (left->mem_info)
568 map_l = left->mem_info->daddr.map;
569 if (right->mem_info)
570 map_r = right->mem_info->daddr.map;
572 return _sort__dso_cmp(map_l, map_r);
575 static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,
576 size_t size, unsigned int width)
578 struct map *map = NULL;
580 if (he->mem_info)
581 map = he->mem_info->daddr.map;
583 return _hist_entry__dso_snprintf(map, bf, size, width);
586 static int64_t
587 sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
589 union perf_mem_data_src data_src_l;
590 union perf_mem_data_src data_src_r;
592 if (left->mem_info)
593 data_src_l = left->mem_info->data_src;
594 else
595 data_src_l.mem_lock = PERF_MEM_LOCK_NA;
597 if (right->mem_info)
598 data_src_r = right->mem_info->data_src;
599 else
600 data_src_r.mem_lock = PERF_MEM_LOCK_NA;
602 return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
605 static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,
606 size_t size, unsigned int width)
608 const char *out;
609 u64 mask = PERF_MEM_LOCK_NA;
611 if (he->mem_info)
612 mask = he->mem_info->data_src.mem_lock;
614 if (mask & PERF_MEM_LOCK_NA)
615 out = "N/A";
616 else if (mask & PERF_MEM_LOCK_LOCKED)
617 out = "Yes";
618 else
619 out = "No";
621 return repsep_snprintf(bf, size, "%-*s", width, out);
624 static int64_t
625 sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
627 union perf_mem_data_src data_src_l;
628 union perf_mem_data_src data_src_r;
630 if (left->mem_info)
631 data_src_l = left->mem_info->data_src;
632 else
633 data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
635 if (right->mem_info)
636 data_src_r = right->mem_info->data_src;
637 else
638 data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
640 return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
643 static const char * const tlb_access[] = {
644 "N/A",
645 "HIT",
646 "MISS",
647 "L1",
648 "L2",
649 "Walker",
650 "Fault",
652 #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
654 static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,
655 size_t size, unsigned int width)
657 char out[64];
658 size_t sz = sizeof(out) - 1; /* -1 for null termination */
659 size_t l = 0, i;
660 u64 m = PERF_MEM_TLB_NA;
661 u64 hit, miss;
663 out[0] = '\0';
665 if (he->mem_info)
666 m = he->mem_info->data_src.mem_dtlb;
668 hit = m & PERF_MEM_TLB_HIT;
669 miss = m & PERF_MEM_TLB_MISS;
671 /* already taken care of */
672 m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
674 for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) {
675 if (!(m & 0x1))
676 continue;
677 if (l) {
678 strcat(out, " or ");
679 l += 4;
681 strncat(out, tlb_access[i], sz - l);
682 l += strlen(tlb_access[i]);
684 if (*out == '\0')
685 strcpy(out, "N/A");
686 if (hit)
687 strncat(out, " hit", sz - l);
688 if (miss)
689 strncat(out, " miss", sz - l);
691 return repsep_snprintf(bf, size, "%-*s", width, out);
694 static int64_t
695 sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
697 union perf_mem_data_src data_src_l;
698 union perf_mem_data_src data_src_r;
700 if (left->mem_info)
701 data_src_l = left->mem_info->data_src;
702 else
703 data_src_l.mem_lvl = PERF_MEM_LVL_NA;
705 if (right->mem_info)
706 data_src_r = right->mem_info->data_src;
707 else
708 data_src_r.mem_lvl = PERF_MEM_LVL_NA;
710 return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
713 static const char * const mem_lvl[] = {
714 "N/A",
715 "HIT",
716 "MISS",
717 "L1",
718 "LFB",
719 "L2",
720 "L3",
721 "Local RAM",
722 "Remote RAM (1 hop)",
723 "Remote RAM (2 hops)",
724 "Remote Cache (1 hop)",
725 "Remote Cache (2 hops)",
726 "I/O",
727 "Uncached",
729 #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
731 static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,
732 size_t size, unsigned int width)
734 char out[64];
735 size_t sz = sizeof(out) - 1; /* -1 for null termination */
736 size_t i, l = 0;
737 u64 m = PERF_MEM_LVL_NA;
738 u64 hit, miss;
740 if (he->mem_info)
741 m = he->mem_info->data_src.mem_lvl;
743 out[0] = '\0';
745 hit = m & PERF_MEM_LVL_HIT;
746 miss = m & PERF_MEM_LVL_MISS;
748 /* already taken care of */
749 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
751 for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) {
752 if (!(m & 0x1))
753 continue;
754 if (l) {
755 strcat(out, " or ");
756 l += 4;
758 strncat(out, mem_lvl[i], sz - l);
759 l += strlen(mem_lvl[i]);
761 if (*out == '\0')
762 strcpy(out, "N/A");
763 if (hit)
764 strncat(out, " hit", sz - l);
765 if (miss)
766 strncat(out, " miss", sz - l);
768 return repsep_snprintf(bf, size, "%-*s", width, out);
771 static int64_t
772 sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
774 union perf_mem_data_src data_src_l;
775 union perf_mem_data_src data_src_r;
777 if (left->mem_info)
778 data_src_l = left->mem_info->data_src;
779 else
780 data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
782 if (right->mem_info)
783 data_src_r = right->mem_info->data_src;
784 else
785 data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
787 return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
790 static const char * const snoop_access[] = {
791 "N/A",
792 "None",
793 "Miss",
794 "Hit",
795 "HitM",
797 #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
799 static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
800 size_t size, unsigned int width)
802 char out[64];
803 size_t sz = sizeof(out) - 1; /* -1 for null termination */
804 size_t i, l = 0;
805 u64 m = PERF_MEM_SNOOP_NA;
807 out[0] = '\0';
809 if (he->mem_info)
810 m = he->mem_info->data_src.mem_snoop;
812 for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {
813 if (!(m & 0x1))
814 continue;
815 if (l) {
816 strcat(out, " or ");
817 l += 4;
819 strncat(out, snoop_access[i], sz - l);
820 l += strlen(snoop_access[i]);
823 if (*out == '\0')
824 strcpy(out, "N/A");
826 return repsep_snprintf(bf, size, "%-*s", width, out);
829 static inline u64 cl_address(u64 address)
831 /* return the cacheline of the address */
832 return (address & ~(cacheline_size - 1));
835 static int64_t
836 sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
838 u64 l, r;
839 struct map *l_map, *r_map;
841 if (!left->mem_info) return -1;
842 if (!right->mem_info) return 1;
844 /* group event types together */
845 if (left->cpumode > right->cpumode) return -1;
846 if (left->cpumode < right->cpumode) return 1;
848 l_map = left->mem_info->daddr.map;
849 r_map = right->mem_info->daddr.map;
851 /* if both are NULL, jump to sort on al_addr instead */
852 if (!l_map && !r_map)
853 goto addr;
855 if (!l_map) return -1;
856 if (!r_map) return 1;
858 if (l_map->maj > r_map->maj) return -1;
859 if (l_map->maj < r_map->maj) return 1;
861 if (l_map->min > r_map->min) return -1;
862 if (l_map->min < r_map->min) return 1;
864 if (l_map->ino > r_map->ino) return -1;
865 if (l_map->ino < r_map->ino) return 1;
867 if (l_map->ino_generation > r_map->ino_generation) return -1;
868 if (l_map->ino_generation < r_map->ino_generation) return 1;
871 * Addresses with no major/minor numbers are assumed to be
872 * anonymous in userspace. Sort those on pid then address.
874 * The kernel and non-zero major/minor mapped areas are
875 * assumed to be unity mapped. Sort those on address.
878 if ((left->cpumode != PERF_RECORD_MISC_KERNEL) &&
879 (!(l_map->flags & MAP_SHARED)) &&
880 !l_map->maj && !l_map->min && !l_map->ino &&
881 !l_map->ino_generation) {
882 /* userspace anonymous */
884 if (left->thread->pid_ > right->thread->pid_) return -1;
885 if (left->thread->pid_ < right->thread->pid_) return 1;
888 addr:
889 /* al_addr does all the right addr - start + offset calculations */
890 l = cl_address(left->mem_info->daddr.al_addr);
891 r = cl_address(right->mem_info->daddr.al_addr);
893 if (l > r) return -1;
894 if (l < r) return 1;
896 return 0;
899 static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf,
900 size_t size, unsigned int width)
903 uint64_t addr = 0;
904 struct map *map = NULL;
905 struct symbol *sym = NULL;
906 char level = he->level;
908 if (he->mem_info) {
909 addr = cl_address(he->mem_info->daddr.al_addr);
910 map = he->mem_info->daddr.map;
911 sym = he->mem_info->daddr.sym;
913 /* print [s] for shared data mmaps */
914 if ((he->cpumode != PERF_RECORD_MISC_KERNEL) &&
915 map && (map->type == MAP__VARIABLE) &&
916 (map->flags & MAP_SHARED) &&
917 (map->maj || map->min || map->ino ||
918 map->ino_generation))
919 level = 's';
920 else if (!map)
921 level = 'X';
923 return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size,
924 width);
927 struct sort_entry sort_mispredict = {
928 .se_header = "Branch Mispredicted",
929 .se_cmp = sort__mispredict_cmp,
930 .se_snprintf = hist_entry__mispredict_snprintf,
931 .se_width_idx = HISTC_MISPREDICT,
934 static u64 he_weight(struct hist_entry *he)
936 return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
939 static int64_t
940 sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
942 return he_weight(left) - he_weight(right);
945 static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,
946 size_t size, unsigned int width)
948 return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he));
951 struct sort_entry sort_local_weight = {
952 .se_header = "Local Weight",
953 .se_cmp = sort__local_weight_cmp,
954 .se_snprintf = hist_entry__local_weight_snprintf,
955 .se_width_idx = HISTC_LOCAL_WEIGHT,
958 static int64_t
959 sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
961 return left->stat.weight - right->stat.weight;
964 static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,
965 size_t size, unsigned int width)
967 return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight);
970 struct sort_entry sort_global_weight = {
971 .se_header = "Weight",
972 .se_cmp = sort__global_weight_cmp,
973 .se_snprintf = hist_entry__global_weight_snprintf,
974 .se_width_idx = HISTC_GLOBAL_WEIGHT,
977 struct sort_entry sort_mem_daddr_sym = {
978 .se_header = "Data Symbol",
979 .se_cmp = sort__daddr_cmp,
980 .se_snprintf = hist_entry__daddr_snprintf,
981 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
984 struct sort_entry sort_mem_daddr_dso = {
985 .se_header = "Data Object",
986 .se_cmp = sort__dso_daddr_cmp,
987 .se_snprintf = hist_entry__dso_daddr_snprintf,
988 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
991 struct sort_entry sort_mem_locked = {
992 .se_header = "Locked",
993 .se_cmp = sort__locked_cmp,
994 .se_snprintf = hist_entry__locked_snprintf,
995 .se_width_idx = HISTC_MEM_LOCKED,
998 struct sort_entry sort_mem_tlb = {
999 .se_header = "TLB access",
1000 .se_cmp = sort__tlb_cmp,
1001 .se_snprintf = hist_entry__tlb_snprintf,
1002 .se_width_idx = HISTC_MEM_TLB,
1005 struct sort_entry sort_mem_lvl = {
1006 .se_header = "Memory access",
1007 .se_cmp = sort__lvl_cmp,
1008 .se_snprintf = hist_entry__lvl_snprintf,
1009 .se_width_idx = HISTC_MEM_LVL,
1012 struct sort_entry sort_mem_snoop = {
1013 .se_header = "Snoop",
1014 .se_cmp = sort__snoop_cmp,
1015 .se_snprintf = hist_entry__snoop_snprintf,
1016 .se_width_idx = HISTC_MEM_SNOOP,
1019 struct sort_entry sort_mem_dcacheline = {
1020 .se_header = "Data Cacheline",
1021 .se_cmp = sort__dcacheline_cmp,
1022 .se_snprintf = hist_entry__dcacheline_snprintf,
1023 .se_width_idx = HISTC_MEM_DCACHELINE,
1026 static int64_t
1027 sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
1029 if (!left->branch_info || !right->branch_info)
1030 return cmp_null(left->branch_info, right->branch_info);
1032 return left->branch_info->flags.abort !=
1033 right->branch_info->flags.abort;
1036 static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf,
1037 size_t size, unsigned int width)
1039 static const char *out = "N/A";
1041 if (he->branch_info) {
1042 if (he->branch_info->flags.abort)
1043 out = "A";
1044 else
1045 out = ".";
1048 return repsep_snprintf(bf, size, "%-*s", width, out);
1051 struct sort_entry sort_abort = {
1052 .se_header = "Transaction abort",
1053 .se_cmp = sort__abort_cmp,
1054 .se_snprintf = hist_entry__abort_snprintf,
1055 .se_width_idx = HISTC_ABORT,
1058 static int64_t
1059 sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right)
1061 if (!left->branch_info || !right->branch_info)
1062 return cmp_null(left->branch_info, right->branch_info);
1064 return left->branch_info->flags.in_tx !=
1065 right->branch_info->flags.in_tx;
1068 static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf,
1069 size_t size, unsigned int width)
1071 static const char *out = "N/A";
1073 if (he->branch_info) {
1074 if (he->branch_info->flags.in_tx)
1075 out = "T";
1076 else
1077 out = ".";
1080 return repsep_snprintf(bf, size, "%-*s", width, out);
1083 struct sort_entry sort_in_tx = {
1084 .se_header = "Branch in transaction",
1085 .se_cmp = sort__in_tx_cmp,
1086 .se_snprintf = hist_entry__in_tx_snprintf,
1087 .se_width_idx = HISTC_IN_TX,
1090 static int64_t
1091 sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
1093 return left->transaction - right->transaction;
1096 static inline char *add_str(char *p, const char *str)
1098 strcpy(p, str);
1099 return p + strlen(str);
1102 static struct txbit {
1103 unsigned flag;
1104 const char *name;
1105 int skip_for_len;
1106 } txbits[] = {
1107 { PERF_TXN_ELISION, "EL ", 0 },
1108 { PERF_TXN_TRANSACTION, "TX ", 1 },
1109 { PERF_TXN_SYNC, "SYNC ", 1 },
1110 { PERF_TXN_ASYNC, "ASYNC ", 0 },
1111 { PERF_TXN_RETRY, "RETRY ", 0 },
1112 { PERF_TXN_CONFLICT, "CON ", 0 },
1113 { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 },
1114 { PERF_TXN_CAPACITY_READ, "CAP-READ ", 0 },
1115 { 0, NULL, 0 }
1118 int hist_entry__transaction_len(void)
1120 int i;
1121 int len = 0;
1123 for (i = 0; txbits[i].name; i++) {
1124 if (!txbits[i].skip_for_len)
1125 len += strlen(txbits[i].name);
1127 len += 4; /* :XX<space> */
1128 return len;
1131 static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf,
1132 size_t size, unsigned int width)
1134 u64 t = he->transaction;
1135 char buf[128];
1136 char *p = buf;
1137 int i;
1139 buf[0] = 0;
1140 for (i = 0; txbits[i].name; i++)
1141 if (txbits[i].flag & t)
1142 p = add_str(p, txbits[i].name);
1143 if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC)))
1144 p = add_str(p, "NEITHER ");
1145 if (t & PERF_TXN_ABORT_MASK) {
1146 sprintf(p, ":%" PRIx64,
1147 (t & PERF_TXN_ABORT_MASK) >>
1148 PERF_TXN_ABORT_SHIFT);
1149 p += strlen(p);
1152 return repsep_snprintf(bf, size, "%-*s", width, buf);
1155 struct sort_entry sort_transaction = {
1156 .se_header = "Transaction ",
1157 .se_cmp = sort__transaction_cmp,
1158 .se_snprintf = hist_entry__transaction_snprintf,
1159 .se_width_idx = HISTC_TRANSACTION,
1162 struct sort_dimension {
1163 const char *name;
1164 struct sort_entry *entry;
1165 int taken;
1168 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
1170 static struct sort_dimension common_sort_dimensions[] = {
1171 DIM(SORT_PID, "pid", sort_thread),
1172 DIM(SORT_COMM, "comm", sort_comm),
1173 DIM(SORT_DSO, "dso", sort_dso),
1174 DIM(SORT_SYM, "symbol", sort_sym),
1175 DIM(SORT_PARENT, "parent", sort_parent),
1176 DIM(SORT_CPU, "cpu", sort_cpu),
1177 DIM(SORT_SRCLINE, "srcline", sort_srcline),
1178 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
1179 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
1180 DIM(SORT_TRANSACTION, "transaction", sort_transaction),
1183 #undef DIM
1185 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
1187 static struct sort_dimension bstack_sort_dimensions[] = {
1188 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
1189 DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
1190 DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
1191 DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
1192 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
1193 DIM(SORT_IN_TX, "in_tx", sort_in_tx),
1194 DIM(SORT_ABORT, "abort", sort_abort),
1197 #undef DIM
1199 #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
1201 static struct sort_dimension memory_sort_dimensions[] = {
1202 DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
1203 DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
1204 DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
1205 DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
1206 DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
1207 DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
1208 DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline),
1211 #undef DIM
1213 struct hpp_dimension {
1214 const char *name;
1215 struct perf_hpp_fmt *fmt;
1216 int taken;
1219 #define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
1221 static struct hpp_dimension hpp_sort_dimensions[] = {
1222 DIM(PERF_HPP__OVERHEAD, "overhead"),
1223 DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"),
1224 DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
1225 DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
1226 DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
1227 DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"),
1228 DIM(PERF_HPP__SAMPLES, "sample"),
1229 DIM(PERF_HPP__PERIOD, "period"),
1232 #undef DIM
1234 struct hpp_sort_entry {
1235 struct perf_hpp_fmt hpp;
1236 struct sort_entry *se;
1239 bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1241 struct hpp_sort_entry *hse_a;
1242 struct hpp_sort_entry *hse_b;
1244 if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
1245 return false;
1247 hse_a = container_of(a, struct hpp_sort_entry, hpp);
1248 hse_b = container_of(b, struct hpp_sort_entry, hpp);
1250 return hse_a->se == hse_b->se;
1253 void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists)
1255 struct hpp_sort_entry *hse;
1257 if (!perf_hpp__is_sort_entry(fmt))
1258 return;
1260 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1261 hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name));
1264 static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1265 struct perf_evsel *evsel)
1267 struct hpp_sort_entry *hse;
1268 size_t len = fmt->user_len;
1270 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1272 if (!len)
1273 len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx);
1275 return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name);
1278 static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
1279 struct perf_hpp *hpp __maybe_unused,
1280 struct perf_evsel *evsel)
1282 struct hpp_sort_entry *hse;
1283 size_t len = fmt->user_len;
1285 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1287 if (!len)
1288 len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx);
1290 return len;
1293 static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1294 struct hist_entry *he)
1296 struct hpp_sort_entry *hse;
1297 size_t len = fmt->user_len;
1299 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1301 if (!len)
1302 len = hists__col_len(he->hists, hse->se->se_width_idx);
1304 return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
1307 static int64_t __sort__hpp_cmp(struct perf_hpp_fmt *fmt,
1308 struct hist_entry *a, struct hist_entry *b)
1310 struct hpp_sort_entry *hse;
1312 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1313 return hse->se->se_cmp(a, b);
1316 static int64_t __sort__hpp_collapse(struct perf_hpp_fmt *fmt,
1317 struct hist_entry *a, struct hist_entry *b)
1319 struct hpp_sort_entry *hse;
1320 int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
1322 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1323 collapse_fn = hse->se->se_collapse ?: hse->se->se_cmp;
1324 return collapse_fn(a, b);
1327 static int64_t __sort__hpp_sort(struct perf_hpp_fmt *fmt,
1328 struct hist_entry *a, struct hist_entry *b)
1330 struct hpp_sort_entry *hse;
1331 int64_t (*sort_fn)(struct hist_entry *, struct hist_entry *);
1333 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1334 sort_fn = hse->se->se_sort ?: hse->se->se_cmp;
1335 return sort_fn(a, b);
1338 static struct hpp_sort_entry *
1339 __sort_dimension__alloc_hpp(struct sort_dimension *sd)
1341 struct hpp_sort_entry *hse;
1343 hse = malloc(sizeof(*hse));
1344 if (hse == NULL) {
1345 pr_err("Memory allocation failed\n");
1346 return NULL;
1349 hse->se = sd->entry;
1350 hse->hpp.name = sd->entry->se_header;
1351 hse->hpp.header = __sort__hpp_header;
1352 hse->hpp.width = __sort__hpp_width;
1353 hse->hpp.entry = __sort__hpp_entry;
1354 hse->hpp.color = NULL;
1356 hse->hpp.cmp = __sort__hpp_cmp;
1357 hse->hpp.collapse = __sort__hpp_collapse;
1358 hse->hpp.sort = __sort__hpp_sort;
1360 INIT_LIST_HEAD(&hse->hpp.list);
1361 INIT_LIST_HEAD(&hse->hpp.sort_list);
1362 hse->hpp.elide = false;
1363 hse->hpp.len = 0;
1364 hse->hpp.user_len = 0;
1366 return hse;
1369 bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
1371 return format->header == __sort__hpp_header;
1374 static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd)
1376 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1378 if (hse == NULL)
1379 return -1;
1381 perf_hpp__register_sort_field(&hse->hpp);
1382 return 0;
1385 static int __sort_dimension__add_hpp_output(struct sort_dimension *sd)
1387 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1389 if (hse == NULL)
1390 return -1;
1392 perf_hpp__column_register(&hse->hpp);
1393 return 0;
1396 static int __sort_dimension__add(struct sort_dimension *sd)
1398 if (sd->taken)
1399 return 0;
1401 if (__sort_dimension__add_hpp_sort(sd) < 0)
1402 return -1;
1404 if (sd->entry->se_collapse)
1405 sort__need_collapse = 1;
1407 sd->taken = 1;
1409 return 0;
1412 static int __hpp_dimension__add(struct hpp_dimension *hd)
1414 if (!hd->taken) {
1415 hd->taken = 1;
1417 perf_hpp__register_sort_field(hd->fmt);
1419 return 0;
1422 static int __sort_dimension__add_output(struct sort_dimension *sd)
1424 if (sd->taken)
1425 return 0;
1427 if (__sort_dimension__add_hpp_output(sd) < 0)
1428 return -1;
1430 sd->taken = 1;
1431 return 0;
1434 static int __hpp_dimension__add_output(struct hpp_dimension *hd)
1436 if (!hd->taken) {
1437 hd->taken = 1;
1439 perf_hpp__column_register(hd->fmt);
1441 return 0;
1444 int sort_dimension__add(const char *tok)
1446 unsigned int i;
1448 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1449 struct sort_dimension *sd = &common_sort_dimensions[i];
1451 if (strncasecmp(tok, sd->name, strlen(tok)))
1452 continue;
1454 if (sd->entry == &sort_parent) {
1455 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
1456 if (ret) {
1457 char err[BUFSIZ];
1459 regerror(ret, &parent_regex, err, sizeof(err));
1460 pr_err("Invalid regex: %s\n%s", parent_pattern, err);
1461 return -EINVAL;
1463 sort__has_parent = 1;
1464 } else if (sd->entry == &sort_sym) {
1465 sort__has_sym = 1;
1466 } else if (sd->entry == &sort_dso) {
1467 sort__has_dso = 1;
1470 return __sort_dimension__add(sd);
1473 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1474 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1476 if (strncasecmp(tok, hd->name, strlen(tok)))
1477 continue;
1479 return __hpp_dimension__add(hd);
1482 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1483 struct sort_dimension *sd = &bstack_sort_dimensions[i];
1485 if (strncasecmp(tok, sd->name, strlen(tok)))
1486 continue;
1488 if (sort__mode != SORT_MODE__BRANCH)
1489 return -EINVAL;
1491 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
1492 sort__has_sym = 1;
1494 __sort_dimension__add(sd);
1495 return 0;
1498 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1499 struct sort_dimension *sd = &memory_sort_dimensions[i];
1501 if (strncasecmp(tok, sd->name, strlen(tok)))
1502 continue;
1504 if (sort__mode != SORT_MODE__MEMORY)
1505 return -EINVAL;
1507 if (sd->entry == &sort_mem_daddr_sym)
1508 sort__has_sym = 1;
1510 __sort_dimension__add(sd);
1511 return 0;
1514 return -ESRCH;
1517 static const char *get_default_sort_order(void)
1519 const char *default_sort_orders[] = {
1520 default_sort_order,
1521 default_branch_sort_order,
1522 default_mem_sort_order,
1523 default_top_sort_order,
1524 default_diff_sort_order,
1527 BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
1529 return default_sort_orders[sort__mode];
1532 static int setup_sort_order(void)
1534 char *new_sort_order;
1537 * Append '+'-prefixed sort order to the default sort
1538 * order string.
1540 if (!sort_order || is_strict_order(sort_order))
1541 return 0;
1543 if (sort_order[1] == '\0') {
1544 error("Invalid --sort key: `+'");
1545 return -EINVAL;
1549 * We allocate new sort_order string, but we never free it,
1550 * because it's checked over the rest of the code.
1552 if (asprintf(&new_sort_order, "%s,%s",
1553 get_default_sort_order(), sort_order + 1) < 0) {
1554 error("Not enough memory to set up --sort");
1555 return -ENOMEM;
1558 sort_order = new_sort_order;
1559 return 0;
1562 static int __setup_sorting(void)
1564 char *tmp, *tok, *str;
1565 const char *sort_keys;
1566 int ret = 0;
1568 ret = setup_sort_order();
1569 if (ret)
1570 return ret;
1572 sort_keys = sort_order;
1573 if (sort_keys == NULL) {
1574 if (is_strict_order(field_order)) {
1576 * If user specified field order but no sort order,
1577 * we'll honor it and not add default sort orders.
1579 return 0;
1582 sort_keys = get_default_sort_order();
1585 str = strdup(sort_keys);
1586 if (str == NULL) {
1587 error("Not enough memory to setup sort keys");
1588 return -ENOMEM;
1591 for (tok = strtok_r(str, ", ", &tmp);
1592 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1593 ret = sort_dimension__add(tok);
1594 if (ret == -EINVAL) {
1595 error("Invalid --sort key: `%s'", tok);
1596 break;
1597 } else if (ret == -ESRCH) {
1598 error("Unknown --sort key: `%s'", tok);
1599 break;
1603 free(str);
1604 return ret;
1607 void perf_hpp__set_elide(int idx, bool elide)
1609 struct perf_hpp_fmt *fmt;
1610 struct hpp_sort_entry *hse;
1612 perf_hpp__for_each_format(fmt) {
1613 if (!perf_hpp__is_sort_entry(fmt))
1614 continue;
1616 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1617 if (hse->se->se_width_idx == idx) {
1618 fmt->elide = elide;
1619 break;
1624 static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)
1626 if (list && strlist__nr_entries(list) == 1) {
1627 if (fp != NULL)
1628 fprintf(fp, "# %s: %s\n", list_name,
1629 strlist__entry(list, 0)->s);
1630 return true;
1632 return false;
1635 static bool get_elide(int idx, FILE *output)
1637 switch (idx) {
1638 case HISTC_SYMBOL:
1639 return __get_elide(symbol_conf.sym_list, "symbol", output);
1640 case HISTC_DSO:
1641 return __get_elide(symbol_conf.dso_list, "dso", output);
1642 case HISTC_COMM:
1643 return __get_elide(symbol_conf.comm_list, "comm", output);
1644 default:
1645 break;
1648 if (sort__mode != SORT_MODE__BRANCH)
1649 return false;
1651 switch (idx) {
1652 case HISTC_SYMBOL_FROM:
1653 return __get_elide(symbol_conf.sym_from_list, "sym_from", output);
1654 case HISTC_SYMBOL_TO:
1655 return __get_elide(symbol_conf.sym_to_list, "sym_to", output);
1656 case HISTC_DSO_FROM:
1657 return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
1658 case HISTC_DSO_TO:
1659 return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
1660 default:
1661 break;
1664 return false;
1667 void sort__setup_elide(FILE *output)
1669 struct perf_hpp_fmt *fmt;
1670 struct hpp_sort_entry *hse;
1672 perf_hpp__for_each_format(fmt) {
1673 if (!perf_hpp__is_sort_entry(fmt))
1674 continue;
1676 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1677 fmt->elide = get_elide(hse->se->se_width_idx, output);
1681 * It makes no sense to elide all of sort entries.
1682 * Just revert them to show up again.
1684 perf_hpp__for_each_format(fmt) {
1685 if (!perf_hpp__is_sort_entry(fmt))
1686 continue;
1688 if (!fmt->elide)
1689 return;
1692 perf_hpp__for_each_format(fmt) {
1693 if (!perf_hpp__is_sort_entry(fmt))
1694 continue;
1696 fmt->elide = false;
1700 static int output_field_add(char *tok)
1702 unsigned int i;
1704 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1705 struct sort_dimension *sd = &common_sort_dimensions[i];
1707 if (strncasecmp(tok, sd->name, strlen(tok)))
1708 continue;
1710 return __sort_dimension__add_output(sd);
1713 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1714 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1716 if (strncasecmp(tok, hd->name, strlen(tok)))
1717 continue;
1719 return __hpp_dimension__add_output(hd);
1722 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1723 struct sort_dimension *sd = &bstack_sort_dimensions[i];
1725 if (strncasecmp(tok, sd->name, strlen(tok)))
1726 continue;
1728 return __sort_dimension__add_output(sd);
1731 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1732 struct sort_dimension *sd = &memory_sort_dimensions[i];
1734 if (strncasecmp(tok, sd->name, strlen(tok)))
1735 continue;
1737 return __sort_dimension__add_output(sd);
1740 return -ESRCH;
1743 static void reset_dimensions(void)
1745 unsigned int i;
1747 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++)
1748 common_sort_dimensions[i].taken = 0;
1750 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++)
1751 hpp_sort_dimensions[i].taken = 0;
1753 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++)
1754 bstack_sort_dimensions[i].taken = 0;
1756 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++)
1757 memory_sort_dimensions[i].taken = 0;
1760 bool is_strict_order(const char *order)
1762 return order && (*order != '+');
1765 static int __setup_output_field(void)
1767 char *tmp, *tok, *str, *strp;
1768 int ret = -EINVAL;
1770 if (field_order == NULL)
1771 return 0;
1773 reset_dimensions();
1775 strp = str = strdup(field_order);
1776 if (str == NULL) {
1777 error("Not enough memory to setup output fields");
1778 return -ENOMEM;
1781 if (!is_strict_order(field_order))
1782 strp++;
1784 if (!strlen(strp)) {
1785 error("Invalid --fields key: `+'");
1786 goto out;
1789 for (tok = strtok_r(strp, ", ", &tmp);
1790 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1791 ret = output_field_add(tok);
1792 if (ret == -EINVAL) {
1793 error("Invalid --fields key: `%s'", tok);
1794 break;
1795 } else if (ret == -ESRCH) {
1796 error("Unknown --fields key: `%s'", tok);
1797 break;
1801 out:
1802 free(str);
1803 return ret;
1806 int setup_sorting(void)
1808 int err;
1810 err = __setup_sorting();
1811 if (err < 0)
1812 return err;
1814 if (parent_pattern != default_parent_pattern) {
1815 err = sort_dimension__add("parent");
1816 if (err < 0)
1817 return err;
1820 reset_dimensions();
1823 * perf diff doesn't use default hpp output fields.
1825 if (sort__mode != SORT_MODE__DIFF)
1826 perf_hpp__init();
1828 err = __setup_output_field();
1829 if (err < 0)
1830 return err;
1832 /* copy sort keys to output fields */
1833 perf_hpp__setup_output_field();
1834 /* and then copy output fields to sort keys */
1835 perf_hpp__append_sort_keys();
1837 return 0;
1840 void reset_output_field(void)
1842 sort__need_collapse = 0;
1843 sort__has_parent = 0;
1844 sort__has_sym = 0;
1845 sort__has_dso = 0;
1847 field_order = NULL;
1848 sort_order = NULL;
1850 reset_dimensions();
1851 perf_hpp__reset_output_field();