8 #include <traceevent/event-parse.h>
9 #include "mem-events.h"
12 const char default_parent_pattern
[] = "^sys_|^do_page_fault";
13 const char *parent_pattern
= default_parent_pattern
;
14 const char default_sort_order
[] = "comm,dso,symbol";
15 const char default_branch_sort_order
[] = "comm,dso_from,symbol_from,symbol_to,cycles";
16 const char default_mem_sort_order
[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
17 const char default_top_sort_order
[] = "dso,symbol";
18 const char default_diff_sort_order
[] = "dso,symbol";
19 const char default_tracepoint_sort_order
[] = "trace";
20 const char *sort_order
;
21 const char *field_order
;
22 regex_t ignore_callees_regex
;
23 int have_ignore_callees
= 0;
24 enum sort_mode sort__mode
= SORT_MODE__NORMAL
;
27 * Replaces all occurrences of a char used with the:
29 * -t, --field-separator
31 * option, that uses a special separator character and don't pad with spaces,
32 * replacing all occurances of this separator in symbol names (and other
33 * output) with a '.' character, that thus it's the only non valid separator.
35 static int repsep_snprintf(char *bf
, size_t size
, const char *fmt
, ...)
41 n
= vsnprintf(bf
, size
, fmt
, ap
);
42 if (symbol_conf
.field_sep
&& n
> 0) {
46 sep
= strchr(sep
, *symbol_conf
.field_sep
);
59 static int64_t cmp_null(const void *l
, const void *r
)
72 sort__thread_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
74 return right
->thread
->tid
- left
->thread
->tid
;
77 static int hist_entry__thread_snprintf(struct hist_entry
*he
, char *bf
,
78 size_t size
, unsigned int width
)
80 const char *comm
= thread__comm_str(he
->thread
);
82 width
= max(7U, width
) - 8;
83 return repsep_snprintf(bf
, size
, "%7d:%-*.*s", he
->thread
->tid
,
84 width
, width
, comm
?: "");
87 static int hist_entry__thread_filter(struct hist_entry
*he
, int type
, const void *arg
)
89 const struct thread
*th
= arg
;
91 if (type
!= HIST_FILTER__THREAD
)
94 return th
&& he
->thread
!= th
;
97 struct sort_entry sort_thread
= {
98 .se_header
= " Pid:Command",
99 .se_cmp
= sort__thread_cmp
,
100 .se_snprintf
= hist_entry__thread_snprintf
,
101 .se_filter
= hist_entry__thread_filter
,
102 .se_width_idx
= HISTC_THREAD
,
108 sort__comm_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
110 /* Compare the addr that should be unique among comm */
111 return strcmp(comm__str(right
->comm
), comm__str(left
->comm
));
115 sort__comm_collapse(struct hist_entry
*left
, struct hist_entry
*right
)
117 /* Compare the addr that should be unique among comm */
118 return strcmp(comm__str(right
->comm
), comm__str(left
->comm
));
122 sort__comm_sort(struct hist_entry
*left
, struct hist_entry
*right
)
124 return strcmp(comm__str(right
->comm
), comm__str(left
->comm
));
127 static int hist_entry__comm_snprintf(struct hist_entry
*he
, char *bf
,
128 size_t size
, unsigned int width
)
130 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, comm__str(he
->comm
));
133 struct sort_entry sort_comm
= {
134 .se_header
= "Command",
135 .se_cmp
= sort__comm_cmp
,
136 .se_collapse
= sort__comm_collapse
,
137 .se_sort
= sort__comm_sort
,
138 .se_snprintf
= hist_entry__comm_snprintf
,
139 .se_filter
= hist_entry__thread_filter
,
140 .se_width_idx
= HISTC_COMM
,
145 static int64_t _sort__dso_cmp(struct map
*map_l
, struct map
*map_r
)
147 struct dso
*dso_l
= map_l
? map_l
->dso
: NULL
;
148 struct dso
*dso_r
= map_r
? map_r
->dso
: NULL
;
149 const char *dso_name_l
, *dso_name_r
;
151 if (!dso_l
|| !dso_r
)
152 return cmp_null(dso_r
, dso_l
);
155 dso_name_l
= dso_l
->long_name
;
156 dso_name_r
= dso_r
->long_name
;
158 dso_name_l
= dso_l
->short_name
;
159 dso_name_r
= dso_r
->short_name
;
162 return strcmp(dso_name_l
, dso_name_r
);
166 sort__dso_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
168 return _sort__dso_cmp(right
->ms
.map
, left
->ms
.map
);
171 static int _hist_entry__dso_snprintf(struct map
*map
, char *bf
,
172 size_t size
, unsigned int width
)
174 if (map
&& map
->dso
) {
175 const char *dso_name
= !verbose
? map
->dso
->short_name
:
177 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, dso_name
);
180 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, "[unknown]");
183 static int hist_entry__dso_snprintf(struct hist_entry
*he
, char *bf
,
184 size_t size
, unsigned int width
)
186 return _hist_entry__dso_snprintf(he
->ms
.map
, bf
, size
, width
);
189 static int hist_entry__dso_filter(struct hist_entry
*he
, int type
, const void *arg
)
191 const struct dso
*dso
= arg
;
193 if (type
!= HIST_FILTER__DSO
)
196 return dso
&& (!he
->ms
.map
|| he
->ms
.map
->dso
!= dso
);
199 struct sort_entry sort_dso
= {
200 .se_header
= "Shared Object",
201 .se_cmp
= sort__dso_cmp
,
202 .se_snprintf
= hist_entry__dso_snprintf
,
203 .se_filter
= hist_entry__dso_filter
,
204 .se_width_idx
= HISTC_DSO
,
209 static int64_t _sort__addr_cmp(u64 left_ip
, u64 right_ip
)
211 return (int64_t)(right_ip
- left_ip
);
214 static int64_t _sort__sym_cmp(struct symbol
*sym_l
, struct symbol
*sym_r
)
216 if (!sym_l
|| !sym_r
)
217 return cmp_null(sym_l
, sym_r
);
222 if (sym_l
->start
!= sym_r
->start
)
223 return (int64_t)(sym_r
->start
- sym_l
->start
);
225 return (int64_t)(sym_r
->end
- sym_l
->end
);
229 sort__sym_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
233 if (!left
->ms
.sym
&& !right
->ms
.sym
)
234 return _sort__addr_cmp(left
->ip
, right
->ip
);
237 * comparing symbol address alone is not enough since it's a
238 * relative address within a dso.
240 if (!hists__has(left
->hists
, dso
) || hists__has(right
->hists
, dso
)) {
241 ret
= sort__dso_cmp(left
, right
);
246 return _sort__sym_cmp(left
->ms
.sym
, right
->ms
.sym
);
250 sort__sym_sort(struct hist_entry
*left
, struct hist_entry
*right
)
252 if (!left
->ms
.sym
|| !right
->ms
.sym
)
253 return cmp_null(left
->ms
.sym
, right
->ms
.sym
);
255 return strcmp(right
->ms
.sym
->name
, left
->ms
.sym
->name
);
258 static int _hist_entry__sym_snprintf(struct map
*map
, struct symbol
*sym
,
259 u64 ip
, char level
, char *bf
, size_t size
,
265 char o
= map
? dso__symtab_origin(map
->dso
) : '!';
266 ret
+= repsep_snprintf(bf
, size
, "%-#*llx %c ",
267 BITS_PER_LONG
/ 4 + 2, ip
, o
);
270 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "[%c] ", level
);
272 if (map
->type
== MAP__VARIABLE
) {
273 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%s", sym
->name
);
274 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "+0x%llx",
275 ip
- map
->unmap_ip(map
, sym
->start
));
277 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%.*s",
282 size_t len
= BITS_PER_LONG
/ 4;
283 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-#.*llx",
290 static int hist_entry__sym_snprintf(struct hist_entry
*he
, char *bf
,
291 size_t size
, unsigned int width
)
293 return _hist_entry__sym_snprintf(he
->ms
.map
, he
->ms
.sym
, he
->ip
,
294 he
->level
, bf
, size
, width
);
297 static int hist_entry__sym_filter(struct hist_entry
*he
, int type
, const void *arg
)
299 const char *sym
= arg
;
301 if (type
!= HIST_FILTER__SYMBOL
)
304 return sym
&& (!he
->ms
.sym
|| !strstr(he
->ms
.sym
->name
, sym
));
307 struct sort_entry sort_sym
= {
308 .se_header
= "Symbol",
309 .se_cmp
= sort__sym_cmp
,
310 .se_sort
= sort__sym_sort
,
311 .se_snprintf
= hist_entry__sym_snprintf
,
312 .se_filter
= hist_entry__sym_filter
,
313 .se_width_idx
= HISTC_SYMBOL
,
318 static char *hist_entry__get_srcline(struct hist_entry
*he
)
320 struct map
*map
= he
->ms
.map
;
323 return SRCLINE_UNKNOWN
;
325 return get_srcline(map
->dso
, map__rip_2objdump(map
, he
->ip
),
330 sort__srcline_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
333 left
->srcline
= hist_entry__get_srcline(left
);
335 right
->srcline
= hist_entry__get_srcline(right
);
337 return strcmp(right
->srcline
, left
->srcline
);
340 static int hist_entry__srcline_snprintf(struct hist_entry
*he
, char *bf
,
341 size_t size
, unsigned int width
)
344 he
->srcline
= hist_entry__get_srcline(he
);
346 return repsep_snprintf(bf
, size
, "%-.*s", width
, he
->srcline
);
349 struct sort_entry sort_srcline
= {
350 .se_header
= "Source:Line",
351 .se_cmp
= sort__srcline_cmp
,
352 .se_snprintf
= hist_entry__srcline_snprintf
,
353 .se_width_idx
= HISTC_SRCLINE
,
356 /* --sort srcline_from */
359 sort__srcline_from_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
361 if (!left
->branch_info
->srcline_from
) {
362 struct map
*map
= left
->branch_info
->from
.map
;
364 left
->branch_info
->srcline_from
= SRCLINE_UNKNOWN
;
366 left
->branch_info
->srcline_from
= get_srcline(map
->dso
,
367 map__rip_2objdump(map
,
368 left
->branch_info
->from
.al_addr
),
369 left
->branch_info
->from
.sym
, true);
371 if (!right
->branch_info
->srcline_from
) {
372 struct map
*map
= right
->branch_info
->from
.map
;
374 right
->branch_info
->srcline_from
= SRCLINE_UNKNOWN
;
376 right
->branch_info
->srcline_from
= get_srcline(map
->dso
,
377 map__rip_2objdump(map
,
378 right
->branch_info
->from
.al_addr
),
379 right
->branch_info
->from
.sym
, true);
381 return strcmp(right
->branch_info
->srcline_from
, left
->branch_info
->srcline_from
);
384 static int hist_entry__srcline_from_snprintf(struct hist_entry
*he
, char *bf
,
385 size_t size
, unsigned int width
)
387 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, he
->branch_info
->srcline_from
);
390 struct sort_entry sort_srcline_from
= {
391 .se_header
= "From Source:Line",
392 .se_cmp
= sort__srcline_from_cmp
,
393 .se_snprintf
= hist_entry__srcline_from_snprintf
,
394 .se_width_idx
= HISTC_SRCLINE_FROM
,
397 /* --sort srcline_to */
400 sort__srcline_to_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
402 if (!left
->branch_info
->srcline_to
) {
403 struct map
*map
= left
->branch_info
->to
.map
;
405 left
->branch_info
->srcline_to
= SRCLINE_UNKNOWN
;
407 left
->branch_info
->srcline_to
= get_srcline(map
->dso
,
408 map__rip_2objdump(map
,
409 left
->branch_info
->to
.al_addr
),
410 left
->branch_info
->from
.sym
, true);
412 if (!right
->branch_info
->srcline_to
) {
413 struct map
*map
= right
->branch_info
->to
.map
;
415 right
->branch_info
->srcline_to
= SRCLINE_UNKNOWN
;
417 right
->branch_info
->srcline_to
= get_srcline(map
->dso
,
418 map__rip_2objdump(map
,
419 right
->branch_info
->to
.al_addr
),
420 right
->branch_info
->to
.sym
, true);
422 return strcmp(right
->branch_info
->srcline_to
, left
->branch_info
->srcline_to
);
425 static int hist_entry__srcline_to_snprintf(struct hist_entry
*he
, char *bf
,
426 size_t size
, unsigned int width
)
428 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, he
->branch_info
->srcline_to
);
431 struct sort_entry sort_srcline_to
= {
432 .se_header
= "To Source:Line",
433 .se_cmp
= sort__srcline_to_cmp
,
434 .se_snprintf
= hist_entry__srcline_to_snprintf
,
435 .se_width_idx
= HISTC_SRCLINE_TO
,
440 static char no_srcfile
[1];
442 static char *hist_entry__get_srcfile(struct hist_entry
*e
)
445 struct map
*map
= e
->ms
.map
;
450 sf
= __get_srcline(map
->dso
, map__rip_2objdump(map
, e
->ip
),
451 e
->ms
.sym
, false, true);
452 if (!strcmp(sf
, SRCLINE_UNKNOWN
))
464 sort__srcfile_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
467 left
->srcfile
= hist_entry__get_srcfile(left
);
469 right
->srcfile
= hist_entry__get_srcfile(right
);
471 return strcmp(right
->srcfile
, left
->srcfile
);
474 static int hist_entry__srcfile_snprintf(struct hist_entry
*he
, char *bf
,
475 size_t size
, unsigned int width
)
478 he
->srcfile
= hist_entry__get_srcfile(he
);
480 return repsep_snprintf(bf
, size
, "%-.*s", width
, he
->srcfile
);
483 struct sort_entry sort_srcfile
= {
484 .se_header
= "Source File",
485 .se_cmp
= sort__srcfile_cmp
,
486 .se_snprintf
= hist_entry__srcfile_snprintf
,
487 .se_width_idx
= HISTC_SRCFILE
,
493 sort__parent_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
495 struct symbol
*sym_l
= left
->parent
;
496 struct symbol
*sym_r
= right
->parent
;
498 if (!sym_l
|| !sym_r
)
499 return cmp_null(sym_l
, sym_r
);
501 return strcmp(sym_r
->name
, sym_l
->name
);
504 static int hist_entry__parent_snprintf(struct hist_entry
*he
, char *bf
,
505 size_t size
, unsigned int width
)
507 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
,
508 he
->parent
? he
->parent
->name
: "[other]");
511 struct sort_entry sort_parent
= {
512 .se_header
= "Parent symbol",
513 .se_cmp
= sort__parent_cmp
,
514 .se_snprintf
= hist_entry__parent_snprintf
,
515 .se_width_idx
= HISTC_PARENT
,
521 sort__cpu_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
523 return right
->cpu
- left
->cpu
;
526 static int hist_entry__cpu_snprintf(struct hist_entry
*he
, char *bf
,
527 size_t size
, unsigned int width
)
529 return repsep_snprintf(bf
, size
, "%*.*d", width
, width
, he
->cpu
);
532 struct sort_entry sort_cpu
= {
534 .se_cmp
= sort__cpu_cmp
,
535 .se_snprintf
= hist_entry__cpu_snprintf
,
536 .se_width_idx
= HISTC_CPU
,
542 sort__socket_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
544 return right
->socket
- left
->socket
;
547 static int hist_entry__socket_snprintf(struct hist_entry
*he
, char *bf
,
548 size_t size
, unsigned int width
)
550 return repsep_snprintf(bf
, size
, "%*.*d", width
, width
-3, he
->socket
);
553 static int hist_entry__socket_filter(struct hist_entry
*he
, int type
, const void *arg
)
555 int sk
= *(const int *)arg
;
557 if (type
!= HIST_FILTER__SOCKET
)
560 return sk
>= 0 && he
->socket
!= sk
;
563 struct sort_entry sort_socket
= {
564 .se_header
= "Socket",
565 .se_cmp
= sort__socket_cmp
,
566 .se_snprintf
= hist_entry__socket_snprintf
,
567 .se_filter
= hist_entry__socket_filter
,
568 .se_width_idx
= HISTC_SOCKET
,
573 static char *get_trace_output(struct hist_entry
*he
)
575 struct trace_seq seq
;
576 struct perf_evsel
*evsel
;
577 struct pevent_record rec
= {
578 .data
= he
->raw_data
,
579 .size
= he
->raw_size
,
582 evsel
= hists_to_evsel(he
->hists
);
584 trace_seq_init(&seq
);
585 if (symbol_conf
.raw_trace
) {
586 pevent_print_fields(&seq
, he
->raw_data
, he
->raw_size
,
589 pevent_event_info(&seq
, evsel
->tp_format
, &rec
);
592 * Trim the buffer, it starts at 4KB and we're not going to
593 * add anything more to this buffer.
595 return realloc(seq
.buffer
, seq
.len
+ 1);
599 sort__trace_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
601 struct perf_evsel
*evsel
;
603 evsel
= hists_to_evsel(left
->hists
);
604 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
)
607 if (left
->trace_output
== NULL
)
608 left
->trace_output
= get_trace_output(left
);
609 if (right
->trace_output
== NULL
)
610 right
->trace_output
= get_trace_output(right
);
612 return strcmp(right
->trace_output
, left
->trace_output
);
615 static int hist_entry__trace_snprintf(struct hist_entry
*he
, char *bf
,
616 size_t size
, unsigned int width
)
618 struct perf_evsel
*evsel
;
620 evsel
= hists_to_evsel(he
->hists
);
621 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
)
622 return scnprintf(bf
, size
, "%-.*s", width
, "N/A");
624 if (he
->trace_output
== NULL
)
625 he
->trace_output
= get_trace_output(he
);
626 return repsep_snprintf(bf
, size
, "%-.*s", width
, he
->trace_output
);
629 struct sort_entry sort_trace
= {
630 .se_header
= "Trace output",
631 .se_cmp
= sort__trace_cmp
,
632 .se_snprintf
= hist_entry__trace_snprintf
,
633 .se_width_idx
= HISTC_TRACE
,
636 /* sort keys for branch stacks */
639 sort__dso_from_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
641 if (!left
->branch_info
|| !right
->branch_info
)
642 return cmp_null(left
->branch_info
, right
->branch_info
);
644 return _sort__dso_cmp(left
->branch_info
->from
.map
,
645 right
->branch_info
->from
.map
);
648 static int hist_entry__dso_from_snprintf(struct hist_entry
*he
, char *bf
,
649 size_t size
, unsigned int width
)
652 return _hist_entry__dso_snprintf(he
->branch_info
->from
.map
,
655 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, "N/A");
658 static int hist_entry__dso_from_filter(struct hist_entry
*he
, int type
,
661 const struct dso
*dso
= arg
;
663 if (type
!= HIST_FILTER__DSO
)
666 return dso
&& (!he
->branch_info
|| !he
->branch_info
->from
.map
||
667 he
->branch_info
->from
.map
->dso
!= dso
);
671 sort__dso_to_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
673 if (!left
->branch_info
|| !right
->branch_info
)
674 return cmp_null(left
->branch_info
, right
->branch_info
);
676 return _sort__dso_cmp(left
->branch_info
->to
.map
,
677 right
->branch_info
->to
.map
);
680 static int hist_entry__dso_to_snprintf(struct hist_entry
*he
, char *bf
,
681 size_t size
, unsigned int width
)
684 return _hist_entry__dso_snprintf(he
->branch_info
->to
.map
,
687 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, "N/A");
690 static int hist_entry__dso_to_filter(struct hist_entry
*he
, int type
,
693 const struct dso
*dso
= arg
;
695 if (type
!= HIST_FILTER__DSO
)
698 return dso
&& (!he
->branch_info
|| !he
->branch_info
->to
.map
||
699 he
->branch_info
->to
.map
->dso
!= dso
);
703 sort__sym_from_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
705 struct addr_map_symbol
*from_l
= &left
->branch_info
->from
;
706 struct addr_map_symbol
*from_r
= &right
->branch_info
->from
;
708 if (!left
->branch_info
|| !right
->branch_info
)
709 return cmp_null(left
->branch_info
, right
->branch_info
);
711 from_l
= &left
->branch_info
->from
;
712 from_r
= &right
->branch_info
->from
;
714 if (!from_l
->sym
&& !from_r
->sym
)
715 return _sort__addr_cmp(from_l
->addr
, from_r
->addr
);
717 return _sort__sym_cmp(from_l
->sym
, from_r
->sym
);
721 sort__sym_to_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
723 struct addr_map_symbol
*to_l
, *to_r
;
725 if (!left
->branch_info
|| !right
->branch_info
)
726 return cmp_null(left
->branch_info
, right
->branch_info
);
728 to_l
= &left
->branch_info
->to
;
729 to_r
= &right
->branch_info
->to
;
731 if (!to_l
->sym
&& !to_r
->sym
)
732 return _sort__addr_cmp(to_l
->addr
, to_r
->addr
);
734 return _sort__sym_cmp(to_l
->sym
, to_r
->sym
);
737 static int hist_entry__sym_from_snprintf(struct hist_entry
*he
, char *bf
,
738 size_t size
, unsigned int width
)
740 if (he
->branch_info
) {
741 struct addr_map_symbol
*from
= &he
->branch_info
->from
;
743 return _hist_entry__sym_snprintf(from
->map
, from
->sym
, from
->addr
,
744 he
->level
, bf
, size
, width
);
747 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, "N/A");
750 static int hist_entry__sym_to_snprintf(struct hist_entry
*he
, char *bf
,
751 size_t size
, unsigned int width
)
753 if (he
->branch_info
) {
754 struct addr_map_symbol
*to
= &he
->branch_info
->to
;
756 return _hist_entry__sym_snprintf(to
->map
, to
->sym
, to
->addr
,
757 he
->level
, bf
, size
, width
);
760 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, "N/A");
763 static int hist_entry__sym_from_filter(struct hist_entry
*he
, int type
,
766 const char *sym
= arg
;
768 if (type
!= HIST_FILTER__SYMBOL
)
771 return sym
&& !(he
->branch_info
&& he
->branch_info
->from
.sym
&&
772 strstr(he
->branch_info
->from
.sym
->name
, sym
));
775 static int hist_entry__sym_to_filter(struct hist_entry
*he
, int type
,
778 const char *sym
= arg
;
780 if (type
!= HIST_FILTER__SYMBOL
)
783 return sym
&& !(he
->branch_info
&& he
->branch_info
->to
.sym
&&
784 strstr(he
->branch_info
->to
.sym
->name
, sym
));
787 struct sort_entry sort_dso_from
= {
788 .se_header
= "Source Shared Object",
789 .se_cmp
= sort__dso_from_cmp
,
790 .se_snprintf
= hist_entry__dso_from_snprintf
,
791 .se_filter
= hist_entry__dso_from_filter
,
792 .se_width_idx
= HISTC_DSO_FROM
,
795 struct sort_entry sort_dso_to
= {
796 .se_header
= "Target Shared Object",
797 .se_cmp
= sort__dso_to_cmp
,
798 .se_snprintf
= hist_entry__dso_to_snprintf
,
799 .se_filter
= hist_entry__dso_to_filter
,
800 .se_width_idx
= HISTC_DSO_TO
,
803 struct sort_entry sort_sym_from
= {
804 .se_header
= "Source Symbol",
805 .se_cmp
= sort__sym_from_cmp
,
806 .se_snprintf
= hist_entry__sym_from_snprintf
,
807 .se_filter
= hist_entry__sym_from_filter
,
808 .se_width_idx
= HISTC_SYMBOL_FROM
,
811 struct sort_entry sort_sym_to
= {
812 .se_header
= "Target Symbol",
813 .se_cmp
= sort__sym_to_cmp
,
814 .se_snprintf
= hist_entry__sym_to_snprintf
,
815 .se_filter
= hist_entry__sym_to_filter
,
816 .se_width_idx
= HISTC_SYMBOL_TO
,
820 sort__mispredict_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
824 if (!left
->branch_info
|| !right
->branch_info
)
825 return cmp_null(left
->branch_info
, right
->branch_info
);
827 mp
= left
->branch_info
->flags
.mispred
!= right
->branch_info
->flags
.mispred
;
828 p
= left
->branch_info
->flags
.predicted
!= right
->branch_info
->flags
.predicted
;
832 static int hist_entry__mispredict_snprintf(struct hist_entry
*he
, char *bf
,
833 size_t size
, unsigned int width
){
834 static const char *out
= "N/A";
836 if (he
->branch_info
) {
837 if (he
->branch_info
->flags
.predicted
)
839 else if (he
->branch_info
->flags
.mispred
)
843 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, out
);
847 sort__cycles_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
849 return left
->branch_info
->flags
.cycles
-
850 right
->branch_info
->flags
.cycles
;
853 static int hist_entry__cycles_snprintf(struct hist_entry
*he
, char *bf
,
854 size_t size
, unsigned int width
)
856 if (he
->branch_info
->flags
.cycles
== 0)
857 return repsep_snprintf(bf
, size
, "%-*s", width
, "-");
858 return repsep_snprintf(bf
, size
, "%-*hd", width
,
859 he
->branch_info
->flags
.cycles
);
862 struct sort_entry sort_cycles
= {
863 .se_header
= "Basic Block Cycles",
864 .se_cmp
= sort__cycles_cmp
,
865 .se_snprintf
= hist_entry__cycles_snprintf
,
866 .se_width_idx
= HISTC_CYCLES
,
869 /* --sort daddr_sym */
871 sort__daddr_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
873 uint64_t l
= 0, r
= 0;
876 l
= left
->mem_info
->daddr
.addr
;
878 r
= right
->mem_info
->daddr
.addr
;
880 return (int64_t)(r
- l
);
883 static int hist_entry__daddr_snprintf(struct hist_entry
*he
, char *bf
,
884 size_t size
, unsigned int width
)
887 struct map
*map
= NULL
;
888 struct symbol
*sym
= NULL
;
891 addr
= he
->mem_info
->daddr
.addr
;
892 map
= he
->mem_info
->daddr
.map
;
893 sym
= he
->mem_info
->daddr
.sym
;
895 return _hist_entry__sym_snprintf(map
, sym
, addr
, he
->level
, bf
, size
,
900 sort__iaddr_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
902 uint64_t l
= 0, r
= 0;
905 l
= left
->mem_info
->iaddr
.addr
;
907 r
= right
->mem_info
->iaddr
.addr
;
909 return (int64_t)(r
- l
);
912 static int hist_entry__iaddr_snprintf(struct hist_entry
*he
, char *bf
,
913 size_t size
, unsigned int width
)
916 struct map
*map
= NULL
;
917 struct symbol
*sym
= NULL
;
920 addr
= he
->mem_info
->iaddr
.addr
;
921 map
= he
->mem_info
->iaddr
.map
;
922 sym
= he
->mem_info
->iaddr
.sym
;
924 return _hist_entry__sym_snprintf(map
, sym
, addr
, he
->level
, bf
, size
,
929 sort__dso_daddr_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
931 struct map
*map_l
= NULL
;
932 struct map
*map_r
= NULL
;
935 map_l
= left
->mem_info
->daddr
.map
;
937 map_r
= right
->mem_info
->daddr
.map
;
939 return _sort__dso_cmp(map_l
, map_r
);
942 static int hist_entry__dso_daddr_snprintf(struct hist_entry
*he
, char *bf
,
943 size_t size
, unsigned int width
)
945 struct map
*map
= NULL
;
948 map
= he
->mem_info
->daddr
.map
;
950 return _hist_entry__dso_snprintf(map
, bf
, size
, width
);
954 sort__locked_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
956 union perf_mem_data_src data_src_l
;
957 union perf_mem_data_src data_src_r
;
960 data_src_l
= left
->mem_info
->data_src
;
962 data_src_l
.mem_lock
= PERF_MEM_LOCK_NA
;
965 data_src_r
= right
->mem_info
->data_src
;
967 data_src_r
.mem_lock
= PERF_MEM_LOCK_NA
;
969 return (int64_t)(data_src_r
.mem_lock
- data_src_l
.mem_lock
);
972 static int hist_entry__locked_snprintf(struct hist_entry
*he
, char *bf
,
973 size_t size
, unsigned int width
)
977 perf_mem__lck_scnprintf(out
, sizeof(out
), he
->mem_info
);
978 return repsep_snprintf(bf
, size
, "%.*s", width
, out
);
982 sort__tlb_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
984 union perf_mem_data_src data_src_l
;
985 union perf_mem_data_src data_src_r
;
988 data_src_l
= left
->mem_info
->data_src
;
990 data_src_l
.mem_dtlb
= PERF_MEM_TLB_NA
;
993 data_src_r
= right
->mem_info
->data_src
;
995 data_src_r
.mem_dtlb
= PERF_MEM_TLB_NA
;
997 return (int64_t)(data_src_r
.mem_dtlb
- data_src_l
.mem_dtlb
);
1000 static int hist_entry__tlb_snprintf(struct hist_entry
*he
, char *bf
,
1001 size_t size
, unsigned int width
)
1005 perf_mem__tlb_scnprintf(out
, sizeof(out
), he
->mem_info
);
1006 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
1010 sort__lvl_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1012 union perf_mem_data_src data_src_l
;
1013 union perf_mem_data_src data_src_r
;
1016 data_src_l
= left
->mem_info
->data_src
;
1018 data_src_l
.mem_lvl
= PERF_MEM_LVL_NA
;
1020 if (right
->mem_info
)
1021 data_src_r
= right
->mem_info
->data_src
;
1023 data_src_r
.mem_lvl
= PERF_MEM_LVL_NA
;
1025 return (int64_t)(data_src_r
.mem_lvl
- data_src_l
.mem_lvl
);
1028 static int hist_entry__lvl_snprintf(struct hist_entry
*he
, char *bf
,
1029 size_t size
, unsigned int width
)
1033 perf_mem__lvl_scnprintf(out
, sizeof(out
), he
->mem_info
);
1034 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
1038 sort__snoop_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1040 union perf_mem_data_src data_src_l
;
1041 union perf_mem_data_src data_src_r
;
1044 data_src_l
= left
->mem_info
->data_src
;
1046 data_src_l
.mem_snoop
= PERF_MEM_SNOOP_NA
;
1048 if (right
->mem_info
)
1049 data_src_r
= right
->mem_info
->data_src
;
1051 data_src_r
.mem_snoop
= PERF_MEM_SNOOP_NA
;
1053 return (int64_t)(data_src_r
.mem_snoop
- data_src_l
.mem_snoop
);
1056 static int hist_entry__snoop_snprintf(struct hist_entry
*he
, char *bf
,
1057 size_t size
, unsigned int width
)
1061 perf_mem__snp_scnprintf(out
, sizeof(out
), he
->mem_info
);
1062 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
1066 sort__dcacheline_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1069 struct map
*l_map
, *r_map
;
1071 if (!left
->mem_info
) return -1;
1072 if (!right
->mem_info
) return 1;
1074 /* group event types together */
1075 if (left
->cpumode
> right
->cpumode
) return -1;
1076 if (left
->cpumode
< right
->cpumode
) return 1;
1078 l_map
= left
->mem_info
->daddr
.map
;
1079 r_map
= right
->mem_info
->daddr
.map
;
1081 /* if both are NULL, jump to sort on al_addr instead */
1082 if (!l_map
&& !r_map
)
1085 if (!l_map
) return -1;
1086 if (!r_map
) return 1;
1088 if (l_map
->maj
> r_map
->maj
) return -1;
1089 if (l_map
->maj
< r_map
->maj
) return 1;
1091 if (l_map
->min
> r_map
->min
) return -1;
1092 if (l_map
->min
< r_map
->min
) return 1;
1094 if (l_map
->ino
> r_map
->ino
) return -1;
1095 if (l_map
->ino
< r_map
->ino
) return 1;
1097 if (l_map
->ino_generation
> r_map
->ino_generation
) return -1;
1098 if (l_map
->ino_generation
< r_map
->ino_generation
) return 1;
1101 * Addresses with no major/minor numbers are assumed to be
1102 * anonymous in userspace. Sort those on pid then address.
1104 * The kernel and non-zero major/minor mapped areas are
1105 * assumed to be unity mapped. Sort those on address.
1108 if ((left
->cpumode
!= PERF_RECORD_MISC_KERNEL
) &&
1109 (!(l_map
->flags
& MAP_SHARED
)) &&
1110 !l_map
->maj
&& !l_map
->min
&& !l_map
->ino
&&
1111 !l_map
->ino_generation
) {
1112 /* userspace anonymous */
1114 if (left
->thread
->pid_
> right
->thread
->pid_
) return -1;
1115 if (left
->thread
->pid_
< right
->thread
->pid_
) return 1;
1119 /* al_addr does all the right addr - start + offset calculations */
1120 l
= cl_address(left
->mem_info
->daddr
.al_addr
);
1121 r
= cl_address(right
->mem_info
->daddr
.al_addr
);
1123 if (l
> r
) return -1;
1124 if (l
< r
) return 1;
1129 static int hist_entry__dcacheline_snprintf(struct hist_entry
*he
, char *bf
,
1130 size_t size
, unsigned int width
)
1134 struct map
*map
= NULL
;
1135 struct symbol
*sym
= NULL
;
1136 char level
= he
->level
;
1139 addr
= cl_address(he
->mem_info
->daddr
.al_addr
);
1140 map
= he
->mem_info
->daddr
.map
;
1141 sym
= he
->mem_info
->daddr
.sym
;
1143 /* print [s] for shared data mmaps */
1144 if ((he
->cpumode
!= PERF_RECORD_MISC_KERNEL
) &&
1145 map
&& (map
->type
== MAP__VARIABLE
) &&
1146 (map
->flags
& MAP_SHARED
) &&
1147 (map
->maj
|| map
->min
|| map
->ino
||
1148 map
->ino_generation
))
1153 return _hist_entry__sym_snprintf(map
, sym
, addr
, level
, bf
, size
,
1157 struct sort_entry sort_mispredict
= {
1158 .se_header
= "Branch Mispredicted",
1159 .se_cmp
= sort__mispredict_cmp
,
1160 .se_snprintf
= hist_entry__mispredict_snprintf
,
1161 .se_width_idx
= HISTC_MISPREDICT
,
1164 static u64
he_weight(struct hist_entry
*he
)
1166 return he
->stat
.nr_events
? he
->stat
.weight
/ he
->stat
.nr_events
: 0;
1170 sort__local_weight_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1172 return he_weight(left
) - he_weight(right
);
1175 static int hist_entry__local_weight_snprintf(struct hist_entry
*he
, char *bf
,
1176 size_t size
, unsigned int width
)
1178 return repsep_snprintf(bf
, size
, "%-*llu", width
, he_weight(he
));
1181 struct sort_entry sort_local_weight
= {
1182 .se_header
= "Local Weight",
1183 .se_cmp
= sort__local_weight_cmp
,
1184 .se_snprintf
= hist_entry__local_weight_snprintf
,
1185 .se_width_idx
= HISTC_LOCAL_WEIGHT
,
1189 sort__global_weight_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1191 return left
->stat
.weight
- right
->stat
.weight
;
1194 static int hist_entry__global_weight_snprintf(struct hist_entry
*he
, char *bf
,
1195 size_t size
, unsigned int width
)
1197 return repsep_snprintf(bf
, size
, "%-*llu", width
, he
->stat
.weight
);
1200 struct sort_entry sort_global_weight
= {
1201 .se_header
= "Weight",
1202 .se_cmp
= sort__global_weight_cmp
,
1203 .se_snprintf
= hist_entry__global_weight_snprintf
,
1204 .se_width_idx
= HISTC_GLOBAL_WEIGHT
,
1207 struct sort_entry sort_mem_daddr_sym
= {
1208 .se_header
= "Data Symbol",
1209 .se_cmp
= sort__daddr_cmp
,
1210 .se_snprintf
= hist_entry__daddr_snprintf
,
1211 .se_width_idx
= HISTC_MEM_DADDR_SYMBOL
,
1214 struct sort_entry sort_mem_iaddr_sym
= {
1215 .se_header
= "Code Symbol",
1216 .se_cmp
= sort__iaddr_cmp
,
1217 .se_snprintf
= hist_entry__iaddr_snprintf
,
1218 .se_width_idx
= HISTC_MEM_IADDR_SYMBOL
,
1221 struct sort_entry sort_mem_daddr_dso
= {
1222 .se_header
= "Data Object",
1223 .se_cmp
= sort__dso_daddr_cmp
,
1224 .se_snprintf
= hist_entry__dso_daddr_snprintf
,
1225 .se_width_idx
= HISTC_MEM_DADDR_DSO
,
1228 struct sort_entry sort_mem_locked
= {
1229 .se_header
= "Locked",
1230 .se_cmp
= sort__locked_cmp
,
1231 .se_snprintf
= hist_entry__locked_snprintf
,
1232 .se_width_idx
= HISTC_MEM_LOCKED
,
1235 struct sort_entry sort_mem_tlb
= {
1236 .se_header
= "TLB access",
1237 .se_cmp
= sort__tlb_cmp
,
1238 .se_snprintf
= hist_entry__tlb_snprintf
,
1239 .se_width_idx
= HISTC_MEM_TLB
,
1242 struct sort_entry sort_mem_lvl
= {
1243 .se_header
= "Memory access",
1244 .se_cmp
= sort__lvl_cmp
,
1245 .se_snprintf
= hist_entry__lvl_snprintf
,
1246 .se_width_idx
= HISTC_MEM_LVL
,
1249 struct sort_entry sort_mem_snoop
= {
1250 .se_header
= "Snoop",
1251 .se_cmp
= sort__snoop_cmp
,
1252 .se_snprintf
= hist_entry__snoop_snprintf
,
1253 .se_width_idx
= HISTC_MEM_SNOOP
,
1256 struct sort_entry sort_mem_dcacheline
= {
1257 .se_header
= "Data Cacheline",
1258 .se_cmp
= sort__dcacheline_cmp
,
1259 .se_snprintf
= hist_entry__dcacheline_snprintf
,
1260 .se_width_idx
= HISTC_MEM_DCACHELINE
,
1264 sort__abort_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1266 if (!left
->branch_info
|| !right
->branch_info
)
1267 return cmp_null(left
->branch_info
, right
->branch_info
);
1269 return left
->branch_info
->flags
.abort
!=
1270 right
->branch_info
->flags
.abort
;
1273 static int hist_entry__abort_snprintf(struct hist_entry
*he
, char *bf
,
1274 size_t size
, unsigned int width
)
1276 static const char *out
= "N/A";
1278 if (he
->branch_info
) {
1279 if (he
->branch_info
->flags
.abort
)
1285 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
1288 struct sort_entry sort_abort
= {
1289 .se_header
= "Transaction abort",
1290 .se_cmp
= sort__abort_cmp
,
1291 .se_snprintf
= hist_entry__abort_snprintf
,
1292 .se_width_idx
= HISTC_ABORT
,
1296 sort__in_tx_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1298 if (!left
->branch_info
|| !right
->branch_info
)
1299 return cmp_null(left
->branch_info
, right
->branch_info
);
1301 return left
->branch_info
->flags
.in_tx
!=
1302 right
->branch_info
->flags
.in_tx
;
1305 static int hist_entry__in_tx_snprintf(struct hist_entry
*he
, char *bf
,
1306 size_t size
, unsigned int width
)
1308 static const char *out
= "N/A";
1310 if (he
->branch_info
) {
1311 if (he
->branch_info
->flags
.in_tx
)
1317 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
1320 struct sort_entry sort_in_tx
= {
1321 .se_header
= "Branch in transaction",
1322 .se_cmp
= sort__in_tx_cmp
,
1323 .se_snprintf
= hist_entry__in_tx_snprintf
,
1324 .se_width_idx
= HISTC_IN_TX
,
1328 sort__transaction_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1330 return left
->transaction
- right
->transaction
;
1333 static inline char *add_str(char *p
, const char *str
)
1336 return p
+ strlen(str
);
1339 static struct txbit
{
1344 { PERF_TXN_ELISION
, "EL ", 0 },
1345 { PERF_TXN_TRANSACTION
, "TX ", 1 },
1346 { PERF_TXN_SYNC
, "SYNC ", 1 },
1347 { PERF_TXN_ASYNC
, "ASYNC ", 0 },
1348 { PERF_TXN_RETRY
, "RETRY ", 0 },
1349 { PERF_TXN_CONFLICT
, "CON ", 0 },
1350 { PERF_TXN_CAPACITY_WRITE
, "CAP-WRITE ", 1 },
1351 { PERF_TXN_CAPACITY_READ
, "CAP-READ ", 0 },
1355 int hist_entry__transaction_len(void)
1360 for (i
= 0; txbits
[i
].name
; i
++) {
1361 if (!txbits
[i
].skip_for_len
)
1362 len
+= strlen(txbits
[i
].name
);
1364 len
+= 4; /* :XX<space> */
1368 static int hist_entry__transaction_snprintf(struct hist_entry
*he
, char *bf
,
1369 size_t size
, unsigned int width
)
1371 u64 t
= he
->transaction
;
1377 for (i
= 0; txbits
[i
].name
; i
++)
1378 if (txbits
[i
].flag
& t
)
1379 p
= add_str(p
, txbits
[i
].name
);
1380 if (t
&& !(t
& (PERF_TXN_SYNC
|PERF_TXN_ASYNC
)))
1381 p
= add_str(p
, "NEITHER ");
1382 if (t
& PERF_TXN_ABORT_MASK
) {
1383 sprintf(p
, ":%" PRIx64
,
1384 (t
& PERF_TXN_ABORT_MASK
) >>
1385 PERF_TXN_ABORT_SHIFT
);
1389 return repsep_snprintf(bf
, size
, "%-*s", width
, buf
);
1392 struct sort_entry sort_transaction
= {
1393 .se_header
= "Transaction ",
1394 .se_cmp
= sort__transaction_cmp
,
1395 .se_snprintf
= hist_entry__transaction_snprintf
,
1396 .se_width_idx
= HISTC_TRANSACTION
,
1399 struct sort_dimension
{
1401 struct sort_entry
*entry
;
1405 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
1407 static struct sort_dimension common_sort_dimensions
[] = {
1408 DIM(SORT_PID
, "pid", sort_thread
),
1409 DIM(SORT_COMM
, "comm", sort_comm
),
1410 DIM(SORT_DSO
, "dso", sort_dso
),
1411 DIM(SORT_SYM
, "symbol", sort_sym
),
1412 DIM(SORT_PARENT
, "parent", sort_parent
),
1413 DIM(SORT_CPU
, "cpu", sort_cpu
),
1414 DIM(SORT_SOCKET
, "socket", sort_socket
),
1415 DIM(SORT_SRCLINE
, "srcline", sort_srcline
),
1416 DIM(SORT_SRCFILE
, "srcfile", sort_srcfile
),
1417 DIM(SORT_LOCAL_WEIGHT
, "local_weight", sort_local_weight
),
1418 DIM(SORT_GLOBAL_WEIGHT
, "weight", sort_global_weight
),
1419 DIM(SORT_TRANSACTION
, "transaction", sort_transaction
),
1420 DIM(SORT_TRACE
, "trace", sort_trace
),
1425 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
1427 static struct sort_dimension bstack_sort_dimensions
[] = {
1428 DIM(SORT_DSO_FROM
, "dso_from", sort_dso_from
),
1429 DIM(SORT_DSO_TO
, "dso_to", sort_dso_to
),
1430 DIM(SORT_SYM_FROM
, "symbol_from", sort_sym_from
),
1431 DIM(SORT_SYM_TO
, "symbol_to", sort_sym_to
),
1432 DIM(SORT_MISPREDICT
, "mispredict", sort_mispredict
),
1433 DIM(SORT_IN_TX
, "in_tx", sort_in_tx
),
1434 DIM(SORT_ABORT
, "abort", sort_abort
),
1435 DIM(SORT_CYCLES
, "cycles", sort_cycles
),
1436 DIM(SORT_SRCLINE_FROM
, "srcline_from", sort_srcline_from
),
1437 DIM(SORT_SRCLINE_TO
, "srcline_to", sort_srcline_to
),
1442 #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
1444 static struct sort_dimension memory_sort_dimensions
[] = {
1445 DIM(SORT_MEM_DADDR_SYMBOL
, "symbol_daddr", sort_mem_daddr_sym
),
1446 DIM(SORT_MEM_IADDR_SYMBOL
, "symbol_iaddr", sort_mem_iaddr_sym
),
1447 DIM(SORT_MEM_DADDR_DSO
, "dso_daddr", sort_mem_daddr_dso
),
1448 DIM(SORT_MEM_LOCKED
, "locked", sort_mem_locked
),
1449 DIM(SORT_MEM_TLB
, "tlb", sort_mem_tlb
),
1450 DIM(SORT_MEM_LVL
, "mem", sort_mem_lvl
),
1451 DIM(SORT_MEM_SNOOP
, "snoop", sort_mem_snoop
),
1452 DIM(SORT_MEM_DCACHELINE
, "dcacheline", sort_mem_dcacheline
),
1457 struct hpp_dimension
{
1459 struct perf_hpp_fmt
*fmt
;
1463 #define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
1465 static struct hpp_dimension hpp_sort_dimensions
[] = {
1466 DIM(PERF_HPP__OVERHEAD
, "overhead"),
1467 DIM(PERF_HPP__OVERHEAD_SYS
, "overhead_sys"),
1468 DIM(PERF_HPP__OVERHEAD_US
, "overhead_us"),
1469 DIM(PERF_HPP__OVERHEAD_GUEST_SYS
, "overhead_guest_sys"),
1470 DIM(PERF_HPP__OVERHEAD_GUEST_US
, "overhead_guest_us"),
1471 DIM(PERF_HPP__OVERHEAD_ACC
, "overhead_children"),
1472 DIM(PERF_HPP__SAMPLES
, "sample"),
1473 DIM(PERF_HPP__PERIOD
, "period"),
1478 struct hpp_sort_entry
{
1479 struct perf_hpp_fmt hpp
;
1480 struct sort_entry
*se
;
1483 void perf_hpp__reset_sort_width(struct perf_hpp_fmt
*fmt
, struct hists
*hists
)
1485 struct hpp_sort_entry
*hse
;
1487 if (!perf_hpp__is_sort_entry(fmt
))
1490 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1491 hists__new_col_len(hists
, hse
->se
->se_width_idx
, strlen(fmt
->name
));
1494 static int __sort__hpp_header(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
1495 struct hists
*hists
)
1497 struct hpp_sort_entry
*hse
;
1498 size_t len
= fmt
->user_len
;
1500 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1503 len
= hists__col_len(hists
, hse
->se
->se_width_idx
);
1505 return scnprintf(hpp
->buf
, hpp
->size
, "%-*.*s", len
, len
, fmt
->name
);
1508 static int __sort__hpp_width(struct perf_hpp_fmt
*fmt
,
1509 struct perf_hpp
*hpp __maybe_unused
,
1510 struct hists
*hists
)
1512 struct hpp_sort_entry
*hse
;
1513 size_t len
= fmt
->user_len
;
1515 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1518 len
= hists__col_len(hists
, hse
->se
->se_width_idx
);
1523 static int __sort__hpp_entry(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
1524 struct hist_entry
*he
)
1526 struct hpp_sort_entry
*hse
;
1527 size_t len
= fmt
->user_len
;
1529 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1532 len
= hists__col_len(he
->hists
, hse
->se
->se_width_idx
);
1534 return hse
->se
->se_snprintf(he
, hpp
->buf
, hpp
->size
, len
);
1537 static int64_t __sort__hpp_cmp(struct perf_hpp_fmt
*fmt
,
1538 struct hist_entry
*a
, struct hist_entry
*b
)
1540 struct hpp_sort_entry
*hse
;
1542 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1543 return hse
->se
->se_cmp(a
, b
);
1546 static int64_t __sort__hpp_collapse(struct perf_hpp_fmt
*fmt
,
1547 struct hist_entry
*a
, struct hist_entry
*b
)
1549 struct hpp_sort_entry
*hse
;
1550 int64_t (*collapse_fn
)(struct hist_entry
*, struct hist_entry
*);
1552 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1553 collapse_fn
= hse
->se
->se_collapse
?: hse
->se
->se_cmp
;
1554 return collapse_fn(a
, b
);
1557 static int64_t __sort__hpp_sort(struct perf_hpp_fmt
*fmt
,
1558 struct hist_entry
*a
, struct hist_entry
*b
)
1560 struct hpp_sort_entry
*hse
;
1561 int64_t (*sort_fn
)(struct hist_entry
*, struct hist_entry
*);
1563 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1564 sort_fn
= hse
->se
->se_sort
?: hse
->se
->se_cmp
;
1565 return sort_fn(a
, b
);
1568 bool perf_hpp__is_sort_entry(struct perf_hpp_fmt
*format
)
1570 return format
->header
== __sort__hpp_header
;
1573 #define MK_SORT_ENTRY_CHK(key) \
1574 bool perf_hpp__is_ ## key ## _entry(struct perf_hpp_fmt *fmt) \
1576 struct hpp_sort_entry *hse; \
1578 if (!perf_hpp__is_sort_entry(fmt)) \
1581 hse = container_of(fmt, struct hpp_sort_entry, hpp); \
1582 return hse->se == &sort_ ## key ; \
1585 MK_SORT_ENTRY_CHK(trace
)
1586 MK_SORT_ENTRY_CHK(srcline
)
1587 MK_SORT_ENTRY_CHK(srcfile
)
1588 MK_SORT_ENTRY_CHK(thread
)
1589 MK_SORT_ENTRY_CHK(comm
)
1590 MK_SORT_ENTRY_CHK(dso
)
1591 MK_SORT_ENTRY_CHK(sym
)
1594 static bool __sort__hpp_equal(struct perf_hpp_fmt
*a
, struct perf_hpp_fmt
*b
)
1596 struct hpp_sort_entry
*hse_a
;
1597 struct hpp_sort_entry
*hse_b
;
1599 if (!perf_hpp__is_sort_entry(a
) || !perf_hpp__is_sort_entry(b
))
1602 hse_a
= container_of(a
, struct hpp_sort_entry
, hpp
);
1603 hse_b
= container_of(b
, struct hpp_sort_entry
, hpp
);
1605 return hse_a
->se
== hse_b
->se
;
1608 static void hse_free(struct perf_hpp_fmt
*fmt
)
1610 struct hpp_sort_entry
*hse
;
1612 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1616 static struct hpp_sort_entry
*
1617 __sort_dimension__alloc_hpp(struct sort_dimension
*sd
, int level
)
1619 struct hpp_sort_entry
*hse
;
1621 hse
= malloc(sizeof(*hse
));
1623 pr_err("Memory allocation failed\n");
1627 hse
->se
= sd
->entry
;
1628 hse
->hpp
.name
= sd
->entry
->se_header
;
1629 hse
->hpp
.header
= __sort__hpp_header
;
1630 hse
->hpp
.width
= __sort__hpp_width
;
1631 hse
->hpp
.entry
= __sort__hpp_entry
;
1632 hse
->hpp
.color
= NULL
;
1634 hse
->hpp
.cmp
= __sort__hpp_cmp
;
1635 hse
->hpp
.collapse
= __sort__hpp_collapse
;
1636 hse
->hpp
.sort
= __sort__hpp_sort
;
1637 hse
->hpp
.equal
= __sort__hpp_equal
;
1638 hse
->hpp
.free
= hse_free
;
1640 INIT_LIST_HEAD(&hse
->hpp
.list
);
1641 INIT_LIST_HEAD(&hse
->hpp
.sort_list
);
1642 hse
->hpp
.elide
= false;
1644 hse
->hpp
.user_len
= 0;
1645 hse
->hpp
.level
= level
;
1650 static void hpp_free(struct perf_hpp_fmt
*fmt
)
1655 static struct perf_hpp_fmt
*__hpp_dimension__alloc_hpp(struct hpp_dimension
*hd
,
1658 struct perf_hpp_fmt
*fmt
;
1660 fmt
= memdup(hd
->fmt
, sizeof(*fmt
));
1662 INIT_LIST_HEAD(&fmt
->list
);
1663 INIT_LIST_HEAD(&fmt
->sort_list
);
1664 fmt
->free
= hpp_free
;
1671 int hist_entry__filter(struct hist_entry
*he
, int type
, const void *arg
)
1673 struct perf_hpp_fmt
*fmt
;
1674 struct hpp_sort_entry
*hse
;
1678 perf_hpp_list__for_each_format(he
->hpp_list
, fmt
) {
1679 if (!perf_hpp__is_sort_entry(fmt
))
1682 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1683 if (hse
->se
->se_filter
== NULL
)
1687 * hist entry is filtered if any of sort key in the hpp list
1688 * is applied. But it should skip non-matched filter types.
1690 r
= hse
->se
->se_filter(he
, type
, arg
);
1701 static int __sort_dimension__add_hpp_sort(struct sort_dimension
*sd
,
1702 struct perf_hpp_list
*list
,
1705 struct hpp_sort_entry
*hse
= __sort_dimension__alloc_hpp(sd
, level
);
1710 perf_hpp_list__register_sort_field(list
, &hse
->hpp
);
1714 static int __sort_dimension__add_hpp_output(struct sort_dimension
*sd
,
1715 struct perf_hpp_list
*list
)
1717 struct hpp_sort_entry
*hse
= __sort_dimension__alloc_hpp(sd
, 0);
1722 perf_hpp_list__column_register(list
, &hse
->hpp
);
1726 struct hpp_dynamic_entry
{
1727 struct perf_hpp_fmt hpp
;
1728 struct perf_evsel
*evsel
;
1729 struct format_field
*field
;
1730 unsigned dynamic_len
;
1734 static int hde_width(struct hpp_dynamic_entry
*hde
)
1736 if (!hde
->hpp
.len
) {
1737 int len
= hde
->dynamic_len
;
1738 int namelen
= strlen(hde
->field
->name
);
1739 int fieldlen
= hde
->field
->size
;
1744 if (!(hde
->field
->flags
& FIELD_IS_STRING
)) {
1745 /* length for print hex numbers */
1746 fieldlen
= hde
->field
->size
* 2 + 2;
1753 return hde
->hpp
.len
;
1756 static void update_dynamic_len(struct hpp_dynamic_entry
*hde
,
1757 struct hist_entry
*he
)
1760 struct format_field
*field
= hde
->field
;
1767 /* parse pretty print result and update max length */
1768 if (!he
->trace_output
)
1769 he
->trace_output
= get_trace_output(he
);
1771 namelen
= strlen(field
->name
);
1772 str
= he
->trace_output
;
1775 pos
= strchr(str
, ' ');
1778 pos
= str
+ strlen(str
);
1781 if (!strncmp(str
, field
->name
, namelen
)) {
1787 if (len
> hde
->dynamic_len
)
1788 hde
->dynamic_len
= len
;
1799 static int __sort__hde_header(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
1800 struct hists
*hists __maybe_unused
)
1802 struct hpp_dynamic_entry
*hde
;
1803 size_t len
= fmt
->user_len
;
1805 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
1808 len
= hde_width(hde
);
1810 return scnprintf(hpp
->buf
, hpp
->size
, "%*.*s", len
, len
, hde
->field
->name
);
1813 static int __sort__hde_width(struct perf_hpp_fmt
*fmt
,
1814 struct perf_hpp
*hpp __maybe_unused
,
1815 struct hists
*hists __maybe_unused
)
1817 struct hpp_dynamic_entry
*hde
;
1818 size_t len
= fmt
->user_len
;
1820 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
1823 len
= hde_width(hde
);
1828 bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt
*fmt
, struct hists
*hists
)
1830 struct hpp_dynamic_entry
*hde
;
1832 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
1834 return hists_to_evsel(hists
) == hde
->evsel
;
1837 static int __sort__hde_entry(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
1838 struct hist_entry
*he
)
1840 struct hpp_dynamic_entry
*hde
;
1841 size_t len
= fmt
->user_len
;
1843 struct format_field
*field
;
1848 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
1851 len
= hde_width(hde
);
1856 if (!he
->trace_output
)
1857 he
->trace_output
= get_trace_output(he
);
1860 namelen
= strlen(field
->name
);
1861 str
= he
->trace_output
;
1864 pos
= strchr(str
, ' ');
1867 pos
= str
+ strlen(str
);
1870 if (!strncmp(str
, field
->name
, namelen
)) {
1872 str
= strndup(str
, pos
- str
);
1875 return scnprintf(hpp
->buf
, hpp
->size
,
1876 "%*.*s", len
, len
, "ERROR");
1887 struct trace_seq seq
;
1889 trace_seq_init(&seq
);
1890 pevent_print_field(&seq
, he
->raw_data
, hde
->field
);
1894 ret
= scnprintf(hpp
->buf
, hpp
->size
, "%*.*s", len
, len
, str
);
1899 static int64_t __sort__hde_cmp(struct perf_hpp_fmt
*fmt
,
1900 struct hist_entry
*a
, struct hist_entry
*b
)
1902 struct hpp_dynamic_entry
*hde
;
1903 struct format_field
*field
;
1904 unsigned offset
, size
;
1906 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
1909 update_dynamic_len(hde
, a
);
1914 if (field
->flags
& FIELD_IS_DYNAMIC
) {
1915 unsigned long long dyn
;
1917 pevent_read_number_field(field
, a
->raw_data
, &dyn
);
1918 offset
= dyn
& 0xffff;
1919 size
= (dyn
>> 16) & 0xffff;
1921 /* record max width for output */
1922 if (size
> hde
->dynamic_len
)
1923 hde
->dynamic_len
= size
;
1925 offset
= field
->offset
;
1929 return memcmp(a
->raw_data
+ offset
, b
->raw_data
+ offset
, size
);
1932 bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt
*fmt
)
1934 return fmt
->cmp
== __sort__hde_cmp
;
1937 static bool __sort__hde_equal(struct perf_hpp_fmt
*a
, struct perf_hpp_fmt
*b
)
1939 struct hpp_dynamic_entry
*hde_a
;
1940 struct hpp_dynamic_entry
*hde_b
;
1942 if (!perf_hpp__is_dynamic_entry(a
) || !perf_hpp__is_dynamic_entry(b
))
1945 hde_a
= container_of(a
, struct hpp_dynamic_entry
, hpp
);
1946 hde_b
= container_of(b
, struct hpp_dynamic_entry
, hpp
);
1948 return hde_a
->field
== hde_b
->field
;
1951 static void hde_free(struct perf_hpp_fmt
*fmt
)
1953 struct hpp_dynamic_entry
*hde
;
1955 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
1959 static struct hpp_dynamic_entry
*
1960 __alloc_dynamic_entry(struct perf_evsel
*evsel
, struct format_field
*field
,
1963 struct hpp_dynamic_entry
*hde
;
1965 hde
= malloc(sizeof(*hde
));
1967 pr_debug("Memory allocation failed\n");
1973 hde
->dynamic_len
= 0;
1975 hde
->hpp
.name
= field
->name
;
1976 hde
->hpp
.header
= __sort__hde_header
;
1977 hde
->hpp
.width
= __sort__hde_width
;
1978 hde
->hpp
.entry
= __sort__hde_entry
;
1979 hde
->hpp
.color
= NULL
;
1981 hde
->hpp
.cmp
= __sort__hde_cmp
;
1982 hde
->hpp
.collapse
= __sort__hde_cmp
;
1983 hde
->hpp
.sort
= __sort__hde_cmp
;
1984 hde
->hpp
.equal
= __sort__hde_equal
;
1985 hde
->hpp
.free
= hde_free
;
1987 INIT_LIST_HEAD(&hde
->hpp
.list
);
1988 INIT_LIST_HEAD(&hde
->hpp
.sort_list
);
1989 hde
->hpp
.elide
= false;
1991 hde
->hpp
.user_len
= 0;
1992 hde
->hpp
.level
= level
;
1997 struct perf_hpp_fmt
*perf_hpp_fmt__dup(struct perf_hpp_fmt
*fmt
)
1999 struct perf_hpp_fmt
*new_fmt
= NULL
;
2001 if (perf_hpp__is_sort_entry(fmt
)) {
2002 struct hpp_sort_entry
*hse
, *new_hse
;
2004 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
2005 new_hse
= memdup(hse
, sizeof(*hse
));
2007 new_fmt
= &new_hse
->hpp
;
2008 } else if (perf_hpp__is_dynamic_entry(fmt
)) {
2009 struct hpp_dynamic_entry
*hde
, *new_hde
;
2011 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
2012 new_hde
= memdup(hde
, sizeof(*hde
));
2014 new_fmt
= &new_hde
->hpp
;
2016 new_fmt
= memdup(fmt
, sizeof(*fmt
));
2019 INIT_LIST_HEAD(&new_fmt
->list
);
2020 INIT_LIST_HEAD(&new_fmt
->sort_list
);
2025 static int parse_field_name(char *str
, char **event
, char **field
, char **opt
)
2027 char *event_name
, *field_name
, *opt_name
;
2030 field_name
= strchr(str
, '.');
2033 *field_name
++ = '\0';
2039 opt_name
= strchr(field_name
, '/');
2043 *event
= event_name
;
2044 *field
= field_name
;
2050 /* find match evsel using a given event name. The event name can be:
2051 * 1. '%' + event index (e.g. '%1' for first event)
2052 * 2. full event name (e.g. sched:sched_switch)
2053 * 3. partial event name (should not contain ':')
2055 static struct perf_evsel
*find_evsel(struct perf_evlist
*evlist
, char *event_name
)
2057 struct perf_evsel
*evsel
= NULL
;
2058 struct perf_evsel
*pos
;
2062 if (event_name
[0] == '%') {
2063 int nr
= strtol(event_name
+1, NULL
, 0);
2065 if (nr
> evlist
->nr_entries
)
2068 evsel
= perf_evlist__first(evlist
);
2070 evsel
= perf_evsel__next(evsel
);
2075 full_name
= !!strchr(event_name
, ':');
2076 evlist__for_each_entry(evlist
, pos
) {
2078 if (full_name
&& !strcmp(pos
->name
, event_name
))
2081 if (!full_name
&& strstr(pos
->name
, event_name
)) {
2083 pr_debug("'%s' event is ambiguous: it can be %s or %s\n",
2084 event_name
, evsel
->name
, pos
->name
);
2094 static int __dynamic_dimension__add(struct perf_evsel
*evsel
,
2095 struct format_field
*field
,
2096 bool raw_trace
, int level
)
2098 struct hpp_dynamic_entry
*hde
;
2100 hde
= __alloc_dynamic_entry(evsel
, field
, level
);
2104 hde
->raw_trace
= raw_trace
;
2106 perf_hpp__register_sort_field(&hde
->hpp
);
2110 static int add_evsel_fields(struct perf_evsel
*evsel
, bool raw_trace
, int level
)
2113 struct format_field
*field
;
2115 field
= evsel
->tp_format
->format
.fields
;
2117 ret
= __dynamic_dimension__add(evsel
, field
, raw_trace
, level
);
2121 field
= field
->next
;
2126 static int add_all_dynamic_fields(struct perf_evlist
*evlist
, bool raw_trace
,
2130 struct perf_evsel
*evsel
;
2132 evlist__for_each_entry(evlist
, evsel
) {
2133 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
)
2136 ret
= add_evsel_fields(evsel
, raw_trace
, level
);
2143 static int add_all_matching_fields(struct perf_evlist
*evlist
,
2144 char *field_name
, bool raw_trace
, int level
)
2147 struct perf_evsel
*evsel
;
2148 struct format_field
*field
;
2150 evlist__for_each_entry(evlist
, evsel
) {
2151 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
)
2154 field
= pevent_find_any_field(evsel
->tp_format
, field_name
);
2158 ret
= __dynamic_dimension__add(evsel
, field
, raw_trace
, level
);
2165 static int add_dynamic_entry(struct perf_evlist
*evlist
, const char *tok
,
2168 char *str
, *event_name
, *field_name
, *opt_name
;
2169 struct perf_evsel
*evsel
;
2170 struct format_field
*field
;
2171 bool raw_trace
= symbol_conf
.raw_trace
;
2181 if (parse_field_name(str
, &event_name
, &field_name
, &opt_name
) < 0) {
2187 if (strcmp(opt_name
, "raw")) {
2188 pr_debug("unsupported field option %s\n", opt_name
);
2195 if (!strcmp(field_name
, "trace_fields")) {
2196 ret
= add_all_dynamic_fields(evlist
, raw_trace
, level
);
2200 if (event_name
== NULL
) {
2201 ret
= add_all_matching_fields(evlist
, field_name
, raw_trace
, level
);
2205 evsel
= find_evsel(evlist
, event_name
);
2206 if (evsel
== NULL
) {
2207 pr_debug("Cannot find event: %s\n", event_name
);
2212 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
) {
2213 pr_debug("%s is not a tracepoint event\n", event_name
);
2218 if (!strcmp(field_name
, "*")) {
2219 ret
= add_evsel_fields(evsel
, raw_trace
, level
);
2221 field
= pevent_find_any_field(evsel
->tp_format
, field_name
);
2222 if (field
== NULL
) {
2223 pr_debug("Cannot find event field for %s.%s\n",
2224 event_name
, field_name
);
2228 ret
= __dynamic_dimension__add(evsel
, field
, raw_trace
, level
);
2236 static int __sort_dimension__add(struct sort_dimension
*sd
,
2237 struct perf_hpp_list
*list
,
2243 if (__sort_dimension__add_hpp_sort(sd
, list
, level
) < 0)
2246 if (sd
->entry
->se_collapse
)
2247 list
->need_collapse
= 1;
2254 static int __hpp_dimension__add(struct hpp_dimension
*hd
,
2255 struct perf_hpp_list
*list
,
2258 struct perf_hpp_fmt
*fmt
;
2263 fmt
= __hpp_dimension__alloc_hpp(hd
, level
);
2268 perf_hpp_list__register_sort_field(list
, fmt
);
2272 static int __sort_dimension__add_output(struct perf_hpp_list
*list
,
2273 struct sort_dimension
*sd
)
2278 if (__sort_dimension__add_hpp_output(sd
, list
) < 0)
2285 static int __hpp_dimension__add_output(struct perf_hpp_list
*list
,
2286 struct hpp_dimension
*hd
)
2288 struct perf_hpp_fmt
*fmt
;
2293 fmt
= __hpp_dimension__alloc_hpp(hd
, 0);
2298 perf_hpp_list__column_register(list
, fmt
);
2302 int hpp_dimension__add_output(unsigned col
)
2304 BUG_ON(col
>= PERF_HPP__MAX_INDEX
);
2305 return __hpp_dimension__add_output(&perf_hpp_list
, &hpp_sort_dimensions
[col
]);
2308 static int sort_dimension__add(struct perf_hpp_list
*list
, const char *tok
,
2309 struct perf_evlist
*evlist
,
2314 for (i
= 0; i
< ARRAY_SIZE(common_sort_dimensions
); i
++) {
2315 struct sort_dimension
*sd
= &common_sort_dimensions
[i
];
2317 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2320 if (sd
->entry
== &sort_parent
) {
2321 int ret
= regcomp(&parent_regex
, parent_pattern
, REG_EXTENDED
);
2325 regerror(ret
, &parent_regex
, err
, sizeof(err
));
2326 pr_err("Invalid regex: %s\n%s", parent_pattern
, err
);
2330 } else if (sd
->entry
== &sort_sym
) {
2333 * perf diff displays the performance difference amongst
2334 * two or more perf.data files. Those files could come
2335 * from different binaries. So we should not compare
2336 * their ips, but the name of symbol.
2338 if (sort__mode
== SORT_MODE__DIFF
)
2339 sd
->entry
->se_collapse
= sort__sym_sort
;
2341 } else if (sd
->entry
== &sort_dso
) {
2343 } else if (sd
->entry
== &sort_socket
) {
2345 } else if (sd
->entry
== &sort_thread
) {
2347 } else if (sd
->entry
== &sort_comm
) {
2351 return __sort_dimension__add(sd
, list
, level
);
2354 for (i
= 0; i
< ARRAY_SIZE(hpp_sort_dimensions
); i
++) {
2355 struct hpp_dimension
*hd
= &hpp_sort_dimensions
[i
];
2357 if (strncasecmp(tok
, hd
->name
, strlen(tok
)))
2360 return __hpp_dimension__add(hd
, list
, level
);
2363 for (i
= 0; i
< ARRAY_SIZE(bstack_sort_dimensions
); i
++) {
2364 struct sort_dimension
*sd
= &bstack_sort_dimensions
[i
];
2366 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2369 if (sort__mode
!= SORT_MODE__BRANCH
)
2372 if (sd
->entry
== &sort_sym_from
|| sd
->entry
== &sort_sym_to
)
2375 __sort_dimension__add(sd
, list
, level
);
2379 for (i
= 0; i
< ARRAY_SIZE(memory_sort_dimensions
); i
++) {
2380 struct sort_dimension
*sd
= &memory_sort_dimensions
[i
];
2382 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2385 if (sort__mode
!= SORT_MODE__MEMORY
)
2388 if (sd
->entry
== &sort_mem_dcacheline
&& cacheline_size
== 0)
2391 if (sd
->entry
== &sort_mem_daddr_sym
)
2394 __sort_dimension__add(sd
, list
, level
);
2398 if (!add_dynamic_entry(evlist
, tok
, level
))
2404 static int setup_sort_list(struct perf_hpp_list
*list
, char *str
,
2405 struct perf_evlist
*evlist
)
2411 bool in_group
= false;
2415 tmp
= strpbrk(str
, "{}, ");
2420 next_level
= level
+ 1;
2424 else if (*tmp
== '}')
2432 ret
= sort_dimension__add(list
, tok
, evlist
, level
);
2433 if (ret
== -EINVAL
) {
2434 if (!cacheline_size
&& !strncasecmp(tok
, "dcacheline", strlen(tok
)))
2435 error("The \"dcacheline\" --sort key needs to know the cacheline size and it couldn't be determined on this system");
2437 error("Invalid --sort key: `%s'", tok
);
2439 } else if (ret
== -ESRCH
) {
2440 error("Unknown --sort key: `%s'", tok
);
2451 static const char *get_default_sort_order(struct perf_evlist
*evlist
)
2453 const char *default_sort_orders
[] = {
2455 default_branch_sort_order
,
2456 default_mem_sort_order
,
2457 default_top_sort_order
,
2458 default_diff_sort_order
,
2459 default_tracepoint_sort_order
,
2461 bool use_trace
= true;
2462 struct perf_evsel
*evsel
;
2464 BUG_ON(sort__mode
>= ARRAY_SIZE(default_sort_orders
));
2469 evlist__for_each_entry(evlist
, evsel
) {
2470 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
) {
2477 sort__mode
= SORT_MODE__TRACEPOINT
;
2478 if (symbol_conf
.raw_trace
)
2479 return "trace_fields";
2482 return default_sort_orders
[sort__mode
];
2485 static int setup_sort_order(struct perf_evlist
*evlist
)
2487 char *new_sort_order
;
2490 * Append '+'-prefixed sort order to the default sort
2493 if (!sort_order
|| is_strict_order(sort_order
))
2496 if (sort_order
[1] == '\0') {
2497 error("Invalid --sort key: `+'");
2502 * We allocate new sort_order string, but we never free it,
2503 * because it's checked over the rest of the code.
2505 if (asprintf(&new_sort_order
, "%s,%s",
2506 get_default_sort_order(evlist
), sort_order
+ 1) < 0) {
2507 error("Not enough memory to set up --sort");
2511 sort_order
= new_sort_order
;
2516 * Adds 'pre,' prefix into 'str' is 'pre' is
2517 * not already part of 'str'.
2519 static char *prefix_if_not_in(const char *pre
, char *str
)
2523 if (!str
|| strstr(str
, pre
))
2526 if (asprintf(&n
, "%s,%s", pre
, str
) < 0)
2533 static char *setup_overhead(char *keys
)
2535 if (sort__mode
== SORT_MODE__DIFF
)
2538 keys
= prefix_if_not_in("overhead", keys
);
2540 if (symbol_conf
.cumulate_callchain
)
2541 keys
= prefix_if_not_in("overhead_children", keys
);
2546 static int __setup_sorting(struct perf_evlist
*evlist
)
2549 const char *sort_keys
;
2552 ret
= setup_sort_order(evlist
);
2556 sort_keys
= sort_order
;
2557 if (sort_keys
== NULL
) {
2558 if (is_strict_order(field_order
)) {
2560 * If user specified field order but no sort order,
2561 * we'll honor it and not add default sort orders.
2566 sort_keys
= get_default_sort_order(evlist
);
2569 str
= strdup(sort_keys
);
2571 error("Not enough memory to setup sort keys");
2576 * Prepend overhead fields for backward compatibility.
2578 if (!is_strict_order(field_order
)) {
2579 str
= setup_overhead(str
);
2581 error("Not enough memory to setup overhead keys");
2586 ret
= setup_sort_list(&perf_hpp_list
, str
, evlist
);
2592 void perf_hpp__set_elide(int idx
, bool elide
)
2594 struct perf_hpp_fmt
*fmt
;
2595 struct hpp_sort_entry
*hse
;
2597 perf_hpp_list__for_each_format(&perf_hpp_list
, fmt
) {
2598 if (!perf_hpp__is_sort_entry(fmt
))
2601 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
2602 if (hse
->se
->se_width_idx
== idx
) {
2609 static bool __get_elide(struct strlist
*list
, const char *list_name
, FILE *fp
)
2611 if (list
&& strlist__nr_entries(list
) == 1) {
2613 fprintf(fp
, "# %s: %s\n", list_name
,
2614 strlist__entry(list
, 0)->s
);
2620 static bool get_elide(int idx
, FILE *output
)
2624 return __get_elide(symbol_conf
.sym_list
, "symbol", output
);
2626 return __get_elide(symbol_conf
.dso_list
, "dso", output
);
2628 return __get_elide(symbol_conf
.comm_list
, "comm", output
);
2633 if (sort__mode
!= SORT_MODE__BRANCH
)
2637 case HISTC_SYMBOL_FROM
:
2638 return __get_elide(symbol_conf
.sym_from_list
, "sym_from", output
);
2639 case HISTC_SYMBOL_TO
:
2640 return __get_elide(symbol_conf
.sym_to_list
, "sym_to", output
);
2641 case HISTC_DSO_FROM
:
2642 return __get_elide(symbol_conf
.dso_from_list
, "dso_from", output
);
2644 return __get_elide(symbol_conf
.dso_to_list
, "dso_to", output
);
2652 void sort__setup_elide(FILE *output
)
2654 struct perf_hpp_fmt
*fmt
;
2655 struct hpp_sort_entry
*hse
;
2657 perf_hpp_list__for_each_format(&perf_hpp_list
, fmt
) {
2658 if (!perf_hpp__is_sort_entry(fmt
))
2661 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
2662 fmt
->elide
= get_elide(hse
->se
->se_width_idx
, output
);
2666 * It makes no sense to elide all of sort entries.
2667 * Just revert them to show up again.
2669 perf_hpp_list__for_each_format(&perf_hpp_list
, fmt
) {
2670 if (!perf_hpp__is_sort_entry(fmt
))
2677 perf_hpp_list__for_each_format(&perf_hpp_list
, fmt
) {
2678 if (!perf_hpp__is_sort_entry(fmt
))
2685 static int output_field_add(struct perf_hpp_list
*list
, char *tok
)
2689 for (i
= 0; i
< ARRAY_SIZE(common_sort_dimensions
); i
++) {
2690 struct sort_dimension
*sd
= &common_sort_dimensions
[i
];
2692 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2695 return __sort_dimension__add_output(list
, sd
);
2698 for (i
= 0; i
< ARRAY_SIZE(hpp_sort_dimensions
); i
++) {
2699 struct hpp_dimension
*hd
= &hpp_sort_dimensions
[i
];
2701 if (strncasecmp(tok
, hd
->name
, strlen(tok
)))
2704 return __hpp_dimension__add_output(list
, hd
);
2707 for (i
= 0; i
< ARRAY_SIZE(bstack_sort_dimensions
); i
++) {
2708 struct sort_dimension
*sd
= &bstack_sort_dimensions
[i
];
2710 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2713 return __sort_dimension__add_output(list
, sd
);
2716 for (i
= 0; i
< ARRAY_SIZE(memory_sort_dimensions
); i
++) {
2717 struct sort_dimension
*sd
= &memory_sort_dimensions
[i
];
2719 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2722 return __sort_dimension__add_output(list
, sd
);
2728 static int setup_output_list(struct perf_hpp_list
*list
, char *str
)
2733 for (tok
= strtok_r(str
, ", ", &tmp
);
2734 tok
; tok
= strtok_r(NULL
, ", ", &tmp
)) {
2735 ret
= output_field_add(list
, tok
);
2736 if (ret
== -EINVAL
) {
2737 error("Invalid --fields key: `%s'", tok
);
2739 } else if (ret
== -ESRCH
) {
2740 error("Unknown --fields key: `%s'", tok
);
2748 static void reset_dimensions(void)
2752 for (i
= 0; i
< ARRAY_SIZE(common_sort_dimensions
); i
++)
2753 common_sort_dimensions
[i
].taken
= 0;
2755 for (i
= 0; i
< ARRAY_SIZE(hpp_sort_dimensions
); i
++)
2756 hpp_sort_dimensions
[i
].taken
= 0;
2758 for (i
= 0; i
< ARRAY_SIZE(bstack_sort_dimensions
); i
++)
2759 bstack_sort_dimensions
[i
].taken
= 0;
2761 for (i
= 0; i
< ARRAY_SIZE(memory_sort_dimensions
); i
++)
2762 memory_sort_dimensions
[i
].taken
= 0;
2765 bool is_strict_order(const char *order
)
2767 return order
&& (*order
!= '+');
2770 static int __setup_output_field(void)
2775 if (field_order
== NULL
)
2778 strp
= str
= strdup(field_order
);
2780 error("Not enough memory to setup output fields");
2784 if (!is_strict_order(field_order
))
2787 if (!strlen(strp
)) {
2788 error("Invalid --fields key: `+'");
2792 ret
= setup_output_list(&perf_hpp_list
, strp
);
2799 int setup_sorting(struct perf_evlist
*evlist
)
2803 err
= __setup_sorting(evlist
);
2807 if (parent_pattern
!= default_parent_pattern
) {
2808 err
= sort_dimension__add(&perf_hpp_list
, "parent", evlist
, -1);
2816 * perf diff doesn't use default hpp output fields.
2818 if (sort__mode
!= SORT_MODE__DIFF
)
2821 err
= __setup_output_field();
2825 /* copy sort keys to output fields */
2826 perf_hpp__setup_output_field(&perf_hpp_list
);
2827 /* and then copy output fields to sort keys */
2828 perf_hpp__append_sort_keys(&perf_hpp_list
);
2830 /* setup hists-specific output fields */
2831 if (perf_hpp__setup_hists_formats(&perf_hpp_list
, evlist
) < 0)
2837 void reset_output_field(void)
2839 perf_hpp_list
.need_collapse
= 0;
2840 perf_hpp_list
.parent
= 0;
2841 perf_hpp_list
.sym
= 0;
2842 perf_hpp_list
.dso
= 0;
2848 perf_hpp__reset_output_field(&perf_hpp_list
);