5 const char default_parent_pattern
[] = "^sys_|^do_page_fault";
6 const char *parent_pattern
= default_parent_pattern
;
7 const char default_sort_order
[] = "comm,dso,symbol";
8 const char *sort_order
= default_sort_order
;
9 int sort__need_collapse
= 0;
10 int sort__has_parent
= 0;
11 int sort__has_sym
= 0;
12 int sort__branch_mode
= -1; /* -1 = means not set */
14 enum sort_type sort__first_dimension
;
16 LIST_HEAD(hist_entry__sort_list
);
18 static int repsep_snprintf(char *bf
, size_t size
, const char *fmt
, ...)
24 n
= vsnprintf(bf
, size
, fmt
, ap
);
25 if (symbol_conf
.field_sep
&& n
> 0) {
29 sep
= strchr(sep
, *symbol_conf
.field_sep
);
42 static int64_t cmp_null(void *l
, void *r
)
55 sort__thread_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
57 return right
->thread
->pid
- left
->thread
->pid
;
60 static int hist_entry__thread_snprintf(struct hist_entry
*self
, char *bf
,
61 size_t size
, unsigned int width
)
63 return repsep_snprintf(bf
, size
, "%*s:%5d", width
- 6,
64 self
->thread
->comm
?: "", self
->thread
->pid
);
67 struct sort_entry sort_thread
= {
68 .se_header
= "Command: Pid",
69 .se_cmp
= sort__thread_cmp
,
70 .se_snprintf
= hist_entry__thread_snprintf
,
71 .se_width_idx
= HISTC_THREAD
,
77 sort__comm_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
79 return right
->thread
->pid
- left
->thread
->pid
;
83 sort__comm_collapse(struct hist_entry
*left
, struct hist_entry
*right
)
85 char *comm_l
= left
->thread
->comm
;
86 char *comm_r
= right
->thread
->comm
;
88 if (!comm_l
|| !comm_r
)
89 return cmp_null(comm_l
, comm_r
);
91 return strcmp(comm_l
, comm_r
);
94 static int hist_entry__comm_snprintf(struct hist_entry
*self
, char *bf
,
95 size_t size
, unsigned int width
)
97 return repsep_snprintf(bf
, size
, "%*s", width
, self
->thread
->comm
);
100 struct sort_entry sort_comm
= {
101 .se_header
= "Command",
102 .se_cmp
= sort__comm_cmp
,
103 .se_collapse
= sort__comm_collapse
,
104 .se_snprintf
= hist_entry__comm_snprintf
,
105 .se_width_idx
= HISTC_COMM
,
110 static int64_t _sort__dso_cmp(struct map
*map_l
, struct map
*map_r
)
112 struct dso
*dso_l
= map_l
? map_l
->dso
: NULL
;
113 struct dso
*dso_r
= map_r
? map_r
->dso
: NULL
;
114 const char *dso_name_l
, *dso_name_r
;
116 if (!dso_l
|| !dso_r
)
117 return cmp_null(dso_l
, dso_r
);
120 dso_name_l
= dso_l
->long_name
;
121 dso_name_r
= dso_r
->long_name
;
123 dso_name_l
= dso_l
->short_name
;
124 dso_name_r
= dso_r
->short_name
;
127 return strcmp(dso_name_l
, dso_name_r
);
131 sort__dso_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
133 return _sort__dso_cmp(left
->ms
.map
, right
->ms
.map
);
136 static int _hist_entry__dso_snprintf(struct map
*map
, char *bf
,
137 size_t size
, unsigned int width
)
139 if (map
&& map
->dso
) {
140 const char *dso_name
= !verbose
? map
->dso
->short_name
:
142 return repsep_snprintf(bf
, size
, "%-*s", width
, dso_name
);
145 return repsep_snprintf(bf
, size
, "%-*s", width
, "[unknown]");
148 static int hist_entry__dso_snprintf(struct hist_entry
*self
, char *bf
,
149 size_t size
, unsigned int width
)
151 return _hist_entry__dso_snprintf(self
->ms
.map
, bf
, size
, width
);
154 struct sort_entry sort_dso
= {
155 .se_header
= "Shared Object",
156 .se_cmp
= sort__dso_cmp
,
157 .se_snprintf
= hist_entry__dso_snprintf
,
158 .se_width_idx
= HISTC_DSO
,
163 static int64_t _sort__sym_cmp(struct symbol
*sym_l
, struct symbol
*sym_r
,
166 if (!sym_l
|| !sym_r
)
167 return cmp_null(sym_l
, sym_r
);
175 return (int64_t)(ip_r
- ip_l
);
179 sort__sym_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
183 if (!left
->ms
.sym
&& !right
->ms
.sym
)
184 return right
->level
- left
->level
;
186 if (!left
->ms
.sym
|| !right
->ms
.sym
)
187 return cmp_null(left
->ms
.sym
, right
->ms
.sym
);
189 if (left
->ms
.sym
== right
->ms
.sym
)
192 ip_l
= left
->ms
.sym
->start
;
193 ip_r
= right
->ms
.sym
->start
;
195 return _sort__sym_cmp(left
->ms
.sym
, right
->ms
.sym
, ip_l
, ip_r
);
198 static int _hist_entry__sym_snprintf(struct map
*map
, struct symbol
*sym
,
199 u64 ip
, char level
, char *bf
, size_t size
,
205 char o
= map
? dso__symtab_origin(map
->dso
) : '!';
206 ret
+= repsep_snprintf(bf
, size
, "%-#*llx %c ",
207 BITS_PER_LONG
/ 4, ip
, o
);
210 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "[%c] ", level
);
212 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-*s",
216 size_t len
= BITS_PER_LONG
/ 4;
217 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-#.*llx",
219 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-*s",
226 static int hist_entry__sym_snprintf(struct hist_entry
*self
, char *bf
,
227 size_t size
, unsigned int width
)
229 return _hist_entry__sym_snprintf(self
->ms
.map
, self
->ms
.sym
, self
->ip
,
230 self
->level
, bf
, size
, width
);
233 struct sort_entry sort_sym
= {
234 .se_header
= "Symbol",
235 .se_cmp
= sort__sym_cmp
,
236 .se_snprintf
= hist_entry__sym_snprintf
,
237 .se_width_idx
= HISTC_SYMBOL
,
243 sort__srcline_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
245 return (int64_t)(right
->ip
- left
->ip
);
248 static int hist_entry__srcline_snprintf(struct hist_entry
*self
, char *bf
,
250 unsigned int width __maybe_unused
)
253 char cmd
[PATH_MAX
+ 2], *path
= self
->srcline
, *nl
;
262 if (!strncmp(self
->ms
.map
->dso
->long_name
, "/tmp/perf-", 10))
265 snprintf(cmd
, sizeof(cmd
), "addr2line -e %s %016" PRIx64
,
266 self
->ms
.map
->dso
->long_name
, self
->ip
);
267 fp
= popen(cmd
, "r");
271 if (getline(&path
, &line_len
, fp
) < 0 || !line_len
)
273 self
->srcline
= strdup(path
);
274 if (self
->srcline
== NULL
)
277 nl
= strchr(self
->srcline
, '\n');
280 path
= self
->srcline
;
284 return repsep_snprintf(bf
, size
, "%s", path
);
288 return repsep_snprintf(bf
, size
, "%-#*llx", BITS_PER_LONG
/ 4, self
->ip
);
291 struct sort_entry sort_srcline
= {
292 .se_header
= "Source:Line",
293 .se_cmp
= sort__srcline_cmp
,
294 .se_snprintf
= hist_entry__srcline_snprintf
,
295 .se_width_idx
= HISTC_SRCLINE
,
301 sort__parent_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
303 struct symbol
*sym_l
= left
->parent
;
304 struct symbol
*sym_r
= right
->parent
;
306 if (!sym_l
|| !sym_r
)
307 return cmp_null(sym_l
, sym_r
);
309 return strcmp(sym_l
->name
, sym_r
->name
);
312 static int hist_entry__parent_snprintf(struct hist_entry
*self
, char *bf
,
313 size_t size
, unsigned int width
)
315 return repsep_snprintf(bf
, size
, "%-*s", width
,
316 self
->parent
? self
->parent
->name
: "[other]");
319 struct sort_entry sort_parent
= {
320 .se_header
= "Parent symbol",
321 .se_cmp
= sort__parent_cmp
,
322 .se_snprintf
= hist_entry__parent_snprintf
,
323 .se_width_idx
= HISTC_PARENT
,
329 sort__cpu_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
331 return right
->cpu
- left
->cpu
;
334 static int hist_entry__cpu_snprintf(struct hist_entry
*self
, char *bf
,
335 size_t size
, unsigned int width
)
337 return repsep_snprintf(bf
, size
, "%*d", width
, self
->cpu
);
340 struct sort_entry sort_cpu
= {
342 .se_cmp
= sort__cpu_cmp
,
343 .se_snprintf
= hist_entry__cpu_snprintf
,
344 .se_width_idx
= HISTC_CPU
,
347 /* sort keys for branch stacks */
350 sort__dso_from_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
352 return _sort__dso_cmp(left
->branch_info
->from
.map
,
353 right
->branch_info
->from
.map
);
356 static int hist_entry__dso_from_snprintf(struct hist_entry
*self
, char *bf
,
357 size_t size
, unsigned int width
)
359 return _hist_entry__dso_snprintf(self
->branch_info
->from
.map
,
364 sort__dso_to_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
366 return _sort__dso_cmp(left
->branch_info
->to
.map
,
367 right
->branch_info
->to
.map
);
370 static int hist_entry__dso_to_snprintf(struct hist_entry
*self
, char *bf
,
371 size_t size
, unsigned int width
)
373 return _hist_entry__dso_snprintf(self
->branch_info
->to
.map
,
378 sort__sym_from_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
380 struct addr_map_symbol
*from_l
= &left
->branch_info
->from
;
381 struct addr_map_symbol
*from_r
= &right
->branch_info
->from
;
383 if (!from_l
->sym
&& !from_r
->sym
)
384 return right
->level
- left
->level
;
386 return _sort__sym_cmp(from_l
->sym
, from_r
->sym
, from_l
->addr
,
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
, to_l
->addr
, to_r
->addr
);
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 struct sort_entry sort_mispredict
= {
472 .se_header
= "Branch Mispredicted",
473 .se_cmp
= sort__mispredict_cmp
,
474 .se_snprintf
= hist_entry__mispredict_snprintf
,
475 .se_width_idx
= HISTC_MISPREDICT
,
478 struct sort_dimension
{
480 struct sort_entry
*entry
;
484 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
486 static struct sort_dimension common_sort_dimensions
[] = {
487 DIM(SORT_PID
, "pid", sort_thread
),
488 DIM(SORT_COMM
, "comm", sort_comm
),
489 DIM(SORT_DSO
, "dso", sort_dso
),
490 DIM(SORT_SYM
, "symbol", sort_sym
),
491 DIM(SORT_PARENT
, "parent", sort_parent
),
492 DIM(SORT_CPU
, "cpu", sort_cpu
),
493 DIM(SORT_SRCLINE
, "srcline", sort_srcline
),
498 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
500 static struct sort_dimension bstack_sort_dimensions
[] = {
501 DIM(SORT_DSO_FROM
, "dso_from", sort_dso_from
),
502 DIM(SORT_DSO_TO
, "dso_to", sort_dso_to
),
503 DIM(SORT_SYM_FROM
, "symbol_from", sort_sym_from
),
504 DIM(SORT_SYM_TO
, "symbol_to", sort_sym_to
),
505 DIM(SORT_MISPREDICT
, "mispredict", sort_mispredict
),
510 int sort_dimension__add(const char *tok
)
514 for (i
= 0; i
< ARRAY_SIZE(common_sort_dimensions
); i
++) {
515 struct sort_dimension
*sd
= &common_sort_dimensions
[i
];
517 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
520 if (sd
->entry
== &sort_parent
) {
521 int ret
= regcomp(&parent_regex
, parent_pattern
, REG_EXTENDED
);
525 regerror(ret
, &parent_regex
, err
, sizeof(err
));
526 pr_err("Invalid regex: %s\n%s", parent_pattern
, err
);
529 sort__has_parent
= 1;
530 } else if (sd
->entry
== &sort_sym
) {
537 if (sd
->entry
->se_collapse
)
538 sort__need_collapse
= 1;
540 if (list_empty(&hist_entry__sort_list
))
541 sort__first_dimension
= i
;
543 list_add_tail(&sd
->entry
->list
, &hist_entry__sort_list
);
549 for (i
= 0; i
< ARRAY_SIZE(bstack_sort_dimensions
); i
++) {
550 struct sort_dimension
*sd
= &bstack_sort_dimensions
[i
];
552 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
555 if (sort__branch_mode
!= 1)
558 if (sd
->entry
== &sort_sym_from
|| sd
->entry
== &sort_sym_to
)
564 if (sd
->entry
->se_collapse
)
565 sort__need_collapse
= 1;
567 if (list_empty(&hist_entry__sort_list
))
568 sort__first_dimension
= i
+ __SORT_BRANCH_STACK
;
570 list_add_tail(&sd
->entry
->list
, &hist_entry__sort_list
);
579 void setup_sorting(const char * const usagestr
[], const struct option
*opts
)
581 char *tmp
, *tok
, *str
= strdup(sort_order
);
583 for (tok
= strtok_r(str
, ", ", &tmp
);
584 tok
; tok
= strtok_r(NULL
, ", ", &tmp
)) {
585 int ret
= sort_dimension__add(tok
);
586 if (ret
== -EINVAL
) {
587 error("Invalid --sort key: `%s'", tok
);
588 usage_with_options(usagestr
, opts
);
589 } else if (ret
== -ESRCH
) {
590 error("Unknown --sort key: `%s'", tok
);
591 usage_with_options(usagestr
, opts
);
598 void sort_entry__setup_elide(struct sort_entry
*self
, struct strlist
*list
,
599 const char *list_name
, FILE *fp
)
601 if (list
&& strlist__nr_entries(list
) == 1) {
603 fprintf(fp
, "# %s: %s\n", list_name
,
604 strlist__entry(list
, 0)->s
);