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
);
595 sort__trace_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
597 struct perf_evsel
*evsel
;
599 evsel
= hists_to_evsel(left
->hists
);
600 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
)
603 if (left
->trace_output
== NULL
)
604 left
->trace_output
= get_trace_output(left
);
605 if (right
->trace_output
== NULL
)
606 right
->trace_output
= get_trace_output(right
);
608 return strcmp(right
->trace_output
, left
->trace_output
);
611 static int hist_entry__trace_snprintf(struct hist_entry
*he
, char *bf
,
612 size_t size
, unsigned int width
)
614 struct perf_evsel
*evsel
;
616 evsel
= hists_to_evsel(he
->hists
);
617 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
)
618 return scnprintf(bf
, size
, "%-.*s", width
, "N/A");
620 if (he
->trace_output
== NULL
)
621 he
->trace_output
= get_trace_output(he
);
622 return repsep_snprintf(bf
, size
, "%-.*s", width
, he
->trace_output
);
625 struct sort_entry sort_trace
= {
626 .se_header
= "Trace output",
627 .se_cmp
= sort__trace_cmp
,
628 .se_snprintf
= hist_entry__trace_snprintf
,
629 .se_width_idx
= HISTC_TRACE
,
632 /* sort keys for branch stacks */
635 sort__dso_from_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
637 if (!left
->branch_info
|| !right
->branch_info
)
638 return cmp_null(left
->branch_info
, right
->branch_info
);
640 return _sort__dso_cmp(left
->branch_info
->from
.map
,
641 right
->branch_info
->from
.map
);
644 static int hist_entry__dso_from_snprintf(struct hist_entry
*he
, char *bf
,
645 size_t size
, unsigned int width
)
648 return _hist_entry__dso_snprintf(he
->branch_info
->from
.map
,
651 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, "N/A");
654 static int hist_entry__dso_from_filter(struct hist_entry
*he
, int type
,
657 const struct dso
*dso
= arg
;
659 if (type
!= HIST_FILTER__DSO
)
662 return dso
&& (!he
->branch_info
|| !he
->branch_info
->from
.map
||
663 he
->branch_info
->from
.map
->dso
!= dso
);
667 sort__dso_to_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
669 if (!left
->branch_info
|| !right
->branch_info
)
670 return cmp_null(left
->branch_info
, right
->branch_info
);
672 return _sort__dso_cmp(left
->branch_info
->to
.map
,
673 right
->branch_info
->to
.map
);
676 static int hist_entry__dso_to_snprintf(struct hist_entry
*he
, char *bf
,
677 size_t size
, unsigned int width
)
680 return _hist_entry__dso_snprintf(he
->branch_info
->to
.map
,
683 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, "N/A");
686 static int hist_entry__dso_to_filter(struct hist_entry
*he
, int type
,
689 const struct dso
*dso
= arg
;
691 if (type
!= HIST_FILTER__DSO
)
694 return dso
&& (!he
->branch_info
|| !he
->branch_info
->to
.map
||
695 he
->branch_info
->to
.map
->dso
!= dso
);
699 sort__sym_from_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
701 struct addr_map_symbol
*from_l
= &left
->branch_info
->from
;
702 struct addr_map_symbol
*from_r
= &right
->branch_info
->from
;
704 if (!left
->branch_info
|| !right
->branch_info
)
705 return cmp_null(left
->branch_info
, right
->branch_info
);
707 from_l
= &left
->branch_info
->from
;
708 from_r
= &right
->branch_info
->from
;
710 if (!from_l
->sym
&& !from_r
->sym
)
711 return _sort__addr_cmp(from_l
->addr
, from_r
->addr
);
713 return _sort__sym_cmp(from_l
->sym
, from_r
->sym
);
717 sort__sym_to_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
719 struct addr_map_symbol
*to_l
, *to_r
;
721 if (!left
->branch_info
|| !right
->branch_info
)
722 return cmp_null(left
->branch_info
, right
->branch_info
);
724 to_l
= &left
->branch_info
->to
;
725 to_r
= &right
->branch_info
->to
;
727 if (!to_l
->sym
&& !to_r
->sym
)
728 return _sort__addr_cmp(to_l
->addr
, to_r
->addr
);
730 return _sort__sym_cmp(to_l
->sym
, to_r
->sym
);
733 static int hist_entry__sym_from_snprintf(struct hist_entry
*he
, char *bf
,
734 size_t size
, unsigned int width
)
736 if (he
->branch_info
) {
737 struct addr_map_symbol
*from
= &he
->branch_info
->from
;
739 return _hist_entry__sym_snprintf(from
->map
, from
->sym
, from
->addr
,
740 he
->level
, bf
, size
, width
);
743 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, "N/A");
746 static int hist_entry__sym_to_snprintf(struct hist_entry
*he
, char *bf
,
747 size_t size
, unsigned int width
)
749 if (he
->branch_info
) {
750 struct addr_map_symbol
*to
= &he
->branch_info
->to
;
752 return _hist_entry__sym_snprintf(to
->map
, to
->sym
, to
->addr
,
753 he
->level
, bf
, size
, width
);
756 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, "N/A");
759 static int hist_entry__sym_from_filter(struct hist_entry
*he
, int type
,
762 const char *sym
= arg
;
764 if (type
!= HIST_FILTER__SYMBOL
)
767 return sym
&& !(he
->branch_info
&& he
->branch_info
->from
.sym
&&
768 strstr(he
->branch_info
->from
.sym
->name
, sym
));
771 static int hist_entry__sym_to_filter(struct hist_entry
*he
, int type
,
774 const char *sym
= arg
;
776 if (type
!= HIST_FILTER__SYMBOL
)
779 return sym
&& !(he
->branch_info
&& he
->branch_info
->to
.sym
&&
780 strstr(he
->branch_info
->to
.sym
->name
, sym
));
783 struct sort_entry sort_dso_from
= {
784 .se_header
= "Source Shared Object",
785 .se_cmp
= sort__dso_from_cmp
,
786 .se_snprintf
= hist_entry__dso_from_snprintf
,
787 .se_filter
= hist_entry__dso_from_filter
,
788 .se_width_idx
= HISTC_DSO_FROM
,
791 struct sort_entry sort_dso_to
= {
792 .se_header
= "Target Shared Object",
793 .se_cmp
= sort__dso_to_cmp
,
794 .se_snprintf
= hist_entry__dso_to_snprintf
,
795 .se_filter
= hist_entry__dso_to_filter
,
796 .se_width_idx
= HISTC_DSO_TO
,
799 struct sort_entry sort_sym_from
= {
800 .se_header
= "Source Symbol",
801 .se_cmp
= sort__sym_from_cmp
,
802 .se_snprintf
= hist_entry__sym_from_snprintf
,
803 .se_filter
= hist_entry__sym_from_filter
,
804 .se_width_idx
= HISTC_SYMBOL_FROM
,
807 struct sort_entry sort_sym_to
= {
808 .se_header
= "Target Symbol",
809 .se_cmp
= sort__sym_to_cmp
,
810 .se_snprintf
= hist_entry__sym_to_snprintf
,
811 .se_filter
= hist_entry__sym_to_filter
,
812 .se_width_idx
= HISTC_SYMBOL_TO
,
816 sort__mispredict_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
820 if (!left
->branch_info
|| !right
->branch_info
)
821 return cmp_null(left
->branch_info
, right
->branch_info
);
823 mp
= left
->branch_info
->flags
.mispred
!= right
->branch_info
->flags
.mispred
;
824 p
= left
->branch_info
->flags
.predicted
!= right
->branch_info
->flags
.predicted
;
828 static int hist_entry__mispredict_snprintf(struct hist_entry
*he
, char *bf
,
829 size_t size
, unsigned int width
){
830 static const char *out
= "N/A";
832 if (he
->branch_info
) {
833 if (he
->branch_info
->flags
.predicted
)
835 else if (he
->branch_info
->flags
.mispred
)
839 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, out
);
843 sort__cycles_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
845 return left
->branch_info
->flags
.cycles
-
846 right
->branch_info
->flags
.cycles
;
849 static int hist_entry__cycles_snprintf(struct hist_entry
*he
, char *bf
,
850 size_t size
, unsigned int width
)
852 if (he
->branch_info
->flags
.cycles
== 0)
853 return repsep_snprintf(bf
, size
, "%-*s", width
, "-");
854 return repsep_snprintf(bf
, size
, "%-*hd", width
,
855 he
->branch_info
->flags
.cycles
);
858 struct sort_entry sort_cycles
= {
859 .se_header
= "Basic Block Cycles",
860 .se_cmp
= sort__cycles_cmp
,
861 .se_snprintf
= hist_entry__cycles_snprintf
,
862 .se_width_idx
= HISTC_CYCLES
,
865 /* --sort daddr_sym */
867 sort__daddr_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
869 uint64_t l
= 0, r
= 0;
872 l
= left
->mem_info
->daddr
.addr
;
874 r
= right
->mem_info
->daddr
.addr
;
876 return (int64_t)(r
- l
);
879 static int hist_entry__daddr_snprintf(struct hist_entry
*he
, char *bf
,
880 size_t size
, unsigned int width
)
883 struct map
*map
= NULL
;
884 struct symbol
*sym
= NULL
;
887 addr
= he
->mem_info
->daddr
.addr
;
888 map
= he
->mem_info
->daddr
.map
;
889 sym
= he
->mem_info
->daddr
.sym
;
891 return _hist_entry__sym_snprintf(map
, sym
, addr
, he
->level
, bf
, size
,
896 sort__iaddr_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
898 uint64_t l
= 0, r
= 0;
901 l
= left
->mem_info
->iaddr
.addr
;
903 r
= right
->mem_info
->iaddr
.addr
;
905 return (int64_t)(r
- l
);
908 static int hist_entry__iaddr_snprintf(struct hist_entry
*he
, char *bf
,
909 size_t size
, unsigned int width
)
912 struct map
*map
= NULL
;
913 struct symbol
*sym
= NULL
;
916 addr
= he
->mem_info
->iaddr
.addr
;
917 map
= he
->mem_info
->iaddr
.map
;
918 sym
= he
->mem_info
->iaddr
.sym
;
920 return _hist_entry__sym_snprintf(map
, sym
, addr
, he
->level
, bf
, size
,
925 sort__dso_daddr_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
927 struct map
*map_l
= NULL
;
928 struct map
*map_r
= NULL
;
931 map_l
= left
->mem_info
->daddr
.map
;
933 map_r
= right
->mem_info
->daddr
.map
;
935 return _sort__dso_cmp(map_l
, map_r
);
938 static int hist_entry__dso_daddr_snprintf(struct hist_entry
*he
, char *bf
,
939 size_t size
, unsigned int width
)
941 struct map
*map
= NULL
;
944 map
= he
->mem_info
->daddr
.map
;
946 return _hist_entry__dso_snprintf(map
, bf
, size
, width
);
950 sort__locked_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
952 union perf_mem_data_src data_src_l
;
953 union perf_mem_data_src data_src_r
;
956 data_src_l
= left
->mem_info
->data_src
;
958 data_src_l
.mem_lock
= PERF_MEM_LOCK_NA
;
961 data_src_r
= right
->mem_info
->data_src
;
963 data_src_r
.mem_lock
= PERF_MEM_LOCK_NA
;
965 return (int64_t)(data_src_r
.mem_lock
- data_src_l
.mem_lock
);
968 static int hist_entry__locked_snprintf(struct hist_entry
*he
, char *bf
,
969 size_t size
, unsigned int width
)
973 perf_mem__lck_scnprintf(out
, sizeof(out
), he
->mem_info
);
974 return repsep_snprintf(bf
, size
, "%.*s", width
, out
);
978 sort__tlb_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
980 union perf_mem_data_src data_src_l
;
981 union perf_mem_data_src data_src_r
;
984 data_src_l
= left
->mem_info
->data_src
;
986 data_src_l
.mem_dtlb
= PERF_MEM_TLB_NA
;
989 data_src_r
= right
->mem_info
->data_src
;
991 data_src_r
.mem_dtlb
= PERF_MEM_TLB_NA
;
993 return (int64_t)(data_src_r
.mem_dtlb
- data_src_l
.mem_dtlb
);
996 static int hist_entry__tlb_snprintf(struct hist_entry
*he
, char *bf
,
997 size_t size
, unsigned int width
)
1001 perf_mem__tlb_scnprintf(out
, sizeof(out
), he
->mem_info
);
1002 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
1006 sort__lvl_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1008 union perf_mem_data_src data_src_l
;
1009 union perf_mem_data_src data_src_r
;
1012 data_src_l
= left
->mem_info
->data_src
;
1014 data_src_l
.mem_lvl
= PERF_MEM_LVL_NA
;
1016 if (right
->mem_info
)
1017 data_src_r
= right
->mem_info
->data_src
;
1019 data_src_r
.mem_lvl
= PERF_MEM_LVL_NA
;
1021 return (int64_t)(data_src_r
.mem_lvl
- data_src_l
.mem_lvl
);
1024 static int hist_entry__lvl_snprintf(struct hist_entry
*he
, char *bf
,
1025 size_t size
, unsigned int width
)
1029 perf_mem__lvl_scnprintf(out
, sizeof(out
), he
->mem_info
);
1030 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
1034 sort__snoop_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1036 union perf_mem_data_src data_src_l
;
1037 union perf_mem_data_src data_src_r
;
1040 data_src_l
= left
->mem_info
->data_src
;
1042 data_src_l
.mem_snoop
= PERF_MEM_SNOOP_NA
;
1044 if (right
->mem_info
)
1045 data_src_r
= right
->mem_info
->data_src
;
1047 data_src_r
.mem_snoop
= PERF_MEM_SNOOP_NA
;
1049 return (int64_t)(data_src_r
.mem_snoop
- data_src_l
.mem_snoop
);
1052 static int hist_entry__snoop_snprintf(struct hist_entry
*he
, char *bf
,
1053 size_t size
, unsigned int width
)
1057 perf_mem__snp_scnprintf(out
, sizeof(out
), he
->mem_info
);
1058 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
1062 sort__dcacheline_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1065 struct map
*l_map
, *r_map
;
1067 if (!left
->mem_info
) return -1;
1068 if (!right
->mem_info
) return 1;
1070 /* group event types together */
1071 if (left
->cpumode
> right
->cpumode
) return -1;
1072 if (left
->cpumode
< right
->cpumode
) return 1;
1074 l_map
= left
->mem_info
->daddr
.map
;
1075 r_map
= right
->mem_info
->daddr
.map
;
1077 /* if both are NULL, jump to sort on al_addr instead */
1078 if (!l_map
&& !r_map
)
1081 if (!l_map
) return -1;
1082 if (!r_map
) return 1;
1084 if (l_map
->maj
> r_map
->maj
) return -1;
1085 if (l_map
->maj
< r_map
->maj
) return 1;
1087 if (l_map
->min
> r_map
->min
) return -1;
1088 if (l_map
->min
< r_map
->min
) return 1;
1090 if (l_map
->ino
> r_map
->ino
) return -1;
1091 if (l_map
->ino
< r_map
->ino
) return 1;
1093 if (l_map
->ino_generation
> r_map
->ino_generation
) return -1;
1094 if (l_map
->ino_generation
< r_map
->ino_generation
) return 1;
1097 * Addresses with no major/minor numbers are assumed to be
1098 * anonymous in userspace. Sort those on pid then address.
1100 * The kernel and non-zero major/minor mapped areas are
1101 * assumed to be unity mapped. Sort those on address.
1104 if ((left
->cpumode
!= PERF_RECORD_MISC_KERNEL
) &&
1105 (!(l_map
->flags
& MAP_SHARED
)) &&
1106 !l_map
->maj
&& !l_map
->min
&& !l_map
->ino
&&
1107 !l_map
->ino_generation
) {
1108 /* userspace anonymous */
1110 if (left
->thread
->pid_
> right
->thread
->pid_
) return -1;
1111 if (left
->thread
->pid_
< right
->thread
->pid_
) return 1;
1115 /* al_addr does all the right addr - start + offset calculations */
1116 l
= cl_address(left
->mem_info
->daddr
.al_addr
);
1117 r
= cl_address(right
->mem_info
->daddr
.al_addr
);
1119 if (l
> r
) return -1;
1120 if (l
< r
) return 1;
1125 static int hist_entry__dcacheline_snprintf(struct hist_entry
*he
, char *bf
,
1126 size_t size
, unsigned int width
)
1130 struct map
*map
= NULL
;
1131 struct symbol
*sym
= NULL
;
1132 char level
= he
->level
;
1135 addr
= cl_address(he
->mem_info
->daddr
.al_addr
);
1136 map
= he
->mem_info
->daddr
.map
;
1137 sym
= he
->mem_info
->daddr
.sym
;
1139 /* print [s] for shared data mmaps */
1140 if ((he
->cpumode
!= PERF_RECORD_MISC_KERNEL
) &&
1141 map
&& (map
->type
== MAP__VARIABLE
) &&
1142 (map
->flags
& MAP_SHARED
) &&
1143 (map
->maj
|| map
->min
|| map
->ino
||
1144 map
->ino_generation
))
1149 return _hist_entry__sym_snprintf(map
, sym
, addr
, level
, bf
, size
,
1153 struct sort_entry sort_mispredict
= {
1154 .se_header
= "Branch Mispredicted",
1155 .se_cmp
= sort__mispredict_cmp
,
1156 .se_snprintf
= hist_entry__mispredict_snprintf
,
1157 .se_width_idx
= HISTC_MISPREDICT
,
1160 static u64
he_weight(struct hist_entry
*he
)
1162 return he
->stat
.nr_events
? he
->stat
.weight
/ he
->stat
.nr_events
: 0;
1166 sort__local_weight_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1168 return he_weight(left
) - he_weight(right
);
1171 static int hist_entry__local_weight_snprintf(struct hist_entry
*he
, char *bf
,
1172 size_t size
, unsigned int width
)
1174 return repsep_snprintf(bf
, size
, "%-*llu", width
, he_weight(he
));
1177 struct sort_entry sort_local_weight
= {
1178 .se_header
= "Local Weight",
1179 .se_cmp
= sort__local_weight_cmp
,
1180 .se_snprintf
= hist_entry__local_weight_snprintf
,
1181 .se_width_idx
= HISTC_LOCAL_WEIGHT
,
1185 sort__global_weight_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1187 return left
->stat
.weight
- right
->stat
.weight
;
1190 static int hist_entry__global_weight_snprintf(struct hist_entry
*he
, char *bf
,
1191 size_t size
, unsigned int width
)
1193 return repsep_snprintf(bf
, size
, "%-*llu", width
, he
->stat
.weight
);
1196 struct sort_entry sort_global_weight
= {
1197 .se_header
= "Weight",
1198 .se_cmp
= sort__global_weight_cmp
,
1199 .se_snprintf
= hist_entry__global_weight_snprintf
,
1200 .se_width_idx
= HISTC_GLOBAL_WEIGHT
,
1203 struct sort_entry sort_mem_daddr_sym
= {
1204 .se_header
= "Data Symbol",
1205 .se_cmp
= sort__daddr_cmp
,
1206 .se_snprintf
= hist_entry__daddr_snprintf
,
1207 .se_width_idx
= HISTC_MEM_DADDR_SYMBOL
,
1210 struct sort_entry sort_mem_iaddr_sym
= {
1211 .se_header
= "Code Symbol",
1212 .se_cmp
= sort__iaddr_cmp
,
1213 .se_snprintf
= hist_entry__iaddr_snprintf
,
1214 .se_width_idx
= HISTC_MEM_IADDR_SYMBOL
,
1217 struct sort_entry sort_mem_daddr_dso
= {
1218 .se_header
= "Data Object",
1219 .se_cmp
= sort__dso_daddr_cmp
,
1220 .se_snprintf
= hist_entry__dso_daddr_snprintf
,
1221 .se_width_idx
= HISTC_MEM_DADDR_DSO
,
1224 struct sort_entry sort_mem_locked
= {
1225 .se_header
= "Locked",
1226 .se_cmp
= sort__locked_cmp
,
1227 .se_snprintf
= hist_entry__locked_snprintf
,
1228 .se_width_idx
= HISTC_MEM_LOCKED
,
1231 struct sort_entry sort_mem_tlb
= {
1232 .se_header
= "TLB access",
1233 .se_cmp
= sort__tlb_cmp
,
1234 .se_snprintf
= hist_entry__tlb_snprintf
,
1235 .se_width_idx
= HISTC_MEM_TLB
,
1238 struct sort_entry sort_mem_lvl
= {
1239 .se_header
= "Memory access",
1240 .se_cmp
= sort__lvl_cmp
,
1241 .se_snprintf
= hist_entry__lvl_snprintf
,
1242 .se_width_idx
= HISTC_MEM_LVL
,
1245 struct sort_entry sort_mem_snoop
= {
1246 .se_header
= "Snoop",
1247 .se_cmp
= sort__snoop_cmp
,
1248 .se_snprintf
= hist_entry__snoop_snprintf
,
1249 .se_width_idx
= HISTC_MEM_SNOOP
,
1252 struct sort_entry sort_mem_dcacheline
= {
1253 .se_header
= "Data Cacheline",
1254 .se_cmp
= sort__dcacheline_cmp
,
1255 .se_snprintf
= hist_entry__dcacheline_snprintf
,
1256 .se_width_idx
= HISTC_MEM_DCACHELINE
,
1260 sort__abort_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1262 if (!left
->branch_info
|| !right
->branch_info
)
1263 return cmp_null(left
->branch_info
, right
->branch_info
);
1265 return left
->branch_info
->flags
.abort
!=
1266 right
->branch_info
->flags
.abort
;
1269 static int hist_entry__abort_snprintf(struct hist_entry
*he
, char *bf
,
1270 size_t size
, unsigned int width
)
1272 static const char *out
= "N/A";
1274 if (he
->branch_info
) {
1275 if (he
->branch_info
->flags
.abort
)
1281 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
1284 struct sort_entry sort_abort
= {
1285 .se_header
= "Transaction abort",
1286 .se_cmp
= sort__abort_cmp
,
1287 .se_snprintf
= hist_entry__abort_snprintf
,
1288 .se_width_idx
= HISTC_ABORT
,
1292 sort__in_tx_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1294 if (!left
->branch_info
|| !right
->branch_info
)
1295 return cmp_null(left
->branch_info
, right
->branch_info
);
1297 return left
->branch_info
->flags
.in_tx
!=
1298 right
->branch_info
->flags
.in_tx
;
1301 static int hist_entry__in_tx_snprintf(struct hist_entry
*he
, char *bf
,
1302 size_t size
, unsigned int width
)
1304 static const char *out
= "N/A";
1306 if (he
->branch_info
) {
1307 if (he
->branch_info
->flags
.in_tx
)
1313 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
1316 struct sort_entry sort_in_tx
= {
1317 .se_header
= "Branch in transaction",
1318 .se_cmp
= sort__in_tx_cmp
,
1319 .se_snprintf
= hist_entry__in_tx_snprintf
,
1320 .se_width_idx
= HISTC_IN_TX
,
1324 sort__transaction_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1326 return left
->transaction
- right
->transaction
;
1329 static inline char *add_str(char *p
, const char *str
)
1332 return p
+ strlen(str
);
1335 static struct txbit
{
1340 { PERF_TXN_ELISION
, "EL ", 0 },
1341 { PERF_TXN_TRANSACTION
, "TX ", 1 },
1342 { PERF_TXN_SYNC
, "SYNC ", 1 },
1343 { PERF_TXN_ASYNC
, "ASYNC ", 0 },
1344 { PERF_TXN_RETRY
, "RETRY ", 0 },
1345 { PERF_TXN_CONFLICT
, "CON ", 0 },
1346 { PERF_TXN_CAPACITY_WRITE
, "CAP-WRITE ", 1 },
1347 { PERF_TXN_CAPACITY_READ
, "CAP-READ ", 0 },
1351 int hist_entry__transaction_len(void)
1356 for (i
= 0; txbits
[i
].name
; i
++) {
1357 if (!txbits
[i
].skip_for_len
)
1358 len
+= strlen(txbits
[i
].name
);
1360 len
+= 4; /* :XX<space> */
1364 static int hist_entry__transaction_snprintf(struct hist_entry
*he
, char *bf
,
1365 size_t size
, unsigned int width
)
1367 u64 t
= he
->transaction
;
1373 for (i
= 0; txbits
[i
].name
; i
++)
1374 if (txbits
[i
].flag
& t
)
1375 p
= add_str(p
, txbits
[i
].name
);
1376 if (t
&& !(t
& (PERF_TXN_SYNC
|PERF_TXN_ASYNC
)))
1377 p
= add_str(p
, "NEITHER ");
1378 if (t
& PERF_TXN_ABORT_MASK
) {
1379 sprintf(p
, ":%" PRIx64
,
1380 (t
& PERF_TXN_ABORT_MASK
) >>
1381 PERF_TXN_ABORT_SHIFT
);
1385 return repsep_snprintf(bf
, size
, "%-*s", width
, buf
);
1388 struct sort_entry sort_transaction
= {
1389 .se_header
= "Transaction ",
1390 .se_cmp
= sort__transaction_cmp
,
1391 .se_snprintf
= hist_entry__transaction_snprintf
,
1392 .se_width_idx
= HISTC_TRANSACTION
,
1395 struct sort_dimension
{
1397 struct sort_entry
*entry
;
1401 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
1403 static struct sort_dimension common_sort_dimensions
[] = {
1404 DIM(SORT_PID
, "pid", sort_thread
),
1405 DIM(SORT_COMM
, "comm", sort_comm
),
1406 DIM(SORT_DSO
, "dso", sort_dso
),
1407 DIM(SORT_SYM
, "symbol", sort_sym
),
1408 DIM(SORT_PARENT
, "parent", sort_parent
),
1409 DIM(SORT_CPU
, "cpu", sort_cpu
),
1410 DIM(SORT_SOCKET
, "socket", sort_socket
),
1411 DIM(SORT_SRCLINE
, "srcline", sort_srcline
),
1412 DIM(SORT_SRCFILE
, "srcfile", sort_srcfile
),
1413 DIM(SORT_LOCAL_WEIGHT
, "local_weight", sort_local_weight
),
1414 DIM(SORT_GLOBAL_WEIGHT
, "weight", sort_global_weight
),
1415 DIM(SORT_TRANSACTION
, "transaction", sort_transaction
),
1416 DIM(SORT_TRACE
, "trace", sort_trace
),
1421 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
1423 static struct sort_dimension bstack_sort_dimensions
[] = {
1424 DIM(SORT_DSO_FROM
, "dso_from", sort_dso_from
),
1425 DIM(SORT_DSO_TO
, "dso_to", sort_dso_to
),
1426 DIM(SORT_SYM_FROM
, "symbol_from", sort_sym_from
),
1427 DIM(SORT_SYM_TO
, "symbol_to", sort_sym_to
),
1428 DIM(SORT_MISPREDICT
, "mispredict", sort_mispredict
),
1429 DIM(SORT_IN_TX
, "in_tx", sort_in_tx
),
1430 DIM(SORT_ABORT
, "abort", sort_abort
),
1431 DIM(SORT_CYCLES
, "cycles", sort_cycles
),
1432 DIM(SORT_SRCLINE_FROM
, "srcline_from", sort_srcline_from
),
1433 DIM(SORT_SRCLINE_TO
, "srcline_to", sort_srcline_to
),
1438 #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
1440 static struct sort_dimension memory_sort_dimensions
[] = {
1441 DIM(SORT_MEM_DADDR_SYMBOL
, "symbol_daddr", sort_mem_daddr_sym
),
1442 DIM(SORT_MEM_IADDR_SYMBOL
, "symbol_iaddr", sort_mem_iaddr_sym
),
1443 DIM(SORT_MEM_DADDR_DSO
, "dso_daddr", sort_mem_daddr_dso
),
1444 DIM(SORT_MEM_LOCKED
, "locked", sort_mem_locked
),
1445 DIM(SORT_MEM_TLB
, "tlb", sort_mem_tlb
),
1446 DIM(SORT_MEM_LVL
, "mem", sort_mem_lvl
),
1447 DIM(SORT_MEM_SNOOP
, "snoop", sort_mem_snoop
),
1448 DIM(SORT_MEM_DCACHELINE
, "dcacheline", sort_mem_dcacheline
),
1453 struct hpp_dimension
{
1455 struct perf_hpp_fmt
*fmt
;
1459 #define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
1461 static struct hpp_dimension hpp_sort_dimensions
[] = {
1462 DIM(PERF_HPP__OVERHEAD
, "overhead"),
1463 DIM(PERF_HPP__OVERHEAD_SYS
, "overhead_sys"),
1464 DIM(PERF_HPP__OVERHEAD_US
, "overhead_us"),
1465 DIM(PERF_HPP__OVERHEAD_GUEST_SYS
, "overhead_guest_sys"),
1466 DIM(PERF_HPP__OVERHEAD_GUEST_US
, "overhead_guest_us"),
1467 DIM(PERF_HPP__OVERHEAD_ACC
, "overhead_children"),
1468 DIM(PERF_HPP__SAMPLES
, "sample"),
1469 DIM(PERF_HPP__PERIOD
, "period"),
1474 struct hpp_sort_entry
{
1475 struct perf_hpp_fmt hpp
;
1476 struct sort_entry
*se
;
1479 void perf_hpp__reset_sort_width(struct perf_hpp_fmt
*fmt
, struct hists
*hists
)
1481 struct hpp_sort_entry
*hse
;
1483 if (!perf_hpp__is_sort_entry(fmt
))
1486 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1487 hists__new_col_len(hists
, hse
->se
->se_width_idx
, strlen(fmt
->name
));
1490 static int __sort__hpp_header(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
1491 struct hists
*hists
)
1493 struct hpp_sort_entry
*hse
;
1494 size_t len
= fmt
->user_len
;
1496 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1499 len
= hists__col_len(hists
, hse
->se
->se_width_idx
);
1501 return scnprintf(hpp
->buf
, hpp
->size
, "%-*.*s", len
, len
, fmt
->name
);
1504 static int __sort__hpp_width(struct perf_hpp_fmt
*fmt
,
1505 struct perf_hpp
*hpp __maybe_unused
,
1506 struct hists
*hists
)
1508 struct hpp_sort_entry
*hse
;
1509 size_t len
= fmt
->user_len
;
1511 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1514 len
= hists__col_len(hists
, hse
->se
->se_width_idx
);
1519 static int __sort__hpp_entry(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
1520 struct hist_entry
*he
)
1522 struct hpp_sort_entry
*hse
;
1523 size_t len
= fmt
->user_len
;
1525 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1528 len
= hists__col_len(he
->hists
, hse
->se
->se_width_idx
);
1530 return hse
->se
->se_snprintf(he
, hpp
->buf
, hpp
->size
, len
);
1533 static int64_t __sort__hpp_cmp(struct perf_hpp_fmt
*fmt
,
1534 struct hist_entry
*a
, struct hist_entry
*b
)
1536 struct hpp_sort_entry
*hse
;
1538 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1539 return hse
->se
->se_cmp(a
, b
);
1542 static int64_t __sort__hpp_collapse(struct perf_hpp_fmt
*fmt
,
1543 struct hist_entry
*a
, struct hist_entry
*b
)
1545 struct hpp_sort_entry
*hse
;
1546 int64_t (*collapse_fn
)(struct hist_entry
*, struct hist_entry
*);
1548 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1549 collapse_fn
= hse
->se
->se_collapse
?: hse
->se
->se_cmp
;
1550 return collapse_fn(a
, b
);
1553 static int64_t __sort__hpp_sort(struct perf_hpp_fmt
*fmt
,
1554 struct hist_entry
*a
, struct hist_entry
*b
)
1556 struct hpp_sort_entry
*hse
;
1557 int64_t (*sort_fn
)(struct hist_entry
*, struct hist_entry
*);
1559 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1560 sort_fn
= hse
->se
->se_sort
?: hse
->se
->se_cmp
;
1561 return sort_fn(a
, b
);
1564 bool perf_hpp__is_sort_entry(struct perf_hpp_fmt
*format
)
1566 return format
->header
== __sort__hpp_header
;
1569 #define MK_SORT_ENTRY_CHK(key) \
1570 bool perf_hpp__is_ ## key ## _entry(struct perf_hpp_fmt *fmt) \
1572 struct hpp_sort_entry *hse; \
1574 if (!perf_hpp__is_sort_entry(fmt)) \
1577 hse = container_of(fmt, struct hpp_sort_entry, hpp); \
1578 return hse->se == &sort_ ## key ; \
1581 MK_SORT_ENTRY_CHK(trace
)
1582 MK_SORT_ENTRY_CHK(srcline
)
1583 MK_SORT_ENTRY_CHK(srcfile
)
1584 MK_SORT_ENTRY_CHK(thread
)
1585 MK_SORT_ENTRY_CHK(comm
)
1586 MK_SORT_ENTRY_CHK(dso
)
1587 MK_SORT_ENTRY_CHK(sym
)
1590 static bool __sort__hpp_equal(struct perf_hpp_fmt
*a
, struct perf_hpp_fmt
*b
)
1592 struct hpp_sort_entry
*hse_a
;
1593 struct hpp_sort_entry
*hse_b
;
1595 if (!perf_hpp__is_sort_entry(a
) || !perf_hpp__is_sort_entry(b
))
1598 hse_a
= container_of(a
, struct hpp_sort_entry
, hpp
);
1599 hse_b
= container_of(b
, struct hpp_sort_entry
, hpp
);
1601 return hse_a
->se
== hse_b
->se
;
1604 static void hse_free(struct perf_hpp_fmt
*fmt
)
1606 struct hpp_sort_entry
*hse
;
1608 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1612 static struct hpp_sort_entry
*
1613 __sort_dimension__alloc_hpp(struct sort_dimension
*sd
, int level
)
1615 struct hpp_sort_entry
*hse
;
1617 hse
= malloc(sizeof(*hse
));
1619 pr_err("Memory allocation failed\n");
1623 hse
->se
= sd
->entry
;
1624 hse
->hpp
.name
= sd
->entry
->se_header
;
1625 hse
->hpp
.header
= __sort__hpp_header
;
1626 hse
->hpp
.width
= __sort__hpp_width
;
1627 hse
->hpp
.entry
= __sort__hpp_entry
;
1628 hse
->hpp
.color
= NULL
;
1630 hse
->hpp
.cmp
= __sort__hpp_cmp
;
1631 hse
->hpp
.collapse
= __sort__hpp_collapse
;
1632 hse
->hpp
.sort
= __sort__hpp_sort
;
1633 hse
->hpp
.equal
= __sort__hpp_equal
;
1634 hse
->hpp
.free
= hse_free
;
1636 INIT_LIST_HEAD(&hse
->hpp
.list
);
1637 INIT_LIST_HEAD(&hse
->hpp
.sort_list
);
1638 hse
->hpp
.elide
= false;
1640 hse
->hpp
.user_len
= 0;
1641 hse
->hpp
.level
= level
;
1646 static void hpp_free(struct perf_hpp_fmt
*fmt
)
1651 static struct perf_hpp_fmt
*__hpp_dimension__alloc_hpp(struct hpp_dimension
*hd
,
1654 struct perf_hpp_fmt
*fmt
;
1656 fmt
= memdup(hd
->fmt
, sizeof(*fmt
));
1658 INIT_LIST_HEAD(&fmt
->list
);
1659 INIT_LIST_HEAD(&fmt
->sort_list
);
1660 fmt
->free
= hpp_free
;
1667 int hist_entry__filter(struct hist_entry
*he
, int type
, const void *arg
)
1669 struct perf_hpp_fmt
*fmt
;
1670 struct hpp_sort_entry
*hse
;
1674 perf_hpp_list__for_each_format(he
->hpp_list
, fmt
) {
1675 if (!perf_hpp__is_sort_entry(fmt
))
1678 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1679 if (hse
->se
->se_filter
== NULL
)
1683 * hist entry is filtered if any of sort key in the hpp list
1684 * is applied. But it should skip non-matched filter types.
1686 r
= hse
->se
->se_filter(he
, type
, arg
);
1697 static int __sort_dimension__add_hpp_sort(struct sort_dimension
*sd
,
1698 struct perf_hpp_list
*list
,
1701 struct hpp_sort_entry
*hse
= __sort_dimension__alloc_hpp(sd
, level
);
1706 perf_hpp_list__register_sort_field(list
, &hse
->hpp
);
1710 static int __sort_dimension__add_hpp_output(struct sort_dimension
*sd
,
1711 struct perf_hpp_list
*list
)
1713 struct hpp_sort_entry
*hse
= __sort_dimension__alloc_hpp(sd
, 0);
1718 perf_hpp_list__column_register(list
, &hse
->hpp
);
1722 struct hpp_dynamic_entry
{
1723 struct perf_hpp_fmt hpp
;
1724 struct perf_evsel
*evsel
;
1725 struct format_field
*field
;
1726 unsigned dynamic_len
;
1730 static int hde_width(struct hpp_dynamic_entry
*hde
)
1732 if (!hde
->hpp
.len
) {
1733 int len
= hde
->dynamic_len
;
1734 int namelen
= strlen(hde
->field
->name
);
1735 int fieldlen
= hde
->field
->size
;
1740 if (!(hde
->field
->flags
& FIELD_IS_STRING
)) {
1741 /* length for print hex numbers */
1742 fieldlen
= hde
->field
->size
* 2 + 2;
1749 return hde
->hpp
.len
;
1752 static void update_dynamic_len(struct hpp_dynamic_entry
*hde
,
1753 struct hist_entry
*he
)
1756 struct format_field
*field
= hde
->field
;
1763 /* parse pretty print result and update max length */
1764 if (!he
->trace_output
)
1765 he
->trace_output
= get_trace_output(he
);
1767 namelen
= strlen(field
->name
);
1768 str
= he
->trace_output
;
1771 pos
= strchr(str
, ' ');
1774 pos
= str
+ strlen(str
);
1777 if (!strncmp(str
, field
->name
, namelen
)) {
1783 if (len
> hde
->dynamic_len
)
1784 hde
->dynamic_len
= len
;
1795 static int __sort__hde_header(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
1796 struct hists
*hists __maybe_unused
)
1798 struct hpp_dynamic_entry
*hde
;
1799 size_t len
= fmt
->user_len
;
1801 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
1804 len
= hde_width(hde
);
1806 return scnprintf(hpp
->buf
, hpp
->size
, "%*.*s", len
, len
, hde
->field
->name
);
1809 static int __sort__hde_width(struct perf_hpp_fmt
*fmt
,
1810 struct perf_hpp
*hpp __maybe_unused
,
1811 struct hists
*hists __maybe_unused
)
1813 struct hpp_dynamic_entry
*hde
;
1814 size_t len
= fmt
->user_len
;
1816 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
1819 len
= hde_width(hde
);
1824 bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt
*fmt
, struct hists
*hists
)
1826 struct hpp_dynamic_entry
*hde
;
1828 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
1830 return hists_to_evsel(hists
) == hde
->evsel
;
1833 static int __sort__hde_entry(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
1834 struct hist_entry
*he
)
1836 struct hpp_dynamic_entry
*hde
;
1837 size_t len
= fmt
->user_len
;
1839 struct format_field
*field
;
1844 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
1847 len
= hde_width(hde
);
1852 if (!he
->trace_output
)
1853 he
->trace_output
= get_trace_output(he
);
1856 namelen
= strlen(field
->name
);
1857 str
= he
->trace_output
;
1860 pos
= strchr(str
, ' ');
1863 pos
= str
+ strlen(str
);
1866 if (!strncmp(str
, field
->name
, namelen
)) {
1868 str
= strndup(str
, pos
- str
);
1871 return scnprintf(hpp
->buf
, hpp
->size
,
1872 "%*.*s", len
, len
, "ERROR");
1883 struct trace_seq seq
;
1885 trace_seq_init(&seq
);
1886 pevent_print_field(&seq
, he
->raw_data
, hde
->field
);
1890 ret
= scnprintf(hpp
->buf
, hpp
->size
, "%*.*s", len
, len
, str
);
1895 static int64_t __sort__hde_cmp(struct perf_hpp_fmt
*fmt
,
1896 struct hist_entry
*a
, struct hist_entry
*b
)
1898 struct hpp_dynamic_entry
*hde
;
1899 struct format_field
*field
;
1900 unsigned offset
, size
;
1902 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
1905 update_dynamic_len(hde
, a
);
1910 if (field
->flags
& FIELD_IS_DYNAMIC
) {
1911 unsigned long long dyn
;
1913 pevent_read_number_field(field
, a
->raw_data
, &dyn
);
1914 offset
= dyn
& 0xffff;
1915 size
= (dyn
>> 16) & 0xffff;
1917 /* record max width for output */
1918 if (size
> hde
->dynamic_len
)
1919 hde
->dynamic_len
= size
;
1921 offset
= field
->offset
;
1925 return memcmp(a
->raw_data
+ offset
, b
->raw_data
+ offset
, size
);
1928 bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt
*fmt
)
1930 return fmt
->cmp
== __sort__hde_cmp
;
1933 static bool __sort__hde_equal(struct perf_hpp_fmt
*a
, struct perf_hpp_fmt
*b
)
1935 struct hpp_dynamic_entry
*hde_a
;
1936 struct hpp_dynamic_entry
*hde_b
;
1938 if (!perf_hpp__is_dynamic_entry(a
) || !perf_hpp__is_dynamic_entry(b
))
1941 hde_a
= container_of(a
, struct hpp_dynamic_entry
, hpp
);
1942 hde_b
= container_of(b
, struct hpp_dynamic_entry
, hpp
);
1944 return hde_a
->field
== hde_b
->field
;
1947 static void hde_free(struct perf_hpp_fmt
*fmt
)
1949 struct hpp_dynamic_entry
*hde
;
1951 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
1955 static struct hpp_dynamic_entry
*
1956 __alloc_dynamic_entry(struct perf_evsel
*evsel
, struct format_field
*field
,
1959 struct hpp_dynamic_entry
*hde
;
1961 hde
= malloc(sizeof(*hde
));
1963 pr_debug("Memory allocation failed\n");
1969 hde
->dynamic_len
= 0;
1971 hde
->hpp
.name
= field
->name
;
1972 hde
->hpp
.header
= __sort__hde_header
;
1973 hde
->hpp
.width
= __sort__hde_width
;
1974 hde
->hpp
.entry
= __sort__hde_entry
;
1975 hde
->hpp
.color
= NULL
;
1977 hde
->hpp
.cmp
= __sort__hde_cmp
;
1978 hde
->hpp
.collapse
= __sort__hde_cmp
;
1979 hde
->hpp
.sort
= __sort__hde_cmp
;
1980 hde
->hpp
.equal
= __sort__hde_equal
;
1981 hde
->hpp
.free
= hde_free
;
1983 INIT_LIST_HEAD(&hde
->hpp
.list
);
1984 INIT_LIST_HEAD(&hde
->hpp
.sort_list
);
1985 hde
->hpp
.elide
= false;
1987 hde
->hpp
.user_len
= 0;
1988 hde
->hpp
.level
= level
;
1993 struct perf_hpp_fmt
*perf_hpp_fmt__dup(struct perf_hpp_fmt
*fmt
)
1995 struct perf_hpp_fmt
*new_fmt
= NULL
;
1997 if (perf_hpp__is_sort_entry(fmt
)) {
1998 struct hpp_sort_entry
*hse
, *new_hse
;
2000 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
2001 new_hse
= memdup(hse
, sizeof(*hse
));
2003 new_fmt
= &new_hse
->hpp
;
2004 } else if (perf_hpp__is_dynamic_entry(fmt
)) {
2005 struct hpp_dynamic_entry
*hde
, *new_hde
;
2007 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
2008 new_hde
= memdup(hde
, sizeof(*hde
));
2010 new_fmt
= &new_hde
->hpp
;
2012 new_fmt
= memdup(fmt
, sizeof(*fmt
));
2015 INIT_LIST_HEAD(&new_fmt
->list
);
2016 INIT_LIST_HEAD(&new_fmt
->sort_list
);
2021 static int parse_field_name(char *str
, char **event
, char **field
, char **opt
)
2023 char *event_name
, *field_name
, *opt_name
;
2026 field_name
= strchr(str
, '.');
2029 *field_name
++ = '\0';
2035 opt_name
= strchr(field_name
, '/');
2039 *event
= event_name
;
2040 *field
= field_name
;
2046 /* find match evsel using a given event name. The event name can be:
2047 * 1. '%' + event index (e.g. '%1' for first event)
2048 * 2. full event name (e.g. sched:sched_switch)
2049 * 3. partial event name (should not contain ':')
2051 static struct perf_evsel
*find_evsel(struct perf_evlist
*evlist
, char *event_name
)
2053 struct perf_evsel
*evsel
= NULL
;
2054 struct perf_evsel
*pos
;
2058 if (event_name
[0] == '%') {
2059 int nr
= strtol(event_name
+1, NULL
, 0);
2061 if (nr
> evlist
->nr_entries
)
2064 evsel
= perf_evlist__first(evlist
);
2066 evsel
= perf_evsel__next(evsel
);
2071 full_name
= !!strchr(event_name
, ':');
2072 evlist__for_each_entry(evlist
, pos
) {
2074 if (full_name
&& !strcmp(pos
->name
, event_name
))
2077 if (!full_name
&& strstr(pos
->name
, event_name
)) {
2079 pr_debug("'%s' event is ambiguous: it can be %s or %s\n",
2080 event_name
, evsel
->name
, pos
->name
);
2090 static int __dynamic_dimension__add(struct perf_evsel
*evsel
,
2091 struct format_field
*field
,
2092 bool raw_trace
, int level
)
2094 struct hpp_dynamic_entry
*hde
;
2096 hde
= __alloc_dynamic_entry(evsel
, field
, level
);
2100 hde
->raw_trace
= raw_trace
;
2102 perf_hpp__register_sort_field(&hde
->hpp
);
2106 static int add_evsel_fields(struct perf_evsel
*evsel
, bool raw_trace
, int level
)
2109 struct format_field
*field
;
2111 field
= evsel
->tp_format
->format
.fields
;
2113 ret
= __dynamic_dimension__add(evsel
, field
, raw_trace
, level
);
2117 field
= field
->next
;
2122 static int add_all_dynamic_fields(struct perf_evlist
*evlist
, bool raw_trace
,
2126 struct perf_evsel
*evsel
;
2128 evlist__for_each_entry(evlist
, evsel
) {
2129 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
)
2132 ret
= add_evsel_fields(evsel
, raw_trace
, level
);
2139 static int add_all_matching_fields(struct perf_evlist
*evlist
,
2140 char *field_name
, bool raw_trace
, int level
)
2143 struct perf_evsel
*evsel
;
2144 struct format_field
*field
;
2146 evlist__for_each_entry(evlist
, evsel
) {
2147 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
)
2150 field
= pevent_find_any_field(evsel
->tp_format
, field_name
);
2154 ret
= __dynamic_dimension__add(evsel
, field
, raw_trace
, level
);
2161 static int add_dynamic_entry(struct perf_evlist
*evlist
, const char *tok
,
2164 char *str
, *event_name
, *field_name
, *opt_name
;
2165 struct perf_evsel
*evsel
;
2166 struct format_field
*field
;
2167 bool raw_trace
= symbol_conf
.raw_trace
;
2177 if (parse_field_name(str
, &event_name
, &field_name
, &opt_name
) < 0) {
2183 if (strcmp(opt_name
, "raw")) {
2184 pr_debug("unsupported field option %s\n", opt_name
);
2191 if (!strcmp(field_name
, "trace_fields")) {
2192 ret
= add_all_dynamic_fields(evlist
, raw_trace
, level
);
2196 if (event_name
== NULL
) {
2197 ret
= add_all_matching_fields(evlist
, field_name
, raw_trace
, level
);
2201 evsel
= find_evsel(evlist
, event_name
);
2202 if (evsel
== NULL
) {
2203 pr_debug("Cannot find event: %s\n", event_name
);
2208 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
) {
2209 pr_debug("%s is not a tracepoint event\n", event_name
);
2214 if (!strcmp(field_name
, "*")) {
2215 ret
= add_evsel_fields(evsel
, raw_trace
, level
);
2217 field
= pevent_find_any_field(evsel
->tp_format
, field_name
);
2218 if (field
== NULL
) {
2219 pr_debug("Cannot find event field for %s.%s\n",
2220 event_name
, field_name
);
2224 ret
= __dynamic_dimension__add(evsel
, field
, raw_trace
, level
);
2232 static int __sort_dimension__add(struct sort_dimension
*sd
,
2233 struct perf_hpp_list
*list
,
2239 if (__sort_dimension__add_hpp_sort(sd
, list
, level
) < 0)
2242 if (sd
->entry
->se_collapse
)
2243 list
->need_collapse
= 1;
2250 static int __hpp_dimension__add(struct hpp_dimension
*hd
,
2251 struct perf_hpp_list
*list
,
2254 struct perf_hpp_fmt
*fmt
;
2259 fmt
= __hpp_dimension__alloc_hpp(hd
, level
);
2264 perf_hpp_list__register_sort_field(list
, fmt
);
2268 static int __sort_dimension__add_output(struct perf_hpp_list
*list
,
2269 struct sort_dimension
*sd
)
2274 if (__sort_dimension__add_hpp_output(sd
, list
) < 0)
2281 static int __hpp_dimension__add_output(struct perf_hpp_list
*list
,
2282 struct hpp_dimension
*hd
)
2284 struct perf_hpp_fmt
*fmt
;
2289 fmt
= __hpp_dimension__alloc_hpp(hd
, 0);
2294 perf_hpp_list__column_register(list
, fmt
);
2298 int hpp_dimension__add_output(unsigned col
)
2300 BUG_ON(col
>= PERF_HPP__MAX_INDEX
);
2301 return __hpp_dimension__add_output(&perf_hpp_list
, &hpp_sort_dimensions
[col
]);
2304 static int sort_dimension__add(struct perf_hpp_list
*list
, const char *tok
,
2305 struct perf_evlist
*evlist
,
2310 for (i
= 0; i
< ARRAY_SIZE(common_sort_dimensions
); i
++) {
2311 struct sort_dimension
*sd
= &common_sort_dimensions
[i
];
2313 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2316 if (sd
->entry
== &sort_parent
) {
2317 int ret
= regcomp(&parent_regex
, parent_pattern
, REG_EXTENDED
);
2321 regerror(ret
, &parent_regex
, err
, sizeof(err
));
2322 pr_err("Invalid regex: %s\n%s", parent_pattern
, err
);
2326 } else if (sd
->entry
== &sort_sym
) {
2329 * perf diff displays the performance difference amongst
2330 * two or more perf.data files. Those files could come
2331 * from different binaries. So we should not compare
2332 * their ips, but the name of symbol.
2334 if (sort__mode
== SORT_MODE__DIFF
)
2335 sd
->entry
->se_collapse
= sort__sym_sort
;
2337 } else if (sd
->entry
== &sort_dso
) {
2339 } else if (sd
->entry
== &sort_socket
) {
2341 } else if (sd
->entry
== &sort_thread
) {
2343 } else if (sd
->entry
== &sort_comm
) {
2347 return __sort_dimension__add(sd
, list
, level
);
2350 for (i
= 0; i
< ARRAY_SIZE(hpp_sort_dimensions
); i
++) {
2351 struct hpp_dimension
*hd
= &hpp_sort_dimensions
[i
];
2353 if (strncasecmp(tok
, hd
->name
, strlen(tok
)))
2356 return __hpp_dimension__add(hd
, list
, level
);
2359 for (i
= 0; i
< ARRAY_SIZE(bstack_sort_dimensions
); i
++) {
2360 struct sort_dimension
*sd
= &bstack_sort_dimensions
[i
];
2362 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2365 if (sort__mode
!= SORT_MODE__BRANCH
)
2368 if (sd
->entry
== &sort_sym_from
|| sd
->entry
== &sort_sym_to
)
2371 __sort_dimension__add(sd
, list
, level
);
2375 for (i
= 0; i
< ARRAY_SIZE(memory_sort_dimensions
); i
++) {
2376 struct sort_dimension
*sd
= &memory_sort_dimensions
[i
];
2378 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2381 if (sort__mode
!= SORT_MODE__MEMORY
)
2384 if (sd
->entry
== &sort_mem_dcacheline
&& cacheline_size
== 0)
2387 if (sd
->entry
== &sort_mem_daddr_sym
)
2390 __sort_dimension__add(sd
, list
, level
);
2394 if (!add_dynamic_entry(evlist
, tok
, level
))
2400 static int setup_sort_list(struct perf_hpp_list
*list
, char *str
,
2401 struct perf_evlist
*evlist
)
2407 bool in_group
= false;
2411 tmp
= strpbrk(str
, "{}, ");
2416 next_level
= level
+ 1;
2420 else if (*tmp
== '}')
2428 ret
= sort_dimension__add(list
, tok
, evlist
, level
);
2429 if (ret
== -EINVAL
) {
2430 if (!cacheline_size
&& !strncasecmp(tok
, "dcacheline", strlen(tok
)))
2431 error("The \"dcacheline\" --sort key needs to know the cacheline size and it couldn't be determined on this system");
2433 error("Invalid --sort key: `%s'", tok
);
2435 } else if (ret
== -ESRCH
) {
2436 error("Unknown --sort key: `%s'", tok
);
2447 static const char *get_default_sort_order(struct perf_evlist
*evlist
)
2449 const char *default_sort_orders
[] = {
2451 default_branch_sort_order
,
2452 default_mem_sort_order
,
2453 default_top_sort_order
,
2454 default_diff_sort_order
,
2455 default_tracepoint_sort_order
,
2457 bool use_trace
= true;
2458 struct perf_evsel
*evsel
;
2460 BUG_ON(sort__mode
>= ARRAY_SIZE(default_sort_orders
));
2465 evlist__for_each_entry(evlist
, evsel
) {
2466 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
) {
2473 sort__mode
= SORT_MODE__TRACEPOINT
;
2474 if (symbol_conf
.raw_trace
)
2475 return "trace_fields";
2478 return default_sort_orders
[sort__mode
];
2481 static int setup_sort_order(struct perf_evlist
*evlist
)
2483 char *new_sort_order
;
2486 * Append '+'-prefixed sort order to the default sort
2489 if (!sort_order
|| is_strict_order(sort_order
))
2492 if (sort_order
[1] == '\0') {
2493 error("Invalid --sort key: `+'");
2498 * We allocate new sort_order string, but we never free it,
2499 * because it's checked over the rest of the code.
2501 if (asprintf(&new_sort_order
, "%s,%s",
2502 get_default_sort_order(evlist
), sort_order
+ 1) < 0) {
2503 error("Not enough memory to set up --sort");
2507 sort_order
= new_sort_order
;
2512 * Adds 'pre,' prefix into 'str' is 'pre' is
2513 * not already part of 'str'.
2515 static char *prefix_if_not_in(const char *pre
, char *str
)
2519 if (!str
|| strstr(str
, pre
))
2522 if (asprintf(&n
, "%s,%s", pre
, str
) < 0)
2529 static char *setup_overhead(char *keys
)
2531 if (sort__mode
== SORT_MODE__DIFF
)
2534 keys
= prefix_if_not_in("overhead", keys
);
2536 if (symbol_conf
.cumulate_callchain
)
2537 keys
= prefix_if_not_in("overhead_children", keys
);
2542 static int __setup_sorting(struct perf_evlist
*evlist
)
2545 const char *sort_keys
;
2548 ret
= setup_sort_order(evlist
);
2552 sort_keys
= sort_order
;
2553 if (sort_keys
== NULL
) {
2554 if (is_strict_order(field_order
)) {
2556 * If user specified field order but no sort order,
2557 * we'll honor it and not add default sort orders.
2562 sort_keys
= get_default_sort_order(evlist
);
2565 str
= strdup(sort_keys
);
2567 error("Not enough memory to setup sort keys");
2572 * Prepend overhead fields for backward compatibility.
2574 if (!is_strict_order(field_order
)) {
2575 str
= setup_overhead(str
);
2577 error("Not enough memory to setup overhead keys");
2582 ret
= setup_sort_list(&perf_hpp_list
, str
, evlist
);
2588 void perf_hpp__set_elide(int idx
, bool elide
)
2590 struct perf_hpp_fmt
*fmt
;
2591 struct hpp_sort_entry
*hse
;
2593 perf_hpp_list__for_each_format(&perf_hpp_list
, fmt
) {
2594 if (!perf_hpp__is_sort_entry(fmt
))
2597 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
2598 if (hse
->se
->se_width_idx
== idx
) {
2605 static bool __get_elide(struct strlist
*list
, const char *list_name
, FILE *fp
)
2607 if (list
&& strlist__nr_entries(list
) == 1) {
2609 fprintf(fp
, "# %s: %s\n", list_name
,
2610 strlist__entry(list
, 0)->s
);
2616 static bool get_elide(int idx
, FILE *output
)
2620 return __get_elide(symbol_conf
.sym_list
, "symbol", output
);
2622 return __get_elide(symbol_conf
.dso_list
, "dso", output
);
2624 return __get_elide(symbol_conf
.comm_list
, "comm", output
);
2629 if (sort__mode
!= SORT_MODE__BRANCH
)
2633 case HISTC_SYMBOL_FROM
:
2634 return __get_elide(symbol_conf
.sym_from_list
, "sym_from", output
);
2635 case HISTC_SYMBOL_TO
:
2636 return __get_elide(symbol_conf
.sym_to_list
, "sym_to", output
);
2637 case HISTC_DSO_FROM
:
2638 return __get_elide(symbol_conf
.dso_from_list
, "dso_from", output
);
2640 return __get_elide(symbol_conf
.dso_to_list
, "dso_to", output
);
2648 void sort__setup_elide(FILE *output
)
2650 struct perf_hpp_fmt
*fmt
;
2651 struct hpp_sort_entry
*hse
;
2653 perf_hpp_list__for_each_format(&perf_hpp_list
, fmt
) {
2654 if (!perf_hpp__is_sort_entry(fmt
))
2657 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
2658 fmt
->elide
= get_elide(hse
->se
->se_width_idx
, output
);
2662 * It makes no sense to elide all of sort entries.
2663 * Just revert them to show up again.
2665 perf_hpp_list__for_each_format(&perf_hpp_list
, fmt
) {
2666 if (!perf_hpp__is_sort_entry(fmt
))
2673 perf_hpp_list__for_each_format(&perf_hpp_list
, fmt
) {
2674 if (!perf_hpp__is_sort_entry(fmt
))
2681 static int output_field_add(struct perf_hpp_list
*list
, char *tok
)
2685 for (i
= 0; i
< ARRAY_SIZE(common_sort_dimensions
); i
++) {
2686 struct sort_dimension
*sd
= &common_sort_dimensions
[i
];
2688 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2691 return __sort_dimension__add_output(list
, sd
);
2694 for (i
= 0; i
< ARRAY_SIZE(hpp_sort_dimensions
); i
++) {
2695 struct hpp_dimension
*hd
= &hpp_sort_dimensions
[i
];
2697 if (strncasecmp(tok
, hd
->name
, strlen(tok
)))
2700 return __hpp_dimension__add_output(list
, hd
);
2703 for (i
= 0; i
< ARRAY_SIZE(bstack_sort_dimensions
); i
++) {
2704 struct sort_dimension
*sd
= &bstack_sort_dimensions
[i
];
2706 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2709 return __sort_dimension__add_output(list
, sd
);
2712 for (i
= 0; i
< ARRAY_SIZE(memory_sort_dimensions
); i
++) {
2713 struct sort_dimension
*sd
= &memory_sort_dimensions
[i
];
2715 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2718 return __sort_dimension__add_output(list
, sd
);
2724 static int setup_output_list(struct perf_hpp_list
*list
, char *str
)
2729 for (tok
= strtok_r(str
, ", ", &tmp
);
2730 tok
; tok
= strtok_r(NULL
, ", ", &tmp
)) {
2731 ret
= output_field_add(list
, tok
);
2732 if (ret
== -EINVAL
) {
2733 error("Invalid --fields key: `%s'", tok
);
2735 } else if (ret
== -ESRCH
) {
2736 error("Unknown --fields key: `%s'", tok
);
2744 static void reset_dimensions(void)
2748 for (i
= 0; i
< ARRAY_SIZE(common_sort_dimensions
); i
++)
2749 common_sort_dimensions
[i
].taken
= 0;
2751 for (i
= 0; i
< ARRAY_SIZE(hpp_sort_dimensions
); i
++)
2752 hpp_sort_dimensions
[i
].taken
= 0;
2754 for (i
= 0; i
< ARRAY_SIZE(bstack_sort_dimensions
); i
++)
2755 bstack_sort_dimensions
[i
].taken
= 0;
2757 for (i
= 0; i
< ARRAY_SIZE(memory_sort_dimensions
); i
++)
2758 memory_sort_dimensions
[i
].taken
= 0;
2761 bool is_strict_order(const char *order
)
2763 return order
&& (*order
!= '+');
2766 static int __setup_output_field(void)
2771 if (field_order
== NULL
)
2774 strp
= str
= strdup(field_order
);
2776 error("Not enough memory to setup output fields");
2780 if (!is_strict_order(field_order
))
2783 if (!strlen(strp
)) {
2784 error("Invalid --fields key: `+'");
2788 ret
= setup_output_list(&perf_hpp_list
, strp
);
2795 int setup_sorting(struct perf_evlist
*evlist
)
2799 err
= __setup_sorting(evlist
);
2803 if (parent_pattern
!= default_parent_pattern
) {
2804 err
= sort_dimension__add(&perf_hpp_list
, "parent", evlist
, -1);
2812 * perf diff doesn't use default hpp output fields.
2814 if (sort__mode
!= SORT_MODE__DIFF
)
2817 err
= __setup_output_field();
2821 /* copy sort keys to output fields */
2822 perf_hpp__setup_output_field(&perf_hpp_list
);
2823 /* and then copy output fields to sort keys */
2824 perf_hpp__append_sort_keys(&perf_hpp_list
);
2826 /* setup hists-specific output fields */
2827 if (perf_hpp__setup_hists_formats(&perf_hpp_list
, evlist
) < 0)
2833 void reset_output_field(void)
2835 perf_hpp_list
.need_collapse
= 0;
2836 perf_hpp_list
.parent
= 0;
2837 perf_hpp_list
.sym
= 0;
2838 perf_hpp_list
.dso
= 0;
2844 perf_hpp__reset_output_field(&perf_hpp_list
);