8 const char default_parent_pattern
[] = "^sys_|^do_page_fault";
9 const char *parent_pattern
= default_parent_pattern
;
10 const char default_sort_order
[] = "comm,dso,symbol";
11 const char default_branch_sort_order
[] = "comm,dso_from,symbol_from,dso_to,symbol_to";
12 const char default_mem_sort_order
[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
13 const char default_top_sort_order
[] = "dso,symbol";
14 const char default_diff_sort_order
[] = "dso,symbol";
15 const char *sort_order
;
16 const char *field_order
;
17 regex_t ignore_callees_regex
;
18 int have_ignore_callees
= 0;
19 int sort__need_collapse
= 0;
20 int sort__has_parent
= 0;
21 int sort__has_sym
= 0;
22 int sort__has_dso
= 0;
23 enum sort_mode sort__mode
= SORT_MODE__NORMAL
;
26 static int repsep_snprintf(char *bf
, size_t size
, const char *fmt
, ...)
32 n
= vsnprintf(bf
, size
, fmt
, ap
);
33 if (symbol_conf
.field_sep
&& n
> 0) {
37 sep
= strchr(sep
, *symbol_conf
.field_sep
);
50 static int64_t cmp_null(const void *l
, const void *r
)
63 sort__thread_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
65 return right
->thread
->tid
- left
->thread
->tid
;
68 static int hist_entry__thread_snprintf(struct hist_entry
*he
, char *bf
,
69 size_t size
, unsigned int width
)
71 const char *comm
= thread__comm_str(he
->thread
);
72 return repsep_snprintf(bf
, size
, "%*s:%5d", width
- 6,
73 comm
?: "", he
->thread
->tid
);
76 struct sort_entry sort_thread
= {
77 .se_header
= "Command: Pid",
78 .se_cmp
= sort__thread_cmp
,
79 .se_snprintf
= hist_entry__thread_snprintf
,
80 .se_width_idx
= HISTC_THREAD
,
86 sort__comm_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
88 /* Compare the addr that should be unique among comm */
89 return comm__str(right
->comm
) - comm__str(left
->comm
);
93 sort__comm_collapse(struct hist_entry
*left
, struct hist_entry
*right
)
95 /* Compare the addr that should be unique among comm */
96 return comm__str(right
->comm
) - comm__str(left
->comm
);
100 sort__comm_sort(struct hist_entry
*left
, struct hist_entry
*right
)
102 return strcmp(comm__str(right
->comm
), comm__str(left
->comm
));
105 static int hist_entry__comm_snprintf(struct hist_entry
*he
, char *bf
,
106 size_t size
, unsigned int width
)
108 return repsep_snprintf(bf
, size
, "%*s", width
, comm__str(he
->comm
));
111 struct sort_entry sort_comm
= {
112 .se_header
= "Command",
113 .se_cmp
= sort__comm_cmp
,
114 .se_collapse
= sort__comm_collapse
,
115 .se_sort
= sort__comm_sort
,
116 .se_snprintf
= hist_entry__comm_snprintf
,
117 .se_width_idx
= HISTC_COMM
,
122 static int64_t _sort__dso_cmp(struct map
*map_l
, struct map
*map_r
)
124 struct dso
*dso_l
= map_l
? map_l
->dso
: NULL
;
125 struct dso
*dso_r
= map_r
? map_r
->dso
: NULL
;
126 const char *dso_name_l
, *dso_name_r
;
128 if (!dso_l
|| !dso_r
)
129 return cmp_null(dso_r
, dso_l
);
132 dso_name_l
= dso_l
->long_name
;
133 dso_name_r
= dso_r
->long_name
;
135 dso_name_l
= dso_l
->short_name
;
136 dso_name_r
= dso_r
->short_name
;
139 return strcmp(dso_name_l
, dso_name_r
);
143 sort__dso_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
145 return _sort__dso_cmp(right
->ms
.map
, left
->ms
.map
);
148 static int _hist_entry__dso_snprintf(struct map
*map
, char *bf
,
149 size_t size
, unsigned int width
)
151 if (map
&& map
->dso
) {
152 const char *dso_name
= !verbose
? map
->dso
->short_name
:
154 return repsep_snprintf(bf
, size
, "%-*s", width
, dso_name
);
157 return repsep_snprintf(bf
, size
, "%-*s", width
, "[unknown]");
160 static int hist_entry__dso_snprintf(struct hist_entry
*he
, char *bf
,
161 size_t size
, unsigned int width
)
163 return _hist_entry__dso_snprintf(he
->ms
.map
, bf
, size
, width
);
166 struct sort_entry sort_dso
= {
167 .se_header
= "Shared Object",
168 .se_cmp
= sort__dso_cmp
,
169 .se_snprintf
= hist_entry__dso_snprintf
,
170 .se_width_idx
= HISTC_DSO
,
175 static int64_t _sort__addr_cmp(u64 left_ip
, u64 right_ip
)
177 return (int64_t)(right_ip
- left_ip
);
180 static int64_t _sort__sym_cmp(struct symbol
*sym_l
, struct symbol
*sym_r
)
184 if (!sym_l
|| !sym_r
)
185 return cmp_null(sym_l
, sym_r
);
193 return (int64_t)(ip_r
- ip_l
);
197 sort__sym_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
201 if (!left
->ms
.sym
&& !right
->ms
.sym
)
202 return _sort__addr_cmp(left
->ip
, right
->ip
);
205 * comparing symbol address alone is not enough since it's a
206 * relative address within a dso.
208 if (!sort__has_dso
) {
209 ret
= sort__dso_cmp(left
, right
);
214 return _sort__sym_cmp(left
->ms
.sym
, right
->ms
.sym
);
218 sort__sym_sort(struct hist_entry
*left
, struct hist_entry
*right
)
220 if (!left
->ms
.sym
|| !right
->ms
.sym
)
221 return cmp_null(left
->ms
.sym
, right
->ms
.sym
);
223 return strcmp(right
->ms
.sym
->name
, left
->ms
.sym
->name
);
226 static int _hist_entry__sym_snprintf(struct map
*map
, struct symbol
*sym
,
227 u64 ip
, char level
, char *bf
, size_t size
,
233 char o
= map
? dso__symtab_origin(map
->dso
) : '!';
234 ret
+= repsep_snprintf(bf
, size
, "%-#*llx %c ",
235 BITS_PER_LONG
/ 4 + 2, ip
, o
);
238 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "[%c] ", level
);
240 if (map
->type
== MAP__VARIABLE
) {
241 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%s", sym
->name
);
242 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "+0x%llx",
243 ip
- map
->unmap_ip(map
, sym
->start
));
244 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-*s",
247 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-*s",
252 size_t len
= BITS_PER_LONG
/ 4;
253 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-#.*llx",
255 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-*s",
262 static int hist_entry__sym_snprintf(struct hist_entry
*he
, char *bf
,
263 size_t size
, unsigned int width
)
265 return _hist_entry__sym_snprintf(he
->ms
.map
, he
->ms
.sym
, he
->ip
,
266 he
->level
, bf
, size
, width
);
269 struct sort_entry sort_sym
= {
270 .se_header
= "Symbol",
271 .se_cmp
= sort__sym_cmp
,
272 .se_sort
= sort__sym_sort
,
273 .se_snprintf
= hist_entry__sym_snprintf
,
274 .se_width_idx
= HISTC_SYMBOL
,
280 sort__srcline_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
282 if (!left
->srcline
) {
284 left
->srcline
= SRCLINE_UNKNOWN
;
286 struct map
*map
= left
->ms
.map
;
287 left
->srcline
= get_srcline(map
->dso
,
288 map__rip_2objdump(map
, left
->ip
));
291 if (!right
->srcline
) {
293 right
->srcline
= SRCLINE_UNKNOWN
;
295 struct map
*map
= right
->ms
.map
;
296 right
->srcline
= get_srcline(map
->dso
,
297 map__rip_2objdump(map
, right
->ip
));
300 return strcmp(right
->srcline
, left
->srcline
);
303 static int hist_entry__srcline_snprintf(struct hist_entry
*he
, char *bf
,
305 unsigned int width __maybe_unused
)
307 return repsep_snprintf(bf
, size
, "%s", he
->srcline
);
310 struct sort_entry sort_srcline
= {
311 .se_header
= "Source:Line",
312 .se_cmp
= sort__srcline_cmp
,
313 .se_snprintf
= hist_entry__srcline_snprintf
,
314 .se_width_idx
= HISTC_SRCLINE
,
320 sort__parent_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
322 struct symbol
*sym_l
= left
->parent
;
323 struct symbol
*sym_r
= right
->parent
;
325 if (!sym_l
|| !sym_r
)
326 return cmp_null(sym_l
, sym_r
);
328 return strcmp(sym_r
->name
, sym_l
->name
);
331 static int hist_entry__parent_snprintf(struct hist_entry
*he
, char *bf
,
332 size_t size
, unsigned int width
)
334 return repsep_snprintf(bf
, size
, "%-*s", width
,
335 he
->parent
? he
->parent
->name
: "[other]");
338 struct sort_entry sort_parent
= {
339 .se_header
= "Parent symbol",
340 .se_cmp
= sort__parent_cmp
,
341 .se_snprintf
= hist_entry__parent_snprintf
,
342 .se_width_idx
= HISTC_PARENT
,
348 sort__cpu_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
350 return right
->cpu
- left
->cpu
;
353 static int hist_entry__cpu_snprintf(struct hist_entry
*he
, char *bf
,
354 size_t size
, unsigned int width
)
356 return repsep_snprintf(bf
, size
, "%*d", width
, he
->cpu
);
359 struct sort_entry sort_cpu
= {
361 .se_cmp
= sort__cpu_cmp
,
362 .se_snprintf
= hist_entry__cpu_snprintf
,
363 .se_width_idx
= HISTC_CPU
,
366 /* sort keys for branch stacks */
369 sort__dso_from_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
371 return _sort__dso_cmp(left
->branch_info
->from
.map
,
372 right
->branch_info
->from
.map
);
375 static int hist_entry__dso_from_snprintf(struct hist_entry
*he
, char *bf
,
376 size_t size
, unsigned int width
)
378 return _hist_entry__dso_snprintf(he
->branch_info
->from
.map
,
383 sort__dso_to_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
385 return _sort__dso_cmp(left
->branch_info
->to
.map
,
386 right
->branch_info
->to
.map
);
389 static int hist_entry__dso_to_snprintf(struct hist_entry
*he
, char *bf
,
390 size_t size
, unsigned int width
)
392 return _hist_entry__dso_snprintf(he
->branch_info
->to
.map
,
397 sort__sym_from_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
399 struct addr_map_symbol
*from_l
= &left
->branch_info
->from
;
400 struct addr_map_symbol
*from_r
= &right
->branch_info
->from
;
402 if (!from_l
->sym
&& !from_r
->sym
)
403 return _sort__addr_cmp(from_l
->addr
, from_r
->addr
);
405 return _sort__sym_cmp(from_l
->sym
, from_r
->sym
);
409 sort__sym_to_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
411 struct addr_map_symbol
*to_l
= &left
->branch_info
->to
;
412 struct addr_map_symbol
*to_r
= &right
->branch_info
->to
;
414 if (!to_l
->sym
&& !to_r
->sym
)
415 return _sort__addr_cmp(to_l
->addr
, to_r
->addr
);
417 return _sort__sym_cmp(to_l
->sym
, to_r
->sym
);
420 static int hist_entry__sym_from_snprintf(struct hist_entry
*he
, char *bf
,
421 size_t size
, unsigned int width
)
423 struct addr_map_symbol
*from
= &he
->branch_info
->from
;
424 return _hist_entry__sym_snprintf(from
->map
, from
->sym
, from
->addr
,
425 he
->level
, bf
, size
, width
);
429 static int hist_entry__sym_to_snprintf(struct hist_entry
*he
, char *bf
,
430 size_t size
, unsigned int width
)
432 struct addr_map_symbol
*to
= &he
->branch_info
->to
;
433 return _hist_entry__sym_snprintf(to
->map
, to
->sym
, to
->addr
,
434 he
->level
, bf
, size
, width
);
438 struct sort_entry sort_dso_from
= {
439 .se_header
= "Source Shared Object",
440 .se_cmp
= sort__dso_from_cmp
,
441 .se_snprintf
= hist_entry__dso_from_snprintf
,
442 .se_width_idx
= HISTC_DSO_FROM
,
445 struct sort_entry sort_dso_to
= {
446 .se_header
= "Target Shared Object",
447 .se_cmp
= sort__dso_to_cmp
,
448 .se_snprintf
= hist_entry__dso_to_snprintf
,
449 .se_width_idx
= HISTC_DSO_TO
,
452 struct sort_entry sort_sym_from
= {
453 .se_header
= "Source Symbol",
454 .se_cmp
= sort__sym_from_cmp
,
455 .se_snprintf
= hist_entry__sym_from_snprintf
,
456 .se_width_idx
= HISTC_SYMBOL_FROM
,
459 struct sort_entry sort_sym_to
= {
460 .se_header
= "Target Symbol",
461 .se_cmp
= sort__sym_to_cmp
,
462 .se_snprintf
= hist_entry__sym_to_snprintf
,
463 .se_width_idx
= HISTC_SYMBOL_TO
,
467 sort__mispredict_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
469 const unsigned char mp
= left
->branch_info
->flags
.mispred
!=
470 right
->branch_info
->flags
.mispred
;
471 const unsigned char p
= left
->branch_info
->flags
.predicted
!=
472 right
->branch_info
->flags
.predicted
;
477 static int hist_entry__mispredict_snprintf(struct hist_entry
*he
, char *bf
,
478 size_t size
, unsigned int width
){
479 static const char *out
= "N/A";
481 if (he
->branch_info
->flags
.predicted
)
483 else if (he
->branch_info
->flags
.mispred
)
486 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
489 /* --sort daddr_sym */
491 sort__daddr_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
493 uint64_t l
= 0, r
= 0;
496 l
= left
->mem_info
->daddr
.addr
;
498 r
= right
->mem_info
->daddr
.addr
;
500 return (int64_t)(r
- l
);
503 static int hist_entry__daddr_snprintf(struct hist_entry
*he
, char *bf
,
504 size_t size
, unsigned int width
)
507 struct map
*map
= NULL
;
508 struct symbol
*sym
= NULL
;
511 addr
= he
->mem_info
->daddr
.addr
;
512 map
= he
->mem_info
->daddr
.map
;
513 sym
= he
->mem_info
->daddr
.sym
;
515 return _hist_entry__sym_snprintf(map
, sym
, addr
, he
->level
, bf
, size
,
520 sort__dso_daddr_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
522 struct map
*map_l
= NULL
;
523 struct map
*map_r
= NULL
;
526 map_l
= left
->mem_info
->daddr
.map
;
528 map_r
= right
->mem_info
->daddr
.map
;
530 return _sort__dso_cmp(map_l
, map_r
);
533 static int hist_entry__dso_daddr_snprintf(struct hist_entry
*he
, char *bf
,
534 size_t size
, unsigned int width
)
536 struct map
*map
= NULL
;
539 map
= he
->mem_info
->daddr
.map
;
541 return _hist_entry__dso_snprintf(map
, bf
, size
, width
);
545 sort__locked_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
547 union perf_mem_data_src data_src_l
;
548 union perf_mem_data_src data_src_r
;
551 data_src_l
= left
->mem_info
->data_src
;
553 data_src_l
.mem_lock
= PERF_MEM_LOCK_NA
;
556 data_src_r
= right
->mem_info
->data_src
;
558 data_src_r
.mem_lock
= PERF_MEM_LOCK_NA
;
560 return (int64_t)(data_src_r
.mem_lock
- data_src_l
.mem_lock
);
563 static int hist_entry__locked_snprintf(struct hist_entry
*he
, char *bf
,
564 size_t size
, unsigned int width
)
567 u64 mask
= PERF_MEM_LOCK_NA
;
570 mask
= he
->mem_info
->data_src
.mem_lock
;
572 if (mask
& PERF_MEM_LOCK_NA
)
574 else if (mask
& PERF_MEM_LOCK_LOCKED
)
579 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
583 sort__tlb_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
585 union perf_mem_data_src data_src_l
;
586 union perf_mem_data_src data_src_r
;
589 data_src_l
= left
->mem_info
->data_src
;
591 data_src_l
.mem_dtlb
= PERF_MEM_TLB_NA
;
594 data_src_r
= right
->mem_info
->data_src
;
596 data_src_r
.mem_dtlb
= PERF_MEM_TLB_NA
;
598 return (int64_t)(data_src_r
.mem_dtlb
- data_src_l
.mem_dtlb
);
601 static const char * const tlb_access
[] = {
610 #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
612 static int hist_entry__tlb_snprintf(struct hist_entry
*he
, char *bf
,
613 size_t size
, unsigned int width
)
616 size_t sz
= sizeof(out
) - 1; /* -1 for null termination */
618 u64 m
= PERF_MEM_TLB_NA
;
624 m
= he
->mem_info
->data_src
.mem_dtlb
;
626 hit
= m
& PERF_MEM_TLB_HIT
;
627 miss
= m
& PERF_MEM_TLB_MISS
;
629 /* already taken care of */
630 m
&= ~(PERF_MEM_TLB_HIT
|PERF_MEM_TLB_MISS
);
632 for (i
= 0; m
&& i
< NUM_TLB_ACCESS
; i
++, m
>>= 1) {
639 strncat(out
, tlb_access
[i
], sz
- l
);
640 l
+= strlen(tlb_access
[i
]);
645 strncat(out
, " hit", sz
- l
);
647 strncat(out
, " miss", sz
- l
);
649 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
653 sort__lvl_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
655 union perf_mem_data_src data_src_l
;
656 union perf_mem_data_src data_src_r
;
659 data_src_l
= left
->mem_info
->data_src
;
661 data_src_l
.mem_lvl
= PERF_MEM_LVL_NA
;
664 data_src_r
= right
->mem_info
->data_src
;
666 data_src_r
.mem_lvl
= PERF_MEM_LVL_NA
;
668 return (int64_t)(data_src_r
.mem_lvl
- data_src_l
.mem_lvl
);
671 static const char * const mem_lvl
[] = {
680 "Remote RAM (1 hop)",
681 "Remote RAM (2 hops)",
682 "Remote Cache (1 hop)",
683 "Remote Cache (2 hops)",
687 #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
689 static int hist_entry__lvl_snprintf(struct hist_entry
*he
, char *bf
,
690 size_t size
, unsigned int width
)
693 size_t sz
= sizeof(out
) - 1; /* -1 for null termination */
695 u64 m
= PERF_MEM_LVL_NA
;
699 m
= he
->mem_info
->data_src
.mem_lvl
;
703 hit
= m
& PERF_MEM_LVL_HIT
;
704 miss
= m
& PERF_MEM_LVL_MISS
;
706 /* already taken care of */
707 m
&= ~(PERF_MEM_LVL_HIT
|PERF_MEM_LVL_MISS
);
709 for (i
= 0; m
&& i
< NUM_MEM_LVL
; i
++, m
>>= 1) {
716 strncat(out
, mem_lvl
[i
], sz
- l
);
717 l
+= strlen(mem_lvl
[i
]);
722 strncat(out
, " hit", sz
- l
);
724 strncat(out
, " miss", sz
- l
);
726 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
730 sort__snoop_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
732 union perf_mem_data_src data_src_l
;
733 union perf_mem_data_src data_src_r
;
736 data_src_l
= left
->mem_info
->data_src
;
738 data_src_l
.mem_snoop
= PERF_MEM_SNOOP_NA
;
741 data_src_r
= right
->mem_info
->data_src
;
743 data_src_r
.mem_snoop
= PERF_MEM_SNOOP_NA
;
745 return (int64_t)(data_src_r
.mem_snoop
- data_src_l
.mem_snoop
);
748 static const char * const snoop_access
[] = {
755 #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
757 static int hist_entry__snoop_snprintf(struct hist_entry
*he
, char *bf
,
758 size_t size
, unsigned int width
)
761 size_t sz
= sizeof(out
) - 1; /* -1 for null termination */
763 u64 m
= PERF_MEM_SNOOP_NA
;
768 m
= he
->mem_info
->data_src
.mem_snoop
;
770 for (i
= 0; m
&& i
< NUM_SNOOP_ACCESS
; i
++, m
>>= 1) {
777 strncat(out
, snoop_access
[i
], sz
- l
);
778 l
+= strlen(snoop_access
[i
]);
784 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
787 struct sort_entry sort_mispredict
= {
788 .se_header
= "Branch Mispredicted",
789 .se_cmp
= sort__mispredict_cmp
,
790 .se_snprintf
= hist_entry__mispredict_snprintf
,
791 .se_width_idx
= HISTC_MISPREDICT
,
794 static u64
he_weight(struct hist_entry
*he
)
796 return he
->stat
.nr_events
? he
->stat
.weight
/ he
->stat
.nr_events
: 0;
800 sort__local_weight_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
802 return he_weight(left
) - he_weight(right
);
805 static int hist_entry__local_weight_snprintf(struct hist_entry
*he
, char *bf
,
806 size_t size
, unsigned int width
)
808 return repsep_snprintf(bf
, size
, "%-*llu", width
, he_weight(he
));
811 struct sort_entry sort_local_weight
= {
812 .se_header
= "Local Weight",
813 .se_cmp
= sort__local_weight_cmp
,
814 .se_snprintf
= hist_entry__local_weight_snprintf
,
815 .se_width_idx
= HISTC_LOCAL_WEIGHT
,
819 sort__global_weight_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
821 return left
->stat
.weight
- right
->stat
.weight
;
824 static int hist_entry__global_weight_snprintf(struct hist_entry
*he
, char *bf
,
825 size_t size
, unsigned int width
)
827 return repsep_snprintf(bf
, size
, "%-*llu", width
, he
->stat
.weight
);
830 struct sort_entry sort_global_weight
= {
831 .se_header
= "Weight",
832 .se_cmp
= sort__global_weight_cmp
,
833 .se_snprintf
= hist_entry__global_weight_snprintf
,
834 .se_width_idx
= HISTC_GLOBAL_WEIGHT
,
837 struct sort_entry sort_mem_daddr_sym
= {
838 .se_header
= "Data Symbol",
839 .se_cmp
= sort__daddr_cmp
,
840 .se_snprintf
= hist_entry__daddr_snprintf
,
841 .se_width_idx
= HISTC_MEM_DADDR_SYMBOL
,
844 struct sort_entry sort_mem_daddr_dso
= {
845 .se_header
= "Data Object",
846 .se_cmp
= sort__dso_daddr_cmp
,
847 .se_snprintf
= hist_entry__dso_daddr_snprintf
,
848 .se_width_idx
= HISTC_MEM_DADDR_SYMBOL
,
851 struct sort_entry sort_mem_locked
= {
852 .se_header
= "Locked",
853 .se_cmp
= sort__locked_cmp
,
854 .se_snprintf
= hist_entry__locked_snprintf
,
855 .se_width_idx
= HISTC_MEM_LOCKED
,
858 struct sort_entry sort_mem_tlb
= {
859 .se_header
= "TLB access",
860 .se_cmp
= sort__tlb_cmp
,
861 .se_snprintf
= hist_entry__tlb_snprintf
,
862 .se_width_idx
= HISTC_MEM_TLB
,
865 struct sort_entry sort_mem_lvl
= {
866 .se_header
= "Memory access",
867 .se_cmp
= sort__lvl_cmp
,
868 .se_snprintf
= hist_entry__lvl_snprintf
,
869 .se_width_idx
= HISTC_MEM_LVL
,
872 struct sort_entry sort_mem_snoop
= {
873 .se_header
= "Snoop",
874 .se_cmp
= sort__snoop_cmp
,
875 .se_snprintf
= hist_entry__snoop_snprintf
,
876 .se_width_idx
= HISTC_MEM_SNOOP
,
880 sort__abort_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
882 return left
->branch_info
->flags
.abort
!=
883 right
->branch_info
->flags
.abort
;
886 static int hist_entry__abort_snprintf(struct hist_entry
*he
, char *bf
,
887 size_t size
, unsigned int width
)
889 static const char *out
= ".";
891 if (he
->branch_info
->flags
.abort
)
893 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
896 struct sort_entry sort_abort
= {
897 .se_header
= "Transaction abort",
898 .se_cmp
= sort__abort_cmp
,
899 .se_snprintf
= hist_entry__abort_snprintf
,
900 .se_width_idx
= HISTC_ABORT
,
904 sort__in_tx_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
906 return left
->branch_info
->flags
.in_tx
!=
907 right
->branch_info
->flags
.in_tx
;
910 static int hist_entry__in_tx_snprintf(struct hist_entry
*he
, char *bf
,
911 size_t size
, unsigned int width
)
913 static const char *out
= ".";
915 if (he
->branch_info
->flags
.in_tx
)
918 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
921 struct sort_entry sort_in_tx
= {
922 .se_header
= "Branch in transaction",
923 .se_cmp
= sort__in_tx_cmp
,
924 .se_snprintf
= hist_entry__in_tx_snprintf
,
925 .se_width_idx
= HISTC_IN_TX
,
929 sort__transaction_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
931 return left
->transaction
- right
->transaction
;
934 static inline char *add_str(char *p
, const char *str
)
937 return p
+ strlen(str
);
940 static struct txbit
{
945 { PERF_TXN_ELISION
, "EL ", 0 },
946 { PERF_TXN_TRANSACTION
, "TX ", 1 },
947 { PERF_TXN_SYNC
, "SYNC ", 1 },
948 { PERF_TXN_ASYNC
, "ASYNC ", 0 },
949 { PERF_TXN_RETRY
, "RETRY ", 0 },
950 { PERF_TXN_CONFLICT
, "CON ", 0 },
951 { PERF_TXN_CAPACITY_WRITE
, "CAP-WRITE ", 1 },
952 { PERF_TXN_CAPACITY_READ
, "CAP-READ ", 0 },
956 int hist_entry__transaction_len(void)
961 for (i
= 0; txbits
[i
].name
; i
++) {
962 if (!txbits
[i
].skip_for_len
)
963 len
+= strlen(txbits
[i
].name
);
965 len
+= 4; /* :XX<space> */
969 static int hist_entry__transaction_snprintf(struct hist_entry
*he
, char *bf
,
970 size_t size
, unsigned int width
)
972 u64 t
= he
->transaction
;
978 for (i
= 0; txbits
[i
].name
; i
++)
979 if (txbits
[i
].flag
& t
)
980 p
= add_str(p
, txbits
[i
].name
);
981 if (t
&& !(t
& (PERF_TXN_SYNC
|PERF_TXN_ASYNC
)))
982 p
= add_str(p
, "NEITHER ");
983 if (t
& PERF_TXN_ABORT_MASK
) {
984 sprintf(p
, ":%" PRIx64
,
985 (t
& PERF_TXN_ABORT_MASK
) >>
986 PERF_TXN_ABORT_SHIFT
);
990 return repsep_snprintf(bf
, size
, "%-*s", width
, buf
);
993 struct sort_entry sort_transaction
= {
994 .se_header
= "Transaction ",
995 .se_cmp
= sort__transaction_cmp
,
996 .se_snprintf
= hist_entry__transaction_snprintf
,
997 .se_width_idx
= HISTC_TRANSACTION
,
1000 struct sort_dimension
{
1002 struct sort_entry
*entry
;
1006 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
1008 static struct sort_dimension common_sort_dimensions
[] = {
1009 DIM(SORT_PID
, "pid", sort_thread
),
1010 DIM(SORT_COMM
, "comm", sort_comm
),
1011 DIM(SORT_DSO
, "dso", sort_dso
),
1012 DIM(SORT_SYM
, "symbol", sort_sym
),
1013 DIM(SORT_PARENT
, "parent", sort_parent
),
1014 DIM(SORT_CPU
, "cpu", sort_cpu
),
1015 DIM(SORT_SRCLINE
, "srcline", sort_srcline
),
1016 DIM(SORT_LOCAL_WEIGHT
, "local_weight", sort_local_weight
),
1017 DIM(SORT_GLOBAL_WEIGHT
, "weight", sort_global_weight
),
1018 DIM(SORT_TRANSACTION
, "transaction", sort_transaction
),
1023 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
1025 static struct sort_dimension bstack_sort_dimensions
[] = {
1026 DIM(SORT_DSO_FROM
, "dso_from", sort_dso_from
),
1027 DIM(SORT_DSO_TO
, "dso_to", sort_dso_to
),
1028 DIM(SORT_SYM_FROM
, "symbol_from", sort_sym_from
),
1029 DIM(SORT_SYM_TO
, "symbol_to", sort_sym_to
),
1030 DIM(SORT_MISPREDICT
, "mispredict", sort_mispredict
),
1031 DIM(SORT_IN_TX
, "in_tx", sort_in_tx
),
1032 DIM(SORT_ABORT
, "abort", sort_abort
),
1037 #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
1039 static struct sort_dimension memory_sort_dimensions
[] = {
1040 DIM(SORT_MEM_DADDR_SYMBOL
, "symbol_daddr", sort_mem_daddr_sym
),
1041 DIM(SORT_MEM_DADDR_DSO
, "dso_daddr", sort_mem_daddr_dso
),
1042 DIM(SORT_MEM_LOCKED
, "locked", sort_mem_locked
),
1043 DIM(SORT_MEM_TLB
, "tlb", sort_mem_tlb
),
1044 DIM(SORT_MEM_LVL
, "mem", sort_mem_lvl
),
1045 DIM(SORT_MEM_SNOOP
, "snoop", sort_mem_snoop
),
1050 struct hpp_dimension
{
1052 struct perf_hpp_fmt
*fmt
;
1056 #define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
1058 static struct hpp_dimension hpp_sort_dimensions
[] = {
1059 DIM(PERF_HPP__OVERHEAD
, "overhead"),
1060 DIM(PERF_HPP__OVERHEAD_SYS
, "overhead_sys"),
1061 DIM(PERF_HPP__OVERHEAD_US
, "overhead_us"),
1062 DIM(PERF_HPP__OVERHEAD_GUEST_SYS
, "overhead_guest_sys"),
1063 DIM(PERF_HPP__OVERHEAD_GUEST_US
, "overhead_guest_us"),
1064 DIM(PERF_HPP__SAMPLES
, "sample"),
1065 DIM(PERF_HPP__PERIOD
, "period"),
1070 struct hpp_sort_entry
{
1071 struct perf_hpp_fmt hpp
;
1072 struct sort_entry
*se
;
1075 bool perf_hpp__same_sort_entry(struct perf_hpp_fmt
*a
, struct perf_hpp_fmt
*b
)
1077 struct hpp_sort_entry
*hse_a
;
1078 struct hpp_sort_entry
*hse_b
;
1080 if (!perf_hpp__is_sort_entry(a
) || !perf_hpp__is_sort_entry(b
))
1083 hse_a
= container_of(a
, struct hpp_sort_entry
, hpp
);
1084 hse_b
= container_of(b
, struct hpp_sort_entry
, hpp
);
1086 return hse_a
->se
== hse_b
->se
;
1089 void perf_hpp__reset_width(struct perf_hpp_fmt
*fmt
, struct hists
*hists
)
1091 struct hpp_sort_entry
*hse
;
1093 if (!perf_hpp__is_sort_entry(fmt
))
1096 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1097 hists__new_col_len(hists
, hse
->se
->se_width_idx
,
1098 strlen(hse
->se
->se_header
));
1101 static int __sort__hpp_header(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
1102 struct perf_evsel
*evsel
)
1104 struct hpp_sort_entry
*hse
;
1107 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1108 len
= hists__col_len(&evsel
->hists
, hse
->se
->se_width_idx
);
1110 return scnprintf(hpp
->buf
, hpp
->size
, "%*s", len
, hse
->se
->se_header
);
1113 static int __sort__hpp_width(struct perf_hpp_fmt
*fmt
,
1114 struct perf_hpp
*hpp __maybe_unused
,
1115 struct perf_evsel
*evsel
)
1117 struct hpp_sort_entry
*hse
;
1119 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1121 return hists__col_len(&evsel
->hists
, hse
->se
->se_width_idx
);
1124 static int __sort__hpp_entry(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
1125 struct hist_entry
*he
)
1127 struct hpp_sort_entry
*hse
;
1130 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1131 len
= hists__col_len(he
->hists
, hse
->se
->se_width_idx
);
1133 return hse
->se
->se_snprintf(he
, hpp
->buf
, hpp
->size
, len
);
1136 static struct hpp_sort_entry
*
1137 __sort_dimension__alloc_hpp(struct sort_dimension
*sd
)
1139 struct hpp_sort_entry
*hse
;
1141 hse
= malloc(sizeof(*hse
));
1143 pr_err("Memory allocation failed\n");
1147 hse
->se
= sd
->entry
;
1148 hse
->hpp
.header
= __sort__hpp_header
;
1149 hse
->hpp
.width
= __sort__hpp_width
;
1150 hse
->hpp
.entry
= __sort__hpp_entry
;
1151 hse
->hpp
.color
= NULL
;
1153 hse
->hpp
.cmp
= sd
->entry
->se_cmp
;
1154 hse
->hpp
.collapse
= sd
->entry
->se_collapse
? : sd
->entry
->se_cmp
;
1155 hse
->hpp
.sort
= sd
->entry
->se_sort
? : hse
->hpp
.collapse
;
1157 INIT_LIST_HEAD(&hse
->hpp
.list
);
1158 INIT_LIST_HEAD(&hse
->hpp
.sort_list
);
1163 bool perf_hpp__is_sort_entry(struct perf_hpp_fmt
*format
)
1165 return format
->header
== __sort__hpp_header
;
1168 static int __sort_dimension__add_hpp_sort(struct sort_dimension
*sd
)
1170 struct hpp_sort_entry
*hse
= __sort_dimension__alloc_hpp(sd
);
1175 perf_hpp__register_sort_field(&hse
->hpp
);
1179 static int __sort_dimension__add_hpp_output(struct sort_dimension
*sd
)
1181 struct hpp_sort_entry
*hse
= __sort_dimension__alloc_hpp(sd
);
1186 perf_hpp__column_register(&hse
->hpp
);
1190 static int __sort_dimension__add(struct sort_dimension
*sd
)
1195 if (__sort_dimension__add_hpp_sort(sd
) < 0)
1198 if (sd
->entry
->se_collapse
)
1199 sort__need_collapse
= 1;
1206 static int __hpp_dimension__add(struct hpp_dimension
*hd
)
1211 perf_hpp__register_sort_field(hd
->fmt
);
1216 static int __sort_dimension__add_output(struct sort_dimension
*sd
)
1221 if (__sort_dimension__add_hpp_output(sd
) < 0)
1228 static int __hpp_dimension__add_output(struct hpp_dimension
*hd
)
1233 perf_hpp__column_register(hd
->fmt
);
1238 int sort_dimension__add(const char *tok
)
1242 for (i
= 0; i
< ARRAY_SIZE(common_sort_dimensions
); i
++) {
1243 struct sort_dimension
*sd
= &common_sort_dimensions
[i
];
1245 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
1248 if (sd
->entry
== &sort_parent
) {
1249 int ret
= regcomp(&parent_regex
, parent_pattern
, REG_EXTENDED
);
1253 regerror(ret
, &parent_regex
, err
, sizeof(err
));
1254 pr_err("Invalid regex: %s\n%s", parent_pattern
, err
);
1257 sort__has_parent
= 1;
1258 } else if (sd
->entry
== &sort_sym
) {
1260 } else if (sd
->entry
== &sort_dso
) {
1264 return __sort_dimension__add(sd
);
1267 for (i
= 0; i
< ARRAY_SIZE(hpp_sort_dimensions
); i
++) {
1268 struct hpp_dimension
*hd
= &hpp_sort_dimensions
[i
];
1270 if (strncasecmp(tok
, hd
->name
, strlen(tok
)))
1273 return __hpp_dimension__add(hd
);
1276 for (i
= 0; i
< ARRAY_SIZE(bstack_sort_dimensions
); i
++) {
1277 struct sort_dimension
*sd
= &bstack_sort_dimensions
[i
];
1279 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
1282 if (sort__mode
!= SORT_MODE__BRANCH
)
1285 if (sd
->entry
== &sort_sym_from
|| sd
->entry
== &sort_sym_to
)
1288 __sort_dimension__add(sd
);
1292 for (i
= 0; i
< ARRAY_SIZE(memory_sort_dimensions
); i
++) {
1293 struct sort_dimension
*sd
= &memory_sort_dimensions
[i
];
1295 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
1298 if (sort__mode
!= SORT_MODE__MEMORY
)
1301 if (sd
->entry
== &sort_mem_daddr_sym
)
1304 __sort_dimension__add(sd
);
1311 static const char *get_default_sort_order(void)
1313 const char *default_sort_orders
[] = {
1315 default_branch_sort_order
,
1316 default_mem_sort_order
,
1317 default_top_sort_order
,
1318 default_diff_sort_order
,
1321 BUG_ON(sort__mode
>= ARRAY_SIZE(default_sort_orders
));
1323 return default_sort_orders
[sort__mode
];
1326 static int __setup_sorting(void)
1328 char *tmp
, *tok
, *str
;
1329 const char *sort_keys
= sort_order
;
1332 if (sort_keys
== NULL
) {
1335 * If user specified field order but no sort order,
1336 * we'll honor it and not add default sort orders.
1341 sort_keys
= get_default_sort_order();
1344 str
= strdup(sort_keys
);
1346 error("Not enough memory to setup sort keys");
1350 for (tok
= strtok_r(str
, ", ", &tmp
);
1351 tok
; tok
= strtok_r(NULL
, ", ", &tmp
)) {
1352 ret
= sort_dimension__add(tok
);
1353 if (ret
== -EINVAL
) {
1354 error("Invalid --sort key: `%s'", tok
);
1356 } else if (ret
== -ESRCH
) {
1357 error("Unknown --sort key: `%s'", tok
);
1366 bool perf_hpp__should_skip(struct perf_hpp_fmt
*format
)
1368 if (perf_hpp__is_sort_entry(format
)) {
1369 struct hpp_sort_entry
*hse
;
1371 hse
= container_of(format
, struct hpp_sort_entry
, hpp
);
1372 return hse
->se
->elide
;
1377 static void sort_entry__setup_elide(struct sort_entry
*se
,
1378 struct strlist
*list
,
1379 const char *list_name
, FILE *fp
)
1381 if (list
&& strlist__nr_entries(list
) == 1) {
1383 fprintf(fp
, "# %s: %s\n", list_name
,
1384 strlist__entry(list
, 0)->s
);
1389 void sort__setup_elide(FILE *output
)
1391 struct perf_hpp_fmt
*fmt
;
1392 struct hpp_sort_entry
*hse
;
1394 sort_entry__setup_elide(&sort_dso
, symbol_conf
.dso_list
,
1396 sort_entry__setup_elide(&sort_comm
, symbol_conf
.comm_list
,
1398 sort_entry__setup_elide(&sort_sym
, symbol_conf
.sym_list
,
1401 if (sort__mode
== SORT_MODE__BRANCH
) {
1402 sort_entry__setup_elide(&sort_dso_from
,
1403 symbol_conf
.dso_from_list
,
1404 "dso_from", output
);
1405 sort_entry__setup_elide(&sort_dso_to
,
1406 symbol_conf
.dso_to_list
,
1408 sort_entry__setup_elide(&sort_sym_from
,
1409 symbol_conf
.sym_from_list
,
1410 "sym_from", output
);
1411 sort_entry__setup_elide(&sort_sym_to
,
1412 symbol_conf
.sym_to_list
,
1414 } else if (sort__mode
== SORT_MODE__MEMORY
) {
1415 sort_entry__setup_elide(&sort_dso
, symbol_conf
.dso_list
,
1416 "symbol_daddr", output
);
1417 sort_entry__setup_elide(&sort_dso
, symbol_conf
.dso_list
,
1418 "dso_daddr", output
);
1419 sort_entry__setup_elide(&sort_dso
, symbol_conf
.dso_list
,
1421 sort_entry__setup_elide(&sort_dso
, symbol_conf
.dso_list
,
1422 "local_weight", output
);
1423 sort_entry__setup_elide(&sort_dso
, symbol_conf
.dso_list
,
1425 sort_entry__setup_elide(&sort_dso
, symbol_conf
.dso_list
,
1430 * It makes no sense to elide all of sort entries.
1431 * Just revert them to show up again.
1433 perf_hpp__for_each_format(fmt
) {
1434 if (!perf_hpp__is_sort_entry(fmt
))
1437 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1438 if (!hse
->se
->elide
)
1442 perf_hpp__for_each_format(fmt
) {
1443 if (!perf_hpp__is_sort_entry(fmt
))
1446 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1447 hse
->se
->elide
= false;
1451 static int output_field_add(char *tok
)
1455 for (i
= 0; i
< ARRAY_SIZE(common_sort_dimensions
); i
++) {
1456 struct sort_dimension
*sd
= &common_sort_dimensions
[i
];
1458 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
1461 return __sort_dimension__add_output(sd
);
1464 for (i
= 0; i
< ARRAY_SIZE(hpp_sort_dimensions
); i
++) {
1465 struct hpp_dimension
*hd
= &hpp_sort_dimensions
[i
];
1467 if (strncasecmp(tok
, hd
->name
, strlen(tok
)))
1470 return __hpp_dimension__add_output(hd
);
1473 for (i
= 0; i
< ARRAY_SIZE(bstack_sort_dimensions
); i
++) {
1474 struct sort_dimension
*sd
= &bstack_sort_dimensions
[i
];
1476 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
1479 return __sort_dimension__add_output(sd
);
1482 for (i
= 0; i
< ARRAY_SIZE(memory_sort_dimensions
); i
++) {
1483 struct sort_dimension
*sd
= &memory_sort_dimensions
[i
];
1485 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
1488 return __sort_dimension__add_output(sd
);
1494 static void reset_dimensions(void)
1498 for (i
= 0; i
< ARRAY_SIZE(common_sort_dimensions
); i
++)
1499 common_sort_dimensions
[i
].taken
= 0;
1501 for (i
= 0; i
< ARRAY_SIZE(hpp_sort_dimensions
); i
++)
1502 hpp_sort_dimensions
[i
].taken
= 0;
1504 for (i
= 0; i
< ARRAY_SIZE(bstack_sort_dimensions
); i
++)
1505 bstack_sort_dimensions
[i
].taken
= 0;
1507 for (i
= 0; i
< ARRAY_SIZE(memory_sort_dimensions
); i
++)
1508 memory_sort_dimensions
[i
].taken
= 0;
1511 static int __setup_output_field(void)
1513 char *tmp
, *tok
, *str
;
1516 if (field_order
== NULL
)
1521 str
= strdup(field_order
);
1523 error("Not enough memory to setup output fields");
1527 for (tok
= strtok_r(str
, ", ", &tmp
);
1528 tok
; tok
= strtok_r(NULL
, ", ", &tmp
)) {
1529 ret
= output_field_add(tok
);
1530 if (ret
== -EINVAL
) {
1531 error("Invalid --fields key: `%s'", tok
);
1533 } else if (ret
== -ESRCH
) {
1534 error("Unknown --fields key: `%s'", tok
);
1543 int setup_sorting(void)
1547 err
= __setup_sorting();
1551 if (parent_pattern
!= default_parent_pattern
) {
1552 err
= sort_dimension__add("parent");
1560 * perf diff doesn't use default hpp output fields.
1562 if (sort__mode
!= SORT_MODE__DIFF
)
1565 err
= __setup_output_field();
1569 /* copy sort keys to output fields */
1570 perf_hpp__setup_output_field();
1571 /* and then copy output fields to sort keys */
1572 perf_hpp__append_sort_keys();