6 const char default_parent_pattern
[] = "^sys_|^do_page_fault";
7 const char *parent_pattern
= default_parent_pattern
;
8 const char default_sort_order
[] = "comm,dso,symbol";
9 const char *sort_order
= default_sort_order
;
10 regex_t ignore_callees_regex
;
11 int have_ignore_callees
= 0;
12 int sort__need_collapse
= 0;
13 int sort__has_parent
= 0;
14 int sort__has_sym
= 0;
15 enum sort_mode sort__mode
= SORT_MODE__NORMAL
;
17 enum sort_type sort__first_dimension
;
19 LIST_HEAD(hist_entry__sort_list
);
21 static int repsep_snprintf(char *bf
, size_t size
, const char *fmt
, ...)
27 n
= vsnprintf(bf
, size
, fmt
, ap
);
28 if (symbol_conf
.field_sep
&& n
> 0) {
32 sep
= strchr(sep
, *symbol_conf
.field_sep
);
45 static int64_t cmp_null(void *l
, void *r
)
58 sort__thread_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
60 return right
->thread
->tid
- left
->thread
->tid
;
63 static int hist_entry__thread_snprintf(struct hist_entry
*self
, char *bf
,
64 size_t size
, unsigned int width
)
66 return repsep_snprintf(bf
, size
, "%*s:%5d", width
- 6,
67 self
->thread
->comm
?: "", self
->thread
->tid
);
70 struct sort_entry sort_thread
= {
71 .se_header
= "Command: Pid",
72 .se_cmp
= sort__thread_cmp
,
73 .se_snprintf
= hist_entry__thread_snprintf
,
74 .se_width_idx
= HISTC_THREAD
,
80 sort__comm_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
82 return right
->thread
->tid
- left
->thread
->tid
;
86 sort__comm_collapse(struct hist_entry
*left
, struct hist_entry
*right
)
88 char *comm_l
= left
->thread
->comm
;
89 char *comm_r
= right
->thread
->comm
;
91 if (!comm_l
|| !comm_r
)
92 return cmp_null(comm_l
, comm_r
);
94 return strcmp(comm_l
, comm_r
);
97 static int hist_entry__comm_snprintf(struct hist_entry
*self
, char *bf
,
98 size_t size
, unsigned int width
)
100 return repsep_snprintf(bf
, size
, "%*s", width
, self
->thread
->comm
);
103 struct sort_entry sort_comm
= {
104 .se_header
= "Command",
105 .se_cmp
= sort__comm_cmp
,
106 .se_collapse
= sort__comm_collapse
,
107 .se_snprintf
= hist_entry__comm_snprintf
,
108 .se_width_idx
= HISTC_COMM
,
113 static int64_t _sort__dso_cmp(struct map
*map_l
, struct map
*map_r
)
115 struct dso
*dso_l
= map_l
? map_l
->dso
: NULL
;
116 struct dso
*dso_r
= map_r
? map_r
->dso
: NULL
;
117 const char *dso_name_l
, *dso_name_r
;
119 if (!dso_l
|| !dso_r
)
120 return cmp_null(dso_l
, dso_r
);
123 dso_name_l
= dso_l
->long_name
;
124 dso_name_r
= dso_r
->long_name
;
126 dso_name_l
= dso_l
->short_name
;
127 dso_name_r
= dso_r
->short_name
;
130 return strcmp(dso_name_l
, dso_name_r
);
134 sort__dso_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
136 return _sort__dso_cmp(left
->ms
.map
, right
->ms
.map
);
139 static int _hist_entry__dso_snprintf(struct map
*map
, char *bf
,
140 size_t size
, unsigned int width
)
142 if (map
&& map
->dso
) {
143 const char *dso_name
= !verbose
? map
->dso
->short_name
:
145 return repsep_snprintf(bf
, size
, "%-*s", width
, dso_name
);
148 return repsep_snprintf(bf
, size
, "%-*s", width
, "[unknown]");
151 static int hist_entry__dso_snprintf(struct hist_entry
*self
, char *bf
,
152 size_t size
, unsigned int width
)
154 return _hist_entry__dso_snprintf(self
->ms
.map
, bf
, size
, width
);
157 struct sort_entry sort_dso
= {
158 .se_header
= "Shared Object",
159 .se_cmp
= sort__dso_cmp
,
160 .se_snprintf
= hist_entry__dso_snprintf
,
161 .se_width_idx
= HISTC_DSO
,
166 static int64_t _sort__sym_cmp(struct symbol
*sym_l
, struct symbol
*sym_r
)
170 if (!sym_l
|| !sym_r
)
171 return cmp_null(sym_l
, sym_r
);
179 return (int64_t)(ip_r
- ip_l
);
183 sort__sym_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
185 if (!left
->ms
.sym
&& !right
->ms
.sym
)
186 return right
->level
- left
->level
;
188 return _sort__sym_cmp(left
->ms
.sym
, right
->ms
.sym
);
191 static int _hist_entry__sym_snprintf(struct map
*map
, struct symbol
*sym
,
192 u64 ip
, char level
, char *bf
, size_t size
,
198 char o
= map
? dso__symtab_origin(map
->dso
) : '!';
199 ret
+= repsep_snprintf(bf
, size
, "%-#*llx %c ",
200 BITS_PER_LONG
/ 4 + 2, ip
, o
);
203 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "[%c] ", level
);
205 if (map
->type
== MAP__VARIABLE
) {
206 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%s", sym
->name
);
207 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "+0x%llx",
208 ip
- map
->unmap_ip(map
, sym
->start
));
209 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-*s",
212 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-*s",
217 size_t len
= BITS_PER_LONG
/ 4;
218 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-#.*llx",
220 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-*s",
227 static int hist_entry__sym_snprintf(struct hist_entry
*self
, char *bf
,
228 size_t size
, unsigned int width
)
230 return _hist_entry__sym_snprintf(self
->ms
.map
, self
->ms
.sym
, self
->ip
,
231 self
->level
, bf
, size
, width
);
234 struct sort_entry sort_sym
= {
235 .se_header
= "Symbol",
236 .se_cmp
= sort__sym_cmp
,
237 .se_snprintf
= hist_entry__sym_snprintf
,
238 .se_width_idx
= HISTC_SYMBOL
,
244 sort__srcline_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
246 return (int64_t)(right
->ip
- left
->ip
);
249 static int hist_entry__srcline_snprintf(struct hist_entry
*self
, char *bf
,
251 unsigned int width __maybe_unused
)
254 char cmd
[PATH_MAX
+ 2], *path
= self
->srcline
, *nl
;
263 if (!strncmp(self
->ms
.map
->dso
->long_name
, "/tmp/perf-", 10))
266 snprintf(cmd
, sizeof(cmd
), "addr2line -e %s %016" PRIx64
,
267 self
->ms
.map
->dso
->long_name
, self
->ip
);
268 fp
= popen(cmd
, "r");
272 if (getline(&path
, &line_len
, fp
) < 0 || !line_len
)
274 self
->srcline
= strdup(path
);
275 if (self
->srcline
== NULL
)
278 nl
= strchr(self
->srcline
, '\n');
281 path
= self
->srcline
;
285 return repsep_snprintf(bf
, size
, "%s", path
);
289 return repsep_snprintf(bf
, size
, "%-#*llx", BITS_PER_LONG
/ 4, self
->ip
);
292 struct sort_entry sort_srcline
= {
293 .se_header
= "Source:Line",
294 .se_cmp
= sort__srcline_cmp
,
295 .se_snprintf
= hist_entry__srcline_snprintf
,
296 .se_width_idx
= HISTC_SRCLINE
,
302 sort__parent_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
304 struct symbol
*sym_l
= left
->parent
;
305 struct symbol
*sym_r
= right
->parent
;
307 if (!sym_l
|| !sym_r
)
308 return cmp_null(sym_l
, sym_r
);
310 return strcmp(sym_l
->name
, sym_r
->name
);
313 static int hist_entry__parent_snprintf(struct hist_entry
*self
, char *bf
,
314 size_t size
, unsigned int width
)
316 return repsep_snprintf(bf
, size
, "%-*s", width
,
317 self
->parent
? self
->parent
->name
: "[other]");
320 struct sort_entry sort_parent
= {
321 .se_header
= "Parent symbol",
322 .se_cmp
= sort__parent_cmp
,
323 .se_snprintf
= hist_entry__parent_snprintf
,
324 .se_width_idx
= HISTC_PARENT
,
330 sort__cpu_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
332 return right
->cpu
- left
->cpu
;
335 static int hist_entry__cpu_snprintf(struct hist_entry
*self
, char *bf
,
336 size_t size
, unsigned int width
)
338 return repsep_snprintf(bf
, size
, "%*d", width
, self
->cpu
);
341 struct sort_entry sort_cpu
= {
343 .se_cmp
= sort__cpu_cmp
,
344 .se_snprintf
= hist_entry__cpu_snprintf
,
345 .se_width_idx
= HISTC_CPU
,
348 /* sort keys for branch stacks */
351 sort__dso_from_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
353 return _sort__dso_cmp(left
->branch_info
->from
.map
,
354 right
->branch_info
->from
.map
);
357 static int hist_entry__dso_from_snprintf(struct hist_entry
*self
, char *bf
,
358 size_t size
, unsigned int width
)
360 return _hist_entry__dso_snprintf(self
->branch_info
->from
.map
,
365 sort__dso_to_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
367 return _sort__dso_cmp(left
->branch_info
->to
.map
,
368 right
->branch_info
->to
.map
);
371 static int hist_entry__dso_to_snprintf(struct hist_entry
*self
, char *bf
,
372 size_t size
, unsigned int width
)
374 return _hist_entry__dso_snprintf(self
->branch_info
->to
.map
,
379 sort__sym_from_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
381 struct addr_map_symbol
*from_l
= &left
->branch_info
->from
;
382 struct addr_map_symbol
*from_r
= &right
->branch_info
->from
;
384 if (!from_l
->sym
&& !from_r
->sym
)
385 return right
->level
- left
->level
;
387 return _sort__sym_cmp(from_l
->sym
, from_r
->sym
);
391 sort__sym_to_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
393 struct addr_map_symbol
*to_l
= &left
->branch_info
->to
;
394 struct addr_map_symbol
*to_r
= &right
->branch_info
->to
;
396 if (!to_l
->sym
&& !to_r
->sym
)
397 return right
->level
- left
->level
;
399 return _sort__sym_cmp(to_l
->sym
, to_r
->sym
);
402 static int hist_entry__sym_from_snprintf(struct hist_entry
*self
, char *bf
,
403 size_t size
, unsigned int width
)
405 struct addr_map_symbol
*from
= &self
->branch_info
->from
;
406 return _hist_entry__sym_snprintf(from
->map
, from
->sym
, from
->addr
,
407 self
->level
, bf
, size
, width
);
411 static int hist_entry__sym_to_snprintf(struct hist_entry
*self
, char *bf
,
412 size_t size
, unsigned int width
)
414 struct addr_map_symbol
*to
= &self
->branch_info
->to
;
415 return _hist_entry__sym_snprintf(to
->map
, to
->sym
, to
->addr
,
416 self
->level
, bf
, size
, width
);
420 struct sort_entry sort_dso_from
= {
421 .se_header
= "Source Shared Object",
422 .se_cmp
= sort__dso_from_cmp
,
423 .se_snprintf
= hist_entry__dso_from_snprintf
,
424 .se_width_idx
= HISTC_DSO_FROM
,
427 struct sort_entry sort_dso_to
= {
428 .se_header
= "Target Shared Object",
429 .se_cmp
= sort__dso_to_cmp
,
430 .se_snprintf
= hist_entry__dso_to_snprintf
,
431 .se_width_idx
= HISTC_DSO_TO
,
434 struct sort_entry sort_sym_from
= {
435 .se_header
= "Source Symbol",
436 .se_cmp
= sort__sym_from_cmp
,
437 .se_snprintf
= hist_entry__sym_from_snprintf
,
438 .se_width_idx
= HISTC_SYMBOL_FROM
,
441 struct sort_entry sort_sym_to
= {
442 .se_header
= "Target Symbol",
443 .se_cmp
= sort__sym_to_cmp
,
444 .se_snprintf
= hist_entry__sym_to_snprintf
,
445 .se_width_idx
= HISTC_SYMBOL_TO
,
449 sort__mispredict_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
451 const unsigned char mp
= left
->branch_info
->flags
.mispred
!=
452 right
->branch_info
->flags
.mispred
;
453 const unsigned char p
= left
->branch_info
->flags
.predicted
!=
454 right
->branch_info
->flags
.predicted
;
459 static int hist_entry__mispredict_snprintf(struct hist_entry
*self
, char *bf
,
460 size_t size
, unsigned int width
){
461 static const char *out
= "N/A";
463 if (self
->branch_info
->flags
.predicted
)
465 else if (self
->branch_info
->flags
.mispred
)
468 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
471 /* --sort daddr_sym */
473 sort__daddr_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
475 uint64_t l
= 0, r
= 0;
478 l
= left
->mem_info
->daddr
.addr
;
480 r
= right
->mem_info
->daddr
.addr
;
482 return (int64_t)(r
- l
);
485 static int hist_entry__daddr_snprintf(struct hist_entry
*self
, char *bf
,
486 size_t size
, unsigned int width
)
489 struct map
*map
= NULL
;
490 struct symbol
*sym
= NULL
;
492 if (self
->mem_info
) {
493 addr
= self
->mem_info
->daddr
.addr
;
494 map
= self
->mem_info
->daddr
.map
;
495 sym
= self
->mem_info
->daddr
.sym
;
497 return _hist_entry__sym_snprintf(map
, sym
, addr
, self
->level
, bf
, size
,
502 sort__dso_daddr_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
504 struct map
*map_l
= NULL
;
505 struct map
*map_r
= NULL
;
508 map_l
= left
->mem_info
->daddr
.map
;
510 map_r
= right
->mem_info
->daddr
.map
;
512 return _sort__dso_cmp(map_l
, map_r
);
515 static int hist_entry__dso_daddr_snprintf(struct hist_entry
*self
, char *bf
,
516 size_t size
, unsigned int width
)
518 struct map
*map
= NULL
;
521 map
= self
->mem_info
->daddr
.map
;
523 return _hist_entry__dso_snprintf(map
, bf
, size
, width
);
527 sort__locked_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
529 union perf_mem_data_src data_src_l
;
530 union perf_mem_data_src data_src_r
;
533 data_src_l
= left
->mem_info
->data_src
;
535 data_src_l
.mem_lock
= PERF_MEM_LOCK_NA
;
538 data_src_r
= right
->mem_info
->data_src
;
540 data_src_r
.mem_lock
= PERF_MEM_LOCK_NA
;
542 return (int64_t)(data_src_r
.mem_lock
- data_src_l
.mem_lock
);
545 static int hist_entry__locked_snprintf(struct hist_entry
*self
, char *bf
,
546 size_t size
, unsigned int width
)
549 u64 mask
= PERF_MEM_LOCK_NA
;
552 mask
= self
->mem_info
->data_src
.mem_lock
;
554 if (mask
& PERF_MEM_LOCK_NA
)
556 else if (mask
& PERF_MEM_LOCK_LOCKED
)
561 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
565 sort__tlb_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
567 union perf_mem_data_src data_src_l
;
568 union perf_mem_data_src data_src_r
;
571 data_src_l
= left
->mem_info
->data_src
;
573 data_src_l
.mem_dtlb
= PERF_MEM_TLB_NA
;
576 data_src_r
= right
->mem_info
->data_src
;
578 data_src_r
.mem_dtlb
= PERF_MEM_TLB_NA
;
580 return (int64_t)(data_src_r
.mem_dtlb
- data_src_l
.mem_dtlb
);
583 static const char * const tlb_access
[] = {
592 #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
594 static int hist_entry__tlb_snprintf(struct hist_entry
*self
, char *bf
,
595 size_t size
, unsigned int width
)
598 size_t sz
= sizeof(out
) - 1; /* -1 for null termination */
600 u64 m
= PERF_MEM_TLB_NA
;
606 m
= self
->mem_info
->data_src
.mem_dtlb
;
608 hit
= m
& PERF_MEM_TLB_HIT
;
609 miss
= m
& PERF_MEM_TLB_MISS
;
611 /* already taken care of */
612 m
&= ~(PERF_MEM_TLB_HIT
|PERF_MEM_TLB_MISS
);
614 for (i
= 0; m
&& i
< NUM_TLB_ACCESS
; i
++, m
>>= 1) {
621 strncat(out
, tlb_access
[i
], sz
- l
);
622 l
+= strlen(tlb_access
[i
]);
627 strncat(out
, " hit", sz
- l
);
629 strncat(out
, " miss", sz
- l
);
631 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
635 sort__lvl_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
637 union perf_mem_data_src data_src_l
;
638 union perf_mem_data_src data_src_r
;
641 data_src_l
= left
->mem_info
->data_src
;
643 data_src_l
.mem_lvl
= PERF_MEM_LVL_NA
;
646 data_src_r
= right
->mem_info
->data_src
;
648 data_src_r
.mem_lvl
= PERF_MEM_LVL_NA
;
650 return (int64_t)(data_src_r
.mem_lvl
- data_src_l
.mem_lvl
);
653 static const char * const mem_lvl
[] = {
662 "Remote RAM (1 hop)",
663 "Remote RAM (2 hops)",
664 "Remote Cache (1 hop)",
665 "Remote Cache (2 hops)",
669 #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
671 static int hist_entry__lvl_snprintf(struct hist_entry
*self
, char *bf
,
672 size_t size
, unsigned int width
)
675 size_t sz
= sizeof(out
) - 1; /* -1 for null termination */
677 u64 m
= PERF_MEM_LVL_NA
;
681 m
= self
->mem_info
->data_src
.mem_lvl
;
685 hit
= m
& PERF_MEM_LVL_HIT
;
686 miss
= m
& PERF_MEM_LVL_MISS
;
688 /* already taken care of */
689 m
&= ~(PERF_MEM_LVL_HIT
|PERF_MEM_LVL_MISS
);
691 for (i
= 0; m
&& i
< NUM_MEM_LVL
; i
++, m
>>= 1) {
698 strncat(out
, mem_lvl
[i
], sz
- l
);
699 l
+= strlen(mem_lvl
[i
]);
704 strncat(out
, " hit", sz
- l
);
706 strncat(out
, " miss", sz
- l
);
708 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
712 sort__snoop_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
714 union perf_mem_data_src data_src_l
;
715 union perf_mem_data_src data_src_r
;
718 data_src_l
= left
->mem_info
->data_src
;
720 data_src_l
.mem_snoop
= PERF_MEM_SNOOP_NA
;
723 data_src_r
= right
->mem_info
->data_src
;
725 data_src_r
.mem_snoop
= PERF_MEM_SNOOP_NA
;
727 return (int64_t)(data_src_r
.mem_snoop
- data_src_l
.mem_snoop
);
730 static const char * const snoop_access
[] = {
737 #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
739 static int hist_entry__snoop_snprintf(struct hist_entry
*self
, char *bf
,
740 size_t size
, unsigned int width
)
743 size_t sz
= sizeof(out
) - 1; /* -1 for null termination */
745 u64 m
= PERF_MEM_SNOOP_NA
;
750 m
= self
->mem_info
->data_src
.mem_snoop
;
752 for (i
= 0; m
&& i
< NUM_SNOOP_ACCESS
; i
++, m
>>= 1) {
759 strncat(out
, snoop_access
[i
], sz
- l
);
760 l
+= strlen(snoop_access
[i
]);
766 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
769 struct sort_entry sort_mispredict
= {
770 .se_header
= "Branch Mispredicted",
771 .se_cmp
= sort__mispredict_cmp
,
772 .se_snprintf
= hist_entry__mispredict_snprintf
,
773 .se_width_idx
= HISTC_MISPREDICT
,
776 static u64
he_weight(struct hist_entry
*he
)
778 return he
->stat
.nr_events
? he
->stat
.weight
/ he
->stat
.nr_events
: 0;
782 sort__local_weight_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
784 return he_weight(left
) - he_weight(right
);
787 static int hist_entry__local_weight_snprintf(struct hist_entry
*self
, char *bf
,
788 size_t size
, unsigned int width
)
790 return repsep_snprintf(bf
, size
, "%-*llu", width
, he_weight(self
));
793 struct sort_entry sort_local_weight
= {
794 .se_header
= "Local Weight",
795 .se_cmp
= sort__local_weight_cmp
,
796 .se_snprintf
= hist_entry__local_weight_snprintf
,
797 .se_width_idx
= HISTC_LOCAL_WEIGHT
,
801 sort__global_weight_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
803 return left
->stat
.weight
- right
->stat
.weight
;
806 static int hist_entry__global_weight_snprintf(struct hist_entry
*self
, char *bf
,
807 size_t size
, unsigned int width
)
809 return repsep_snprintf(bf
, size
, "%-*llu", width
, self
->stat
.weight
);
812 struct sort_entry sort_global_weight
= {
813 .se_header
= "Weight",
814 .se_cmp
= sort__global_weight_cmp
,
815 .se_snprintf
= hist_entry__global_weight_snprintf
,
816 .se_width_idx
= HISTC_GLOBAL_WEIGHT
,
819 struct sort_entry sort_mem_daddr_sym
= {
820 .se_header
= "Data Symbol",
821 .se_cmp
= sort__daddr_cmp
,
822 .se_snprintf
= hist_entry__daddr_snprintf
,
823 .se_width_idx
= HISTC_MEM_DADDR_SYMBOL
,
826 struct sort_entry sort_mem_daddr_dso
= {
827 .se_header
= "Data Object",
828 .se_cmp
= sort__dso_daddr_cmp
,
829 .se_snprintf
= hist_entry__dso_daddr_snprintf
,
830 .se_width_idx
= HISTC_MEM_DADDR_SYMBOL
,
833 struct sort_entry sort_mem_locked
= {
834 .se_header
= "Locked",
835 .se_cmp
= sort__locked_cmp
,
836 .se_snprintf
= hist_entry__locked_snprintf
,
837 .se_width_idx
= HISTC_MEM_LOCKED
,
840 struct sort_entry sort_mem_tlb
= {
841 .se_header
= "TLB access",
842 .se_cmp
= sort__tlb_cmp
,
843 .se_snprintf
= hist_entry__tlb_snprintf
,
844 .se_width_idx
= HISTC_MEM_TLB
,
847 struct sort_entry sort_mem_lvl
= {
848 .se_header
= "Memory access",
849 .se_cmp
= sort__lvl_cmp
,
850 .se_snprintf
= hist_entry__lvl_snprintf
,
851 .se_width_idx
= HISTC_MEM_LVL
,
854 struct sort_entry sort_mem_snoop
= {
855 .se_header
= "Snoop",
856 .se_cmp
= sort__snoop_cmp
,
857 .se_snprintf
= hist_entry__snoop_snprintf
,
858 .se_width_idx
= HISTC_MEM_SNOOP
,
861 struct sort_dimension
{
863 struct sort_entry
*entry
;
867 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
869 static struct sort_dimension common_sort_dimensions
[] = {
870 DIM(SORT_PID
, "pid", sort_thread
),
871 DIM(SORT_COMM
, "comm", sort_comm
),
872 DIM(SORT_DSO
, "dso", sort_dso
),
873 DIM(SORT_SYM
, "symbol", sort_sym
),
874 DIM(SORT_PARENT
, "parent", sort_parent
),
875 DIM(SORT_CPU
, "cpu", sort_cpu
),
876 DIM(SORT_SRCLINE
, "srcline", sort_srcline
),
877 DIM(SORT_LOCAL_WEIGHT
, "local_weight", sort_local_weight
),
878 DIM(SORT_GLOBAL_WEIGHT
, "weight", sort_global_weight
),
883 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
885 static struct sort_dimension bstack_sort_dimensions
[] = {
886 DIM(SORT_DSO_FROM
, "dso_from", sort_dso_from
),
887 DIM(SORT_DSO_TO
, "dso_to", sort_dso_to
),
888 DIM(SORT_SYM_FROM
, "symbol_from", sort_sym_from
),
889 DIM(SORT_SYM_TO
, "symbol_to", sort_sym_to
),
890 DIM(SORT_MISPREDICT
, "mispredict", sort_mispredict
),
895 #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
897 static struct sort_dimension memory_sort_dimensions
[] = {
898 DIM(SORT_MEM_DADDR_SYMBOL
, "symbol_daddr", sort_mem_daddr_sym
),
899 DIM(SORT_MEM_DADDR_DSO
, "dso_daddr", sort_mem_daddr_dso
),
900 DIM(SORT_MEM_LOCKED
, "locked", sort_mem_locked
),
901 DIM(SORT_MEM_TLB
, "tlb", sort_mem_tlb
),
902 DIM(SORT_MEM_LVL
, "mem", sort_mem_lvl
),
903 DIM(SORT_MEM_SNOOP
, "snoop", sort_mem_snoop
),
908 static void __sort_dimension__add(struct sort_dimension
*sd
, enum sort_type idx
)
913 if (sd
->entry
->se_collapse
)
914 sort__need_collapse
= 1;
916 if (list_empty(&hist_entry__sort_list
))
917 sort__first_dimension
= idx
;
919 list_add_tail(&sd
->entry
->list
, &hist_entry__sort_list
);
923 int sort_dimension__add(const char *tok
)
927 for (i
= 0; i
< ARRAY_SIZE(common_sort_dimensions
); i
++) {
928 struct sort_dimension
*sd
= &common_sort_dimensions
[i
];
930 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
933 if (sd
->entry
== &sort_parent
) {
934 int ret
= regcomp(&parent_regex
, parent_pattern
, REG_EXTENDED
);
938 regerror(ret
, &parent_regex
, err
, sizeof(err
));
939 pr_err("Invalid regex: %s\n%s", parent_pattern
, err
);
942 sort__has_parent
= 1;
943 } else if (sd
->entry
== &sort_sym
) {
947 __sort_dimension__add(sd
, i
);
951 for (i
= 0; i
< ARRAY_SIZE(bstack_sort_dimensions
); i
++) {
952 struct sort_dimension
*sd
= &bstack_sort_dimensions
[i
];
954 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
957 if (sort__mode
!= SORT_MODE__BRANCH
)
960 if (sd
->entry
== &sort_sym_from
|| sd
->entry
== &sort_sym_to
)
963 __sort_dimension__add(sd
, i
+ __SORT_BRANCH_STACK
);
967 for (i
= 0; i
< ARRAY_SIZE(memory_sort_dimensions
); i
++) {
968 struct sort_dimension
*sd
= &memory_sort_dimensions
[i
];
970 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
973 if (sort__mode
!= SORT_MODE__MEMORY
)
976 if (sd
->entry
== &sort_mem_daddr_sym
)
979 __sort_dimension__add(sd
, i
+ __SORT_MEMORY_MODE
);
986 int setup_sorting(void)
988 char *tmp
, *tok
, *str
= strdup(sort_order
);
992 error("Not enough memory to setup sort keys");
996 for (tok
= strtok_r(str
, ", ", &tmp
);
997 tok
; tok
= strtok_r(NULL
, ", ", &tmp
)) {
998 ret
= sort_dimension__add(tok
);
999 if (ret
== -EINVAL
) {
1000 error("Invalid --sort key: `%s'", tok
);
1002 } else if (ret
== -ESRCH
) {
1003 error("Unknown --sort key: `%s'", tok
);
1012 static void sort_entry__setup_elide(struct sort_entry
*self
,
1013 struct strlist
*list
,
1014 const char *list_name
, FILE *fp
)
1016 if (list
&& strlist__nr_entries(list
) == 1) {
1018 fprintf(fp
, "# %s: %s\n", list_name
,
1019 strlist__entry(list
, 0)->s
);
1024 void sort__setup_elide(FILE *output
)
1026 sort_entry__setup_elide(&sort_dso
, symbol_conf
.dso_list
,
1028 sort_entry__setup_elide(&sort_comm
, symbol_conf
.comm_list
,
1030 sort_entry__setup_elide(&sort_sym
, symbol_conf
.sym_list
,
1033 if (sort__mode
== SORT_MODE__BRANCH
) {
1034 sort_entry__setup_elide(&sort_dso_from
,
1035 symbol_conf
.dso_from_list
,
1036 "dso_from", output
);
1037 sort_entry__setup_elide(&sort_dso_to
,
1038 symbol_conf
.dso_to_list
,
1040 sort_entry__setup_elide(&sort_sym_from
,
1041 symbol_conf
.sym_from_list
,
1042 "sym_from", output
);
1043 sort_entry__setup_elide(&sort_sym_to
,
1044 symbol_conf
.sym_to_list
,
1046 } else if (sort__mode
== SORT_MODE__MEMORY
) {
1047 sort_entry__setup_elide(&sort_dso
, symbol_conf
.dso_list
,
1048 "symbol_daddr", output
);
1049 sort_entry__setup_elide(&sort_dso
, symbol_conf
.dso_list
,
1050 "dso_daddr", output
);
1051 sort_entry__setup_elide(&sort_dso
, symbol_conf
.dso_list
,
1053 sort_entry__setup_elide(&sort_dso
, symbol_conf
.dso_list
,
1054 "local_weight", output
);
1055 sort_entry__setup_elide(&sort_dso
, symbol_conf
.dso_list
,
1057 sort_entry__setup_elide(&sort_dso
, symbol_conf
.dso_list
,