1 // SPDX-License-Identifier: GPL-2.0
5 #include <linux/mman.h>
6 #include <linux/time64.h>
17 #include <traceevent/event-parse.h>
18 #include "mem-events.h"
20 #include "time-utils.h"
21 #include <linux/kernel.h>
24 const char default_parent_pattern
[] = "^sys_|^do_page_fault";
25 const char *parent_pattern
= default_parent_pattern
;
26 const char *default_sort_order
= "comm,dso,symbol";
27 const char default_branch_sort_order
[] = "comm,dso_from,symbol_from,symbol_to,cycles";
28 const char default_mem_sort_order
[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
29 const char default_top_sort_order
[] = "dso,symbol";
30 const char default_diff_sort_order
[] = "dso,symbol";
31 const char default_tracepoint_sort_order
[] = "trace";
32 const char *sort_order
;
33 const char *field_order
;
34 regex_t ignore_callees_regex
;
35 int have_ignore_callees
= 0;
36 enum sort_mode sort__mode
= SORT_MODE__NORMAL
;
39 * Replaces all occurrences of a char used with the:
41 * -t, --field-separator
43 * option, that uses a special separator character and don't pad with spaces,
44 * replacing all occurrences of this separator in symbol names (and other
45 * output) with a '.' character, that thus it's the only non valid separator.
47 static int repsep_snprintf(char *bf
, size_t size
, const char *fmt
, ...)
53 n
= vsnprintf(bf
, size
, fmt
, ap
);
54 if (symbol_conf
.field_sep
&& n
> 0) {
58 sep
= strchr(sep
, *symbol_conf
.field_sep
);
71 static int64_t cmp_null(const void *l
, const void *r
)
84 sort__thread_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
86 return right
->thread
->tid
- left
->thread
->tid
;
89 static int hist_entry__thread_snprintf(struct hist_entry
*he
, char *bf
,
90 size_t size
, unsigned int width
)
92 const char *comm
= thread__comm_str(he
->thread
);
94 width
= max(7U, width
) - 8;
95 return repsep_snprintf(bf
, size
, "%7d:%-*.*s", he
->thread
->tid
,
96 width
, width
, comm
?: "");
99 static int hist_entry__thread_filter(struct hist_entry
*he
, int type
, const void *arg
)
101 const struct thread
*th
= arg
;
103 if (type
!= HIST_FILTER__THREAD
)
106 return th
&& he
->thread
!= th
;
109 struct sort_entry sort_thread
= {
110 .se_header
= " Pid:Command",
111 .se_cmp
= sort__thread_cmp
,
112 .se_snprintf
= hist_entry__thread_snprintf
,
113 .se_filter
= hist_entry__thread_filter
,
114 .se_width_idx
= HISTC_THREAD
,
120 * We can't use pointer comparison in functions below,
121 * because it gives different results based on pointer
122 * values, which could break some sorting assumptions.
125 sort__comm_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
127 return strcmp(comm__str(right
->comm
), comm__str(left
->comm
));
131 sort__comm_collapse(struct hist_entry
*left
, struct hist_entry
*right
)
133 return strcmp(comm__str(right
->comm
), comm__str(left
->comm
));
137 sort__comm_sort(struct hist_entry
*left
, struct hist_entry
*right
)
139 return strcmp(comm__str(right
->comm
), comm__str(left
->comm
));
142 static int hist_entry__comm_snprintf(struct hist_entry
*he
, char *bf
,
143 size_t size
, unsigned int width
)
145 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, comm__str(he
->comm
));
148 struct sort_entry sort_comm
= {
149 .se_header
= "Command",
150 .se_cmp
= sort__comm_cmp
,
151 .se_collapse
= sort__comm_collapse
,
152 .se_sort
= sort__comm_sort
,
153 .se_snprintf
= hist_entry__comm_snprintf
,
154 .se_filter
= hist_entry__thread_filter
,
155 .se_width_idx
= HISTC_COMM
,
160 static int64_t _sort__dso_cmp(struct map
*map_l
, struct map
*map_r
)
162 struct dso
*dso_l
= map_l
? map_l
->dso
: NULL
;
163 struct dso
*dso_r
= map_r
? map_r
->dso
: NULL
;
164 const char *dso_name_l
, *dso_name_r
;
166 if (!dso_l
|| !dso_r
)
167 return cmp_null(dso_r
, dso_l
);
170 dso_name_l
= dso_l
->long_name
;
171 dso_name_r
= dso_r
->long_name
;
173 dso_name_l
= dso_l
->short_name
;
174 dso_name_r
= dso_r
->short_name
;
177 return strcmp(dso_name_l
, dso_name_r
);
181 sort__dso_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
183 return _sort__dso_cmp(right
->ms
.map
, left
->ms
.map
);
186 static int _hist_entry__dso_snprintf(struct map
*map
, char *bf
,
187 size_t size
, unsigned int width
)
189 if (map
&& map
->dso
) {
190 const char *dso_name
= verbose
> 0 ? map
->dso
->long_name
:
191 map
->dso
->short_name
;
192 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, dso_name
);
195 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, "[unknown]");
198 static int hist_entry__dso_snprintf(struct hist_entry
*he
, char *bf
,
199 size_t size
, unsigned int width
)
201 return _hist_entry__dso_snprintf(he
->ms
.map
, bf
, size
, width
);
204 static int hist_entry__dso_filter(struct hist_entry
*he
, int type
, const void *arg
)
206 const struct dso
*dso
= arg
;
208 if (type
!= HIST_FILTER__DSO
)
211 return dso
&& (!he
->ms
.map
|| he
->ms
.map
->dso
!= dso
);
214 struct sort_entry sort_dso
= {
215 .se_header
= "Shared Object",
216 .se_cmp
= sort__dso_cmp
,
217 .se_snprintf
= hist_entry__dso_snprintf
,
218 .se_filter
= hist_entry__dso_filter
,
219 .se_width_idx
= HISTC_DSO
,
224 static int64_t _sort__addr_cmp(u64 left_ip
, u64 right_ip
)
226 return (int64_t)(right_ip
- left_ip
);
229 static int64_t _sort__sym_cmp(struct symbol
*sym_l
, struct symbol
*sym_r
)
231 if (!sym_l
|| !sym_r
)
232 return cmp_null(sym_l
, sym_r
);
237 if (sym_l
->inlined
|| sym_r
->inlined
) {
238 int ret
= strcmp(sym_l
->name
, sym_r
->name
);
242 if ((sym_l
->start
<= sym_r
->end
) && (sym_l
->end
>= sym_r
->start
))
246 if (sym_l
->start
!= sym_r
->start
)
247 return (int64_t)(sym_r
->start
- sym_l
->start
);
249 return (int64_t)(sym_r
->end
- sym_l
->end
);
253 sort__sym_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
257 if (!left
->ms
.sym
&& !right
->ms
.sym
)
258 return _sort__addr_cmp(left
->ip
, right
->ip
);
261 * comparing symbol address alone is not enough since it's a
262 * relative address within a dso.
264 if (!hists__has(left
->hists
, dso
) || hists__has(right
->hists
, dso
)) {
265 ret
= sort__dso_cmp(left
, right
);
270 return _sort__sym_cmp(left
->ms
.sym
, right
->ms
.sym
);
274 sort__sym_sort(struct hist_entry
*left
, struct hist_entry
*right
)
276 if (!left
->ms
.sym
|| !right
->ms
.sym
)
277 return cmp_null(left
->ms
.sym
, right
->ms
.sym
);
279 return strcmp(right
->ms
.sym
->name
, left
->ms
.sym
->name
);
282 static int _hist_entry__sym_snprintf(struct map
*map
, struct symbol
*sym
,
283 u64 ip
, char level
, char *bf
, size_t size
,
289 char o
= map
? dso__symtab_origin(map
->dso
) : '!';
290 ret
+= repsep_snprintf(bf
, size
, "%-#*llx %c ",
291 BITS_PER_LONG
/ 4 + 2, ip
, o
);
294 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "[%c] ", level
);
296 if (sym
->type
== STT_OBJECT
) {
297 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%s", sym
->name
);
298 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "+0x%llx",
299 ip
- map
->unmap_ip(map
, sym
->start
));
301 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%.*s",
305 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
,
309 size_t len
= BITS_PER_LONG
/ 4;
310 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-#.*llx",
317 static int hist_entry__sym_snprintf(struct hist_entry
*he
, char *bf
,
318 size_t size
, unsigned int width
)
320 return _hist_entry__sym_snprintf(he
->ms
.map
, he
->ms
.sym
, he
->ip
,
321 he
->level
, bf
, size
, width
);
324 static int hist_entry__sym_filter(struct hist_entry
*he
, int type
, const void *arg
)
326 const char *sym
= arg
;
328 if (type
!= HIST_FILTER__SYMBOL
)
331 return sym
&& (!he
->ms
.sym
|| !strstr(he
->ms
.sym
->name
, sym
));
334 struct sort_entry sort_sym
= {
335 .se_header
= "Symbol",
336 .se_cmp
= sort__sym_cmp
,
337 .se_sort
= sort__sym_sort
,
338 .se_snprintf
= hist_entry__sym_snprintf
,
339 .se_filter
= hist_entry__sym_filter
,
340 .se_width_idx
= HISTC_SYMBOL
,
345 char *hist_entry__srcline(struct hist_entry
*he
)
347 return map__srcline(he
->ms
.map
, he
->ip
, he
->ms
.sym
);
351 sort__srcline_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
354 left
->srcline
= hist_entry__srcline(left
);
356 right
->srcline
= hist_entry__srcline(right
);
358 return strcmp(right
->srcline
, left
->srcline
);
361 static int hist_entry__srcline_snprintf(struct hist_entry
*he
, char *bf
,
362 size_t size
, unsigned int width
)
365 he
->srcline
= hist_entry__srcline(he
);
367 return repsep_snprintf(bf
, size
, "%-.*s", width
, he
->srcline
);
370 struct sort_entry sort_srcline
= {
371 .se_header
= "Source:Line",
372 .se_cmp
= sort__srcline_cmp
,
373 .se_snprintf
= hist_entry__srcline_snprintf
,
374 .se_width_idx
= HISTC_SRCLINE
,
377 /* --sort srcline_from */
379 static char *addr_map_symbol__srcline(struct addr_map_symbol
*ams
)
381 return map__srcline(ams
->map
, ams
->al_addr
, ams
->sym
);
385 sort__srcline_from_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
387 if (!left
->branch_info
->srcline_from
)
388 left
->branch_info
->srcline_from
= addr_map_symbol__srcline(&left
->branch_info
->from
);
390 if (!right
->branch_info
->srcline_from
)
391 right
->branch_info
->srcline_from
= addr_map_symbol__srcline(&right
->branch_info
->from
);
393 return strcmp(right
->branch_info
->srcline_from
, left
->branch_info
->srcline_from
);
396 static int hist_entry__srcline_from_snprintf(struct hist_entry
*he
, char *bf
,
397 size_t size
, unsigned int width
)
399 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, he
->branch_info
->srcline_from
);
402 struct sort_entry sort_srcline_from
= {
403 .se_header
= "From Source:Line",
404 .se_cmp
= sort__srcline_from_cmp
,
405 .se_snprintf
= hist_entry__srcline_from_snprintf
,
406 .se_width_idx
= HISTC_SRCLINE_FROM
,
409 /* --sort srcline_to */
412 sort__srcline_to_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
414 if (!left
->branch_info
->srcline_to
)
415 left
->branch_info
->srcline_to
= addr_map_symbol__srcline(&left
->branch_info
->to
);
417 if (!right
->branch_info
->srcline_to
)
418 right
->branch_info
->srcline_to
= addr_map_symbol__srcline(&right
->branch_info
->to
);
420 return strcmp(right
->branch_info
->srcline_to
, left
->branch_info
->srcline_to
);
423 static int hist_entry__srcline_to_snprintf(struct hist_entry
*he
, char *bf
,
424 size_t size
, unsigned int width
)
426 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, he
->branch_info
->srcline_to
);
429 struct sort_entry sort_srcline_to
= {
430 .se_header
= "To Source:Line",
431 .se_cmp
= sort__srcline_to_cmp
,
432 .se_snprintf
= hist_entry__srcline_to_snprintf
,
433 .se_width_idx
= HISTC_SRCLINE_TO
,
436 static int hist_entry__sym_ipc_snprintf(struct hist_entry
*he
, char *bf
,
437 size_t size
, unsigned int width
)
440 struct symbol
*sym
= he
->ms
.sym
;
441 struct annotation
*notes
;
442 double ipc
= 0.0, coverage
= 0.0;
446 return repsep_snprintf(bf
, size
, "%-*s", width
, "-");
448 notes
= symbol__annotation(sym
);
450 if (notes
->hit_cycles
)
451 ipc
= notes
->hit_insn
/ ((double)notes
->hit_cycles
);
453 if (notes
->total_insn
) {
454 coverage
= notes
->cover_insn
* 100.0 /
455 ((double)notes
->total_insn
);
458 snprintf(tmp
, sizeof(tmp
), "%-5.2f [%5.1f%%]", ipc
, coverage
);
459 return repsep_snprintf(bf
, size
, "%-*s", width
, tmp
);
462 struct sort_entry sort_sym_ipc
= {
463 .se_header
= "IPC [IPC Coverage]",
464 .se_cmp
= sort__sym_cmp
,
465 .se_snprintf
= hist_entry__sym_ipc_snprintf
,
466 .se_width_idx
= HISTC_SYMBOL_IPC
,
469 static int hist_entry__sym_ipc_null_snprintf(struct hist_entry
*he
471 char *bf
, size_t size
,
476 snprintf(tmp
, sizeof(tmp
), "%-5s %2s", "-", "-");
477 return repsep_snprintf(bf
, size
, "%-*s", width
, tmp
);
480 struct sort_entry sort_sym_ipc_null
= {
481 .se_header
= "IPC [IPC Coverage]",
482 .se_cmp
= sort__sym_cmp
,
483 .se_snprintf
= hist_entry__sym_ipc_null_snprintf
,
484 .se_width_idx
= HISTC_SYMBOL_IPC
,
489 static char no_srcfile
[1];
491 static char *hist_entry__get_srcfile(struct hist_entry
*e
)
494 struct map
*map
= e
->ms
.map
;
499 sf
= __get_srcline(map
->dso
, map__rip_2objdump(map
, e
->ip
),
500 e
->ms
.sym
, false, true, true, e
->ip
);
501 if (!strcmp(sf
, SRCLINE_UNKNOWN
))
513 sort__srcfile_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
516 left
->srcfile
= hist_entry__get_srcfile(left
);
518 right
->srcfile
= hist_entry__get_srcfile(right
);
520 return strcmp(right
->srcfile
, left
->srcfile
);
523 static int hist_entry__srcfile_snprintf(struct hist_entry
*he
, char *bf
,
524 size_t size
, unsigned int width
)
527 he
->srcfile
= hist_entry__get_srcfile(he
);
529 return repsep_snprintf(bf
, size
, "%-.*s", width
, he
->srcfile
);
532 struct sort_entry sort_srcfile
= {
533 .se_header
= "Source File",
534 .se_cmp
= sort__srcfile_cmp
,
535 .se_snprintf
= hist_entry__srcfile_snprintf
,
536 .se_width_idx
= HISTC_SRCFILE
,
542 sort__parent_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
544 struct symbol
*sym_l
= left
->parent
;
545 struct symbol
*sym_r
= right
->parent
;
547 if (!sym_l
|| !sym_r
)
548 return cmp_null(sym_l
, sym_r
);
550 return strcmp(sym_r
->name
, sym_l
->name
);
553 static int hist_entry__parent_snprintf(struct hist_entry
*he
, char *bf
,
554 size_t size
, unsigned int width
)
556 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
,
557 he
->parent
? he
->parent
->name
: "[other]");
560 struct sort_entry sort_parent
= {
561 .se_header
= "Parent symbol",
562 .se_cmp
= sort__parent_cmp
,
563 .se_snprintf
= hist_entry__parent_snprintf
,
564 .se_width_idx
= HISTC_PARENT
,
570 sort__cpu_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
572 return right
->cpu
- left
->cpu
;
575 static int hist_entry__cpu_snprintf(struct hist_entry
*he
, char *bf
,
576 size_t size
, unsigned int width
)
578 return repsep_snprintf(bf
, size
, "%*.*d", width
, width
, he
->cpu
);
581 struct sort_entry sort_cpu
= {
583 .se_cmp
= sort__cpu_cmp
,
584 .se_snprintf
= hist_entry__cpu_snprintf
,
585 .se_width_idx
= HISTC_CPU
,
588 /* --sort cgroup_id */
590 static int64_t _sort__cgroup_dev_cmp(u64 left_dev
, u64 right_dev
)
592 return (int64_t)(right_dev
- left_dev
);
595 static int64_t _sort__cgroup_inode_cmp(u64 left_ino
, u64 right_ino
)
597 return (int64_t)(right_ino
- left_ino
);
601 sort__cgroup_id_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
605 ret
= _sort__cgroup_dev_cmp(right
->cgroup_id
.dev
, left
->cgroup_id
.dev
);
609 return _sort__cgroup_inode_cmp(right
->cgroup_id
.ino
,
610 left
->cgroup_id
.ino
);
613 static int hist_entry__cgroup_id_snprintf(struct hist_entry
*he
,
614 char *bf
, size_t size
,
615 unsigned int width __maybe_unused
)
617 return repsep_snprintf(bf
, size
, "%lu/0x%lx", he
->cgroup_id
.dev
,
621 struct sort_entry sort_cgroup_id
= {
622 .se_header
= "cgroup id (dev/inode)",
623 .se_cmp
= sort__cgroup_id_cmp
,
624 .se_snprintf
= hist_entry__cgroup_id_snprintf
,
625 .se_width_idx
= HISTC_CGROUP_ID
,
631 sort__socket_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
633 return right
->socket
- left
->socket
;
636 static int hist_entry__socket_snprintf(struct hist_entry
*he
, char *bf
,
637 size_t size
, unsigned int width
)
639 return repsep_snprintf(bf
, size
, "%*.*d", width
, width
-3, he
->socket
);
642 static int hist_entry__socket_filter(struct hist_entry
*he
, int type
, const void *arg
)
644 int sk
= *(const int *)arg
;
646 if (type
!= HIST_FILTER__SOCKET
)
649 return sk
>= 0 && he
->socket
!= sk
;
652 struct sort_entry sort_socket
= {
653 .se_header
= "Socket",
654 .se_cmp
= sort__socket_cmp
,
655 .se_snprintf
= hist_entry__socket_snprintf
,
656 .se_filter
= hist_entry__socket_filter
,
657 .se_width_idx
= HISTC_SOCKET
,
663 sort__time_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
665 return right
->time
- left
->time
;
668 static int hist_entry__time_snprintf(struct hist_entry
*he
, char *bf
,
669 size_t size
, unsigned int width
)
672 unsigned long long nsecs
;
676 secs
= nsecs
/ NSEC_PER_SEC
;
677 nsecs
-= secs
* NSEC_PER_SEC
;
679 if (symbol_conf
.nanosecs
)
680 snprintf(he_time
, sizeof he_time
, "%5lu.%09llu: ",
683 timestamp__scnprintf_usec(he
->time
, he_time
,
686 return repsep_snprintf(bf
, size
, "%-.*s", width
, he_time
);
689 struct sort_entry sort_time
= {
691 .se_cmp
= sort__time_cmp
,
692 .se_snprintf
= hist_entry__time_snprintf
,
693 .se_width_idx
= HISTC_TIME
,
698 static char *get_trace_output(struct hist_entry
*he
)
700 struct trace_seq seq
;
701 struct perf_evsel
*evsel
;
702 struct tep_record rec
= {
703 .data
= he
->raw_data
,
704 .size
= he
->raw_size
,
707 evsel
= hists_to_evsel(he
->hists
);
709 trace_seq_init(&seq
);
710 if (symbol_conf
.raw_trace
) {
711 tep_print_fields(&seq
, he
->raw_data
, he
->raw_size
,
714 tep_event_info(&seq
, evsel
->tp_format
, &rec
);
717 * Trim the buffer, it starts at 4KB and we're not going to
718 * add anything more to this buffer.
720 return realloc(seq
.buffer
, seq
.len
+ 1);
724 sort__trace_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
726 struct perf_evsel
*evsel
;
728 evsel
= hists_to_evsel(left
->hists
);
729 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
)
732 if (left
->trace_output
== NULL
)
733 left
->trace_output
= get_trace_output(left
);
734 if (right
->trace_output
== NULL
)
735 right
->trace_output
= get_trace_output(right
);
737 return strcmp(right
->trace_output
, left
->trace_output
);
740 static int hist_entry__trace_snprintf(struct hist_entry
*he
, char *bf
,
741 size_t size
, unsigned int width
)
743 struct perf_evsel
*evsel
;
745 evsel
= hists_to_evsel(he
->hists
);
746 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
)
747 return scnprintf(bf
, size
, "%-.*s", width
, "N/A");
749 if (he
->trace_output
== NULL
)
750 he
->trace_output
= get_trace_output(he
);
751 return repsep_snprintf(bf
, size
, "%-.*s", width
, he
->trace_output
);
754 struct sort_entry sort_trace
= {
755 .se_header
= "Trace output",
756 .se_cmp
= sort__trace_cmp
,
757 .se_snprintf
= hist_entry__trace_snprintf
,
758 .se_width_idx
= HISTC_TRACE
,
761 /* sort keys for branch stacks */
764 sort__dso_from_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
766 if (!left
->branch_info
|| !right
->branch_info
)
767 return cmp_null(left
->branch_info
, right
->branch_info
);
769 return _sort__dso_cmp(left
->branch_info
->from
.map
,
770 right
->branch_info
->from
.map
);
773 static int hist_entry__dso_from_snprintf(struct hist_entry
*he
, char *bf
,
774 size_t size
, unsigned int width
)
777 return _hist_entry__dso_snprintf(he
->branch_info
->from
.map
,
780 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, "N/A");
783 static int hist_entry__dso_from_filter(struct hist_entry
*he
, int type
,
786 const struct dso
*dso
= arg
;
788 if (type
!= HIST_FILTER__DSO
)
791 return dso
&& (!he
->branch_info
|| !he
->branch_info
->from
.map
||
792 he
->branch_info
->from
.map
->dso
!= dso
);
796 sort__dso_to_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
798 if (!left
->branch_info
|| !right
->branch_info
)
799 return cmp_null(left
->branch_info
, right
->branch_info
);
801 return _sort__dso_cmp(left
->branch_info
->to
.map
,
802 right
->branch_info
->to
.map
);
805 static int hist_entry__dso_to_snprintf(struct hist_entry
*he
, char *bf
,
806 size_t size
, unsigned int width
)
809 return _hist_entry__dso_snprintf(he
->branch_info
->to
.map
,
812 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, "N/A");
815 static int hist_entry__dso_to_filter(struct hist_entry
*he
, int type
,
818 const struct dso
*dso
= arg
;
820 if (type
!= HIST_FILTER__DSO
)
823 return dso
&& (!he
->branch_info
|| !he
->branch_info
->to
.map
||
824 he
->branch_info
->to
.map
->dso
!= dso
);
828 sort__sym_from_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
830 struct addr_map_symbol
*from_l
= &left
->branch_info
->from
;
831 struct addr_map_symbol
*from_r
= &right
->branch_info
->from
;
833 if (!left
->branch_info
|| !right
->branch_info
)
834 return cmp_null(left
->branch_info
, right
->branch_info
);
836 from_l
= &left
->branch_info
->from
;
837 from_r
= &right
->branch_info
->from
;
839 if (!from_l
->sym
&& !from_r
->sym
)
840 return _sort__addr_cmp(from_l
->addr
, from_r
->addr
);
842 return _sort__sym_cmp(from_l
->sym
, from_r
->sym
);
846 sort__sym_to_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
848 struct addr_map_symbol
*to_l
, *to_r
;
850 if (!left
->branch_info
|| !right
->branch_info
)
851 return cmp_null(left
->branch_info
, right
->branch_info
);
853 to_l
= &left
->branch_info
->to
;
854 to_r
= &right
->branch_info
->to
;
856 if (!to_l
->sym
&& !to_r
->sym
)
857 return _sort__addr_cmp(to_l
->addr
, to_r
->addr
);
859 return _sort__sym_cmp(to_l
->sym
, to_r
->sym
);
862 static int hist_entry__sym_from_snprintf(struct hist_entry
*he
, char *bf
,
863 size_t size
, unsigned int width
)
865 if (he
->branch_info
) {
866 struct addr_map_symbol
*from
= &he
->branch_info
->from
;
868 return _hist_entry__sym_snprintf(from
->map
, from
->sym
, from
->addr
,
869 he
->level
, bf
, size
, width
);
872 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, "N/A");
875 static int hist_entry__sym_to_snprintf(struct hist_entry
*he
, char *bf
,
876 size_t size
, unsigned int width
)
878 if (he
->branch_info
) {
879 struct addr_map_symbol
*to
= &he
->branch_info
->to
;
881 return _hist_entry__sym_snprintf(to
->map
, to
->sym
, to
->addr
,
882 he
->level
, bf
, size
, width
);
885 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, "N/A");
888 static int hist_entry__sym_from_filter(struct hist_entry
*he
, int type
,
891 const char *sym
= arg
;
893 if (type
!= HIST_FILTER__SYMBOL
)
896 return sym
&& !(he
->branch_info
&& he
->branch_info
->from
.sym
&&
897 strstr(he
->branch_info
->from
.sym
->name
, sym
));
900 static int hist_entry__sym_to_filter(struct hist_entry
*he
, int type
,
903 const char *sym
= arg
;
905 if (type
!= HIST_FILTER__SYMBOL
)
908 return sym
&& !(he
->branch_info
&& he
->branch_info
->to
.sym
&&
909 strstr(he
->branch_info
->to
.sym
->name
, sym
));
912 struct sort_entry sort_dso_from
= {
913 .se_header
= "Source Shared Object",
914 .se_cmp
= sort__dso_from_cmp
,
915 .se_snprintf
= hist_entry__dso_from_snprintf
,
916 .se_filter
= hist_entry__dso_from_filter
,
917 .se_width_idx
= HISTC_DSO_FROM
,
920 struct sort_entry sort_dso_to
= {
921 .se_header
= "Target Shared Object",
922 .se_cmp
= sort__dso_to_cmp
,
923 .se_snprintf
= hist_entry__dso_to_snprintf
,
924 .se_filter
= hist_entry__dso_to_filter
,
925 .se_width_idx
= HISTC_DSO_TO
,
928 struct sort_entry sort_sym_from
= {
929 .se_header
= "Source Symbol",
930 .se_cmp
= sort__sym_from_cmp
,
931 .se_snprintf
= hist_entry__sym_from_snprintf
,
932 .se_filter
= hist_entry__sym_from_filter
,
933 .se_width_idx
= HISTC_SYMBOL_FROM
,
936 struct sort_entry sort_sym_to
= {
937 .se_header
= "Target Symbol",
938 .se_cmp
= sort__sym_to_cmp
,
939 .se_snprintf
= hist_entry__sym_to_snprintf
,
940 .se_filter
= hist_entry__sym_to_filter
,
941 .se_width_idx
= HISTC_SYMBOL_TO
,
945 sort__mispredict_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
949 if (!left
->branch_info
|| !right
->branch_info
)
950 return cmp_null(left
->branch_info
, right
->branch_info
);
952 mp
= left
->branch_info
->flags
.mispred
!= right
->branch_info
->flags
.mispred
;
953 p
= left
->branch_info
->flags
.predicted
!= right
->branch_info
->flags
.predicted
;
957 static int hist_entry__mispredict_snprintf(struct hist_entry
*he
, char *bf
,
958 size_t size
, unsigned int width
){
959 static const char *out
= "N/A";
961 if (he
->branch_info
) {
962 if (he
->branch_info
->flags
.predicted
)
964 else if (he
->branch_info
->flags
.mispred
)
968 return repsep_snprintf(bf
, size
, "%-*.*s", width
, width
, out
);
972 sort__cycles_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
974 if (!left
->branch_info
|| !right
->branch_info
)
975 return cmp_null(left
->branch_info
, right
->branch_info
);
977 return left
->branch_info
->flags
.cycles
-
978 right
->branch_info
->flags
.cycles
;
981 static int hist_entry__cycles_snprintf(struct hist_entry
*he
, char *bf
,
982 size_t size
, unsigned int width
)
984 if (!he
->branch_info
)
985 return scnprintf(bf
, size
, "%-.*s", width
, "N/A");
986 if (he
->branch_info
->flags
.cycles
== 0)
987 return repsep_snprintf(bf
, size
, "%-*s", width
, "-");
988 return repsep_snprintf(bf
, size
, "%-*hd", width
,
989 he
->branch_info
->flags
.cycles
);
992 struct sort_entry sort_cycles
= {
993 .se_header
= "Basic Block Cycles",
994 .se_cmp
= sort__cycles_cmp
,
995 .se_snprintf
= hist_entry__cycles_snprintf
,
996 .se_width_idx
= HISTC_CYCLES
,
999 /* --sort daddr_sym */
1001 sort__daddr_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1003 uint64_t l
= 0, r
= 0;
1006 l
= left
->mem_info
->daddr
.addr
;
1007 if (right
->mem_info
)
1008 r
= right
->mem_info
->daddr
.addr
;
1010 return (int64_t)(r
- l
);
1013 static int hist_entry__daddr_snprintf(struct hist_entry
*he
, char *bf
,
1014 size_t size
, unsigned int width
)
1017 struct map
*map
= NULL
;
1018 struct symbol
*sym
= NULL
;
1021 addr
= he
->mem_info
->daddr
.addr
;
1022 map
= he
->mem_info
->daddr
.map
;
1023 sym
= he
->mem_info
->daddr
.sym
;
1025 return _hist_entry__sym_snprintf(map
, sym
, addr
, he
->level
, bf
, size
,
1030 sort__iaddr_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1032 uint64_t l
= 0, r
= 0;
1035 l
= left
->mem_info
->iaddr
.addr
;
1036 if (right
->mem_info
)
1037 r
= right
->mem_info
->iaddr
.addr
;
1039 return (int64_t)(r
- l
);
1042 static int hist_entry__iaddr_snprintf(struct hist_entry
*he
, char *bf
,
1043 size_t size
, unsigned int width
)
1046 struct map
*map
= NULL
;
1047 struct symbol
*sym
= NULL
;
1050 addr
= he
->mem_info
->iaddr
.addr
;
1051 map
= he
->mem_info
->iaddr
.map
;
1052 sym
= he
->mem_info
->iaddr
.sym
;
1054 return _hist_entry__sym_snprintf(map
, sym
, addr
, he
->level
, bf
, size
,
1059 sort__dso_daddr_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1061 struct map
*map_l
= NULL
;
1062 struct map
*map_r
= NULL
;
1065 map_l
= left
->mem_info
->daddr
.map
;
1066 if (right
->mem_info
)
1067 map_r
= right
->mem_info
->daddr
.map
;
1069 return _sort__dso_cmp(map_l
, map_r
);
1072 static int hist_entry__dso_daddr_snprintf(struct hist_entry
*he
, char *bf
,
1073 size_t size
, unsigned int width
)
1075 struct map
*map
= NULL
;
1078 map
= he
->mem_info
->daddr
.map
;
1080 return _hist_entry__dso_snprintf(map
, bf
, size
, width
);
1084 sort__locked_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1086 union perf_mem_data_src data_src_l
;
1087 union perf_mem_data_src data_src_r
;
1090 data_src_l
= left
->mem_info
->data_src
;
1092 data_src_l
.mem_lock
= PERF_MEM_LOCK_NA
;
1094 if (right
->mem_info
)
1095 data_src_r
= right
->mem_info
->data_src
;
1097 data_src_r
.mem_lock
= PERF_MEM_LOCK_NA
;
1099 return (int64_t)(data_src_r
.mem_lock
- data_src_l
.mem_lock
);
1102 static int hist_entry__locked_snprintf(struct hist_entry
*he
, char *bf
,
1103 size_t size
, unsigned int width
)
1107 perf_mem__lck_scnprintf(out
, sizeof(out
), he
->mem_info
);
1108 return repsep_snprintf(bf
, size
, "%.*s", width
, out
);
1112 sort__tlb_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1114 union perf_mem_data_src data_src_l
;
1115 union perf_mem_data_src data_src_r
;
1118 data_src_l
= left
->mem_info
->data_src
;
1120 data_src_l
.mem_dtlb
= PERF_MEM_TLB_NA
;
1122 if (right
->mem_info
)
1123 data_src_r
= right
->mem_info
->data_src
;
1125 data_src_r
.mem_dtlb
= PERF_MEM_TLB_NA
;
1127 return (int64_t)(data_src_r
.mem_dtlb
- data_src_l
.mem_dtlb
);
1130 static int hist_entry__tlb_snprintf(struct hist_entry
*he
, char *bf
,
1131 size_t size
, unsigned int width
)
1135 perf_mem__tlb_scnprintf(out
, sizeof(out
), he
->mem_info
);
1136 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
1140 sort__lvl_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1142 union perf_mem_data_src data_src_l
;
1143 union perf_mem_data_src data_src_r
;
1146 data_src_l
= left
->mem_info
->data_src
;
1148 data_src_l
.mem_lvl
= PERF_MEM_LVL_NA
;
1150 if (right
->mem_info
)
1151 data_src_r
= right
->mem_info
->data_src
;
1153 data_src_r
.mem_lvl
= PERF_MEM_LVL_NA
;
1155 return (int64_t)(data_src_r
.mem_lvl
- data_src_l
.mem_lvl
);
1158 static int hist_entry__lvl_snprintf(struct hist_entry
*he
, char *bf
,
1159 size_t size
, unsigned int width
)
1163 perf_mem__lvl_scnprintf(out
, sizeof(out
), he
->mem_info
);
1164 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
1168 sort__snoop_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1170 union perf_mem_data_src data_src_l
;
1171 union perf_mem_data_src data_src_r
;
1174 data_src_l
= left
->mem_info
->data_src
;
1176 data_src_l
.mem_snoop
= PERF_MEM_SNOOP_NA
;
1178 if (right
->mem_info
)
1179 data_src_r
= right
->mem_info
->data_src
;
1181 data_src_r
.mem_snoop
= PERF_MEM_SNOOP_NA
;
1183 return (int64_t)(data_src_r
.mem_snoop
- data_src_l
.mem_snoop
);
1186 static int hist_entry__snoop_snprintf(struct hist_entry
*he
, char *bf
,
1187 size_t size
, unsigned int width
)
1191 perf_mem__snp_scnprintf(out
, sizeof(out
), he
->mem_info
);
1192 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
1196 sort__dcacheline_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1199 struct map
*l_map
, *r_map
;
1201 if (!left
->mem_info
) return -1;
1202 if (!right
->mem_info
) return 1;
1204 /* group event types together */
1205 if (left
->cpumode
> right
->cpumode
) return -1;
1206 if (left
->cpumode
< right
->cpumode
) return 1;
1208 l_map
= left
->mem_info
->daddr
.map
;
1209 r_map
= right
->mem_info
->daddr
.map
;
1211 /* if both are NULL, jump to sort on al_addr instead */
1212 if (!l_map
&& !r_map
)
1215 if (!l_map
) return -1;
1216 if (!r_map
) return 1;
1218 if (l_map
->maj
> r_map
->maj
) return -1;
1219 if (l_map
->maj
< r_map
->maj
) return 1;
1221 if (l_map
->min
> r_map
->min
) return -1;
1222 if (l_map
->min
< r_map
->min
) return 1;
1224 if (l_map
->ino
> r_map
->ino
) return -1;
1225 if (l_map
->ino
< r_map
->ino
) return 1;
1227 if (l_map
->ino_generation
> r_map
->ino_generation
) return -1;
1228 if (l_map
->ino_generation
< r_map
->ino_generation
) return 1;
1231 * Addresses with no major/minor numbers are assumed to be
1232 * anonymous in userspace. Sort those on pid then address.
1234 * The kernel and non-zero major/minor mapped areas are
1235 * assumed to be unity mapped. Sort those on address.
1238 if ((left
->cpumode
!= PERF_RECORD_MISC_KERNEL
) &&
1239 (!(l_map
->flags
& MAP_SHARED
)) &&
1240 !l_map
->maj
&& !l_map
->min
&& !l_map
->ino
&&
1241 !l_map
->ino_generation
) {
1242 /* userspace anonymous */
1244 if (left
->thread
->pid_
> right
->thread
->pid_
) return -1;
1245 if (left
->thread
->pid_
< right
->thread
->pid_
) return 1;
1249 /* al_addr does all the right addr - start + offset calculations */
1250 l
= cl_address(left
->mem_info
->daddr
.al_addr
);
1251 r
= cl_address(right
->mem_info
->daddr
.al_addr
);
1253 if (l
> r
) return -1;
1254 if (l
< r
) return 1;
1259 static int hist_entry__dcacheline_snprintf(struct hist_entry
*he
, char *bf
,
1260 size_t size
, unsigned int width
)
1264 struct map
*map
= NULL
;
1265 struct symbol
*sym
= NULL
;
1266 char level
= he
->level
;
1269 addr
= cl_address(he
->mem_info
->daddr
.al_addr
);
1270 map
= he
->mem_info
->daddr
.map
;
1271 sym
= he
->mem_info
->daddr
.sym
;
1273 /* print [s] for shared data mmaps */
1274 if ((he
->cpumode
!= PERF_RECORD_MISC_KERNEL
) &&
1275 map
&& !(map
->prot
& PROT_EXEC
) &&
1276 (map
->flags
& MAP_SHARED
) &&
1277 (map
->maj
|| map
->min
|| map
->ino
||
1278 map
->ino_generation
))
1283 return _hist_entry__sym_snprintf(map
, sym
, addr
, level
, bf
, size
,
1287 struct sort_entry sort_mispredict
= {
1288 .se_header
= "Branch Mispredicted",
1289 .se_cmp
= sort__mispredict_cmp
,
1290 .se_snprintf
= hist_entry__mispredict_snprintf
,
1291 .se_width_idx
= HISTC_MISPREDICT
,
1294 static u64
he_weight(struct hist_entry
*he
)
1296 return he
->stat
.nr_events
? he
->stat
.weight
/ he
->stat
.nr_events
: 0;
1300 sort__local_weight_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1302 return he_weight(left
) - he_weight(right
);
1305 static int hist_entry__local_weight_snprintf(struct hist_entry
*he
, char *bf
,
1306 size_t size
, unsigned int width
)
1308 return repsep_snprintf(bf
, size
, "%-*llu", width
, he_weight(he
));
1311 struct sort_entry sort_local_weight
= {
1312 .se_header
= "Local Weight",
1313 .se_cmp
= sort__local_weight_cmp
,
1314 .se_snprintf
= hist_entry__local_weight_snprintf
,
1315 .se_width_idx
= HISTC_LOCAL_WEIGHT
,
1319 sort__global_weight_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1321 return left
->stat
.weight
- right
->stat
.weight
;
1324 static int hist_entry__global_weight_snprintf(struct hist_entry
*he
, char *bf
,
1325 size_t size
, unsigned int width
)
1327 return repsep_snprintf(bf
, size
, "%-*llu", width
, he
->stat
.weight
);
1330 struct sort_entry sort_global_weight
= {
1331 .se_header
= "Weight",
1332 .se_cmp
= sort__global_weight_cmp
,
1333 .se_snprintf
= hist_entry__global_weight_snprintf
,
1334 .se_width_idx
= HISTC_GLOBAL_WEIGHT
,
1337 struct sort_entry sort_mem_daddr_sym
= {
1338 .se_header
= "Data Symbol",
1339 .se_cmp
= sort__daddr_cmp
,
1340 .se_snprintf
= hist_entry__daddr_snprintf
,
1341 .se_width_idx
= HISTC_MEM_DADDR_SYMBOL
,
1344 struct sort_entry sort_mem_iaddr_sym
= {
1345 .se_header
= "Code Symbol",
1346 .se_cmp
= sort__iaddr_cmp
,
1347 .se_snprintf
= hist_entry__iaddr_snprintf
,
1348 .se_width_idx
= HISTC_MEM_IADDR_SYMBOL
,
1351 struct sort_entry sort_mem_daddr_dso
= {
1352 .se_header
= "Data Object",
1353 .se_cmp
= sort__dso_daddr_cmp
,
1354 .se_snprintf
= hist_entry__dso_daddr_snprintf
,
1355 .se_width_idx
= HISTC_MEM_DADDR_DSO
,
1358 struct sort_entry sort_mem_locked
= {
1359 .se_header
= "Locked",
1360 .se_cmp
= sort__locked_cmp
,
1361 .se_snprintf
= hist_entry__locked_snprintf
,
1362 .se_width_idx
= HISTC_MEM_LOCKED
,
1365 struct sort_entry sort_mem_tlb
= {
1366 .se_header
= "TLB access",
1367 .se_cmp
= sort__tlb_cmp
,
1368 .se_snprintf
= hist_entry__tlb_snprintf
,
1369 .se_width_idx
= HISTC_MEM_TLB
,
1372 struct sort_entry sort_mem_lvl
= {
1373 .se_header
= "Memory access",
1374 .se_cmp
= sort__lvl_cmp
,
1375 .se_snprintf
= hist_entry__lvl_snprintf
,
1376 .se_width_idx
= HISTC_MEM_LVL
,
1379 struct sort_entry sort_mem_snoop
= {
1380 .se_header
= "Snoop",
1381 .se_cmp
= sort__snoop_cmp
,
1382 .se_snprintf
= hist_entry__snoop_snprintf
,
1383 .se_width_idx
= HISTC_MEM_SNOOP
,
1386 struct sort_entry sort_mem_dcacheline
= {
1387 .se_header
= "Data Cacheline",
1388 .se_cmp
= sort__dcacheline_cmp
,
1389 .se_snprintf
= hist_entry__dcacheline_snprintf
,
1390 .se_width_idx
= HISTC_MEM_DCACHELINE
,
1394 sort__phys_daddr_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1396 uint64_t l
= 0, r
= 0;
1399 l
= left
->mem_info
->daddr
.phys_addr
;
1400 if (right
->mem_info
)
1401 r
= right
->mem_info
->daddr
.phys_addr
;
1403 return (int64_t)(r
- l
);
1406 static int hist_entry__phys_daddr_snprintf(struct hist_entry
*he
, char *bf
,
1407 size_t size
, unsigned int width
)
1411 size_t len
= BITS_PER_LONG
/ 4;
1413 addr
= he
->mem_info
->daddr
.phys_addr
;
1415 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "[%c] ", he
->level
);
1417 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-#.*llx", len
, addr
);
1419 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-*s", width
- ret
, "");
1427 struct sort_entry sort_mem_phys_daddr
= {
1428 .se_header
= "Data Physical Address",
1429 .se_cmp
= sort__phys_daddr_cmp
,
1430 .se_snprintf
= hist_entry__phys_daddr_snprintf
,
1431 .se_width_idx
= HISTC_MEM_PHYS_DADDR
,
1435 sort__abort_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1437 if (!left
->branch_info
|| !right
->branch_info
)
1438 return cmp_null(left
->branch_info
, right
->branch_info
);
1440 return left
->branch_info
->flags
.abort
!=
1441 right
->branch_info
->flags
.abort
;
1444 static int hist_entry__abort_snprintf(struct hist_entry
*he
, char *bf
,
1445 size_t size
, unsigned int width
)
1447 static const char *out
= "N/A";
1449 if (he
->branch_info
) {
1450 if (he
->branch_info
->flags
.abort
)
1456 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
1459 struct sort_entry sort_abort
= {
1460 .se_header
= "Transaction abort",
1461 .se_cmp
= sort__abort_cmp
,
1462 .se_snprintf
= hist_entry__abort_snprintf
,
1463 .se_width_idx
= HISTC_ABORT
,
1467 sort__in_tx_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1469 if (!left
->branch_info
|| !right
->branch_info
)
1470 return cmp_null(left
->branch_info
, right
->branch_info
);
1472 return left
->branch_info
->flags
.in_tx
!=
1473 right
->branch_info
->flags
.in_tx
;
1476 static int hist_entry__in_tx_snprintf(struct hist_entry
*he
, char *bf
,
1477 size_t size
, unsigned int width
)
1479 static const char *out
= "N/A";
1481 if (he
->branch_info
) {
1482 if (he
->branch_info
->flags
.in_tx
)
1488 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
1491 struct sort_entry sort_in_tx
= {
1492 .se_header
= "Branch in transaction",
1493 .se_cmp
= sort__in_tx_cmp
,
1494 .se_snprintf
= hist_entry__in_tx_snprintf
,
1495 .se_width_idx
= HISTC_IN_TX
,
1499 sort__transaction_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1501 return left
->transaction
- right
->transaction
;
1504 static inline char *add_str(char *p
, const char *str
)
1507 return p
+ strlen(str
);
1510 static struct txbit
{
1515 { PERF_TXN_ELISION
, "EL ", 0 },
1516 { PERF_TXN_TRANSACTION
, "TX ", 1 },
1517 { PERF_TXN_SYNC
, "SYNC ", 1 },
1518 { PERF_TXN_ASYNC
, "ASYNC ", 0 },
1519 { PERF_TXN_RETRY
, "RETRY ", 0 },
1520 { PERF_TXN_CONFLICT
, "CON ", 0 },
1521 { PERF_TXN_CAPACITY_WRITE
, "CAP-WRITE ", 1 },
1522 { PERF_TXN_CAPACITY_READ
, "CAP-READ ", 0 },
1526 int hist_entry__transaction_len(void)
1531 for (i
= 0; txbits
[i
].name
; i
++) {
1532 if (!txbits
[i
].skip_for_len
)
1533 len
+= strlen(txbits
[i
].name
);
1535 len
+= 4; /* :XX<space> */
1539 static int hist_entry__transaction_snprintf(struct hist_entry
*he
, char *bf
,
1540 size_t size
, unsigned int width
)
1542 u64 t
= he
->transaction
;
1548 for (i
= 0; txbits
[i
].name
; i
++)
1549 if (txbits
[i
].flag
& t
)
1550 p
= add_str(p
, txbits
[i
].name
);
1551 if (t
&& !(t
& (PERF_TXN_SYNC
|PERF_TXN_ASYNC
)))
1552 p
= add_str(p
, "NEITHER ");
1553 if (t
& PERF_TXN_ABORT_MASK
) {
1554 sprintf(p
, ":%" PRIx64
,
1555 (t
& PERF_TXN_ABORT_MASK
) >>
1556 PERF_TXN_ABORT_SHIFT
);
1560 return repsep_snprintf(bf
, size
, "%-*s", width
, buf
);
1563 struct sort_entry sort_transaction
= {
1564 .se_header
= "Transaction ",
1565 .se_cmp
= sort__transaction_cmp
,
1566 .se_snprintf
= hist_entry__transaction_snprintf
,
1567 .se_width_idx
= HISTC_TRANSACTION
,
1570 /* --sort symbol_size */
1572 static int64_t _sort__sym_size_cmp(struct symbol
*sym_l
, struct symbol
*sym_r
)
1574 int64_t size_l
= sym_l
!= NULL
? symbol__size(sym_l
) : 0;
1575 int64_t size_r
= sym_r
!= NULL
? symbol__size(sym_r
) : 0;
1577 return size_l
< size_r
? -1 :
1578 size_l
== size_r
? 0 : 1;
1582 sort__sym_size_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1584 return _sort__sym_size_cmp(right
->ms
.sym
, left
->ms
.sym
);
1587 static int _hist_entry__sym_size_snprintf(struct symbol
*sym
, char *bf
,
1588 size_t bf_size
, unsigned int width
)
1591 return repsep_snprintf(bf
, bf_size
, "%*d", width
, symbol__size(sym
));
1593 return repsep_snprintf(bf
, bf_size
, "%*s", width
, "unknown");
1596 static int hist_entry__sym_size_snprintf(struct hist_entry
*he
, char *bf
,
1597 size_t size
, unsigned int width
)
1599 return _hist_entry__sym_size_snprintf(he
->ms
.sym
, bf
, size
, width
);
1602 struct sort_entry sort_sym_size
= {
1603 .se_header
= "Symbol size",
1604 .se_cmp
= sort__sym_size_cmp
,
1605 .se_snprintf
= hist_entry__sym_size_snprintf
,
1606 .se_width_idx
= HISTC_SYM_SIZE
,
1609 /* --sort dso_size */
1611 static int64_t _sort__dso_size_cmp(struct map
*map_l
, struct map
*map_r
)
1613 int64_t size_l
= map_l
!= NULL
? map__size(map_l
) : 0;
1614 int64_t size_r
= map_r
!= NULL
? map__size(map_r
) : 0;
1616 return size_l
< size_r
? -1 :
1617 size_l
== size_r
? 0 : 1;
1621 sort__dso_size_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
1623 return _sort__dso_size_cmp(right
->ms
.map
, left
->ms
.map
);
1626 static int _hist_entry__dso_size_snprintf(struct map
*map
, char *bf
,
1627 size_t bf_size
, unsigned int width
)
1629 if (map
&& map
->dso
)
1630 return repsep_snprintf(bf
, bf_size
, "%*d", width
,
1633 return repsep_snprintf(bf
, bf_size
, "%*s", width
, "unknown");
1636 static int hist_entry__dso_size_snprintf(struct hist_entry
*he
, char *bf
,
1637 size_t size
, unsigned int width
)
1639 return _hist_entry__dso_size_snprintf(he
->ms
.map
, bf
, size
, width
);
1642 struct sort_entry sort_dso_size
= {
1643 .se_header
= "DSO size",
1644 .se_cmp
= sort__dso_size_cmp
,
1645 .se_snprintf
= hist_entry__dso_size_snprintf
,
1646 .se_width_idx
= HISTC_DSO_SIZE
,
1650 struct sort_dimension
{
1652 struct sort_entry
*entry
;
1656 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
1658 static struct sort_dimension common_sort_dimensions
[] = {
1659 DIM(SORT_PID
, "pid", sort_thread
),
1660 DIM(SORT_COMM
, "comm", sort_comm
),
1661 DIM(SORT_DSO
, "dso", sort_dso
),
1662 DIM(SORT_SYM
, "symbol", sort_sym
),
1663 DIM(SORT_PARENT
, "parent", sort_parent
),
1664 DIM(SORT_CPU
, "cpu", sort_cpu
),
1665 DIM(SORT_SOCKET
, "socket", sort_socket
),
1666 DIM(SORT_SRCLINE
, "srcline", sort_srcline
),
1667 DIM(SORT_SRCFILE
, "srcfile", sort_srcfile
),
1668 DIM(SORT_LOCAL_WEIGHT
, "local_weight", sort_local_weight
),
1669 DIM(SORT_GLOBAL_WEIGHT
, "weight", sort_global_weight
),
1670 DIM(SORT_TRANSACTION
, "transaction", sort_transaction
),
1671 DIM(SORT_TRACE
, "trace", sort_trace
),
1672 DIM(SORT_SYM_SIZE
, "symbol_size", sort_sym_size
),
1673 DIM(SORT_DSO_SIZE
, "dso_size", sort_dso_size
),
1674 DIM(SORT_CGROUP_ID
, "cgroup_id", sort_cgroup_id
),
1675 DIM(SORT_SYM_IPC_NULL
, "ipc_null", sort_sym_ipc_null
),
1676 DIM(SORT_TIME
, "time", sort_time
),
1681 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
1683 static struct sort_dimension bstack_sort_dimensions
[] = {
1684 DIM(SORT_DSO_FROM
, "dso_from", sort_dso_from
),
1685 DIM(SORT_DSO_TO
, "dso_to", sort_dso_to
),
1686 DIM(SORT_SYM_FROM
, "symbol_from", sort_sym_from
),
1687 DIM(SORT_SYM_TO
, "symbol_to", sort_sym_to
),
1688 DIM(SORT_MISPREDICT
, "mispredict", sort_mispredict
),
1689 DIM(SORT_IN_TX
, "in_tx", sort_in_tx
),
1690 DIM(SORT_ABORT
, "abort", sort_abort
),
1691 DIM(SORT_CYCLES
, "cycles", sort_cycles
),
1692 DIM(SORT_SRCLINE_FROM
, "srcline_from", sort_srcline_from
),
1693 DIM(SORT_SRCLINE_TO
, "srcline_to", sort_srcline_to
),
1694 DIM(SORT_SYM_IPC
, "ipc_lbr", sort_sym_ipc
),
1699 #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
1701 static struct sort_dimension memory_sort_dimensions
[] = {
1702 DIM(SORT_MEM_DADDR_SYMBOL
, "symbol_daddr", sort_mem_daddr_sym
),
1703 DIM(SORT_MEM_IADDR_SYMBOL
, "symbol_iaddr", sort_mem_iaddr_sym
),
1704 DIM(SORT_MEM_DADDR_DSO
, "dso_daddr", sort_mem_daddr_dso
),
1705 DIM(SORT_MEM_LOCKED
, "locked", sort_mem_locked
),
1706 DIM(SORT_MEM_TLB
, "tlb", sort_mem_tlb
),
1707 DIM(SORT_MEM_LVL
, "mem", sort_mem_lvl
),
1708 DIM(SORT_MEM_SNOOP
, "snoop", sort_mem_snoop
),
1709 DIM(SORT_MEM_DCACHELINE
, "dcacheline", sort_mem_dcacheline
),
1710 DIM(SORT_MEM_PHYS_DADDR
, "phys_daddr", sort_mem_phys_daddr
),
1715 struct hpp_dimension
{
1717 struct perf_hpp_fmt
*fmt
;
1721 #define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
1723 static struct hpp_dimension hpp_sort_dimensions
[] = {
1724 DIM(PERF_HPP__OVERHEAD
, "overhead"),
1725 DIM(PERF_HPP__OVERHEAD_SYS
, "overhead_sys"),
1726 DIM(PERF_HPP__OVERHEAD_US
, "overhead_us"),
1727 DIM(PERF_HPP__OVERHEAD_GUEST_SYS
, "overhead_guest_sys"),
1728 DIM(PERF_HPP__OVERHEAD_GUEST_US
, "overhead_guest_us"),
1729 DIM(PERF_HPP__OVERHEAD_ACC
, "overhead_children"),
1730 DIM(PERF_HPP__SAMPLES
, "sample"),
1731 DIM(PERF_HPP__PERIOD
, "period"),
1736 struct hpp_sort_entry
{
1737 struct perf_hpp_fmt hpp
;
1738 struct sort_entry
*se
;
1741 void perf_hpp__reset_sort_width(struct perf_hpp_fmt
*fmt
, struct hists
*hists
)
1743 struct hpp_sort_entry
*hse
;
1745 if (!perf_hpp__is_sort_entry(fmt
))
1748 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1749 hists__new_col_len(hists
, hse
->se
->se_width_idx
, strlen(fmt
->name
));
1752 static int __sort__hpp_header(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
1753 struct hists
*hists
, int line __maybe_unused
,
1754 int *span __maybe_unused
)
1756 struct hpp_sort_entry
*hse
;
1757 size_t len
= fmt
->user_len
;
1759 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1762 len
= hists__col_len(hists
, hse
->se
->se_width_idx
);
1764 return scnprintf(hpp
->buf
, hpp
->size
, "%-*.*s", len
, len
, fmt
->name
);
1767 static int __sort__hpp_width(struct perf_hpp_fmt
*fmt
,
1768 struct perf_hpp
*hpp __maybe_unused
,
1769 struct hists
*hists
)
1771 struct hpp_sort_entry
*hse
;
1772 size_t len
= fmt
->user_len
;
1774 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1777 len
= hists__col_len(hists
, hse
->se
->se_width_idx
);
1782 static int __sort__hpp_entry(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
1783 struct hist_entry
*he
)
1785 struct hpp_sort_entry
*hse
;
1786 size_t len
= fmt
->user_len
;
1788 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1791 len
= hists__col_len(he
->hists
, hse
->se
->se_width_idx
);
1793 return hse
->se
->se_snprintf(he
, hpp
->buf
, hpp
->size
, len
);
1796 static int64_t __sort__hpp_cmp(struct perf_hpp_fmt
*fmt
,
1797 struct hist_entry
*a
, struct hist_entry
*b
)
1799 struct hpp_sort_entry
*hse
;
1801 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1802 return hse
->se
->se_cmp(a
, b
);
1805 static int64_t __sort__hpp_collapse(struct perf_hpp_fmt
*fmt
,
1806 struct hist_entry
*a
, struct hist_entry
*b
)
1808 struct hpp_sort_entry
*hse
;
1809 int64_t (*collapse_fn
)(struct hist_entry
*, struct hist_entry
*);
1811 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1812 collapse_fn
= hse
->se
->se_collapse
?: hse
->se
->se_cmp
;
1813 return collapse_fn(a
, b
);
1816 static int64_t __sort__hpp_sort(struct perf_hpp_fmt
*fmt
,
1817 struct hist_entry
*a
, struct hist_entry
*b
)
1819 struct hpp_sort_entry
*hse
;
1820 int64_t (*sort_fn
)(struct hist_entry
*, struct hist_entry
*);
1822 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1823 sort_fn
= hse
->se
->se_sort
?: hse
->se
->se_cmp
;
1824 return sort_fn(a
, b
);
1827 bool perf_hpp__is_sort_entry(struct perf_hpp_fmt
*format
)
1829 return format
->header
== __sort__hpp_header
;
1832 #define MK_SORT_ENTRY_CHK(key) \
1833 bool perf_hpp__is_ ## key ## _entry(struct perf_hpp_fmt *fmt) \
1835 struct hpp_sort_entry *hse; \
1837 if (!perf_hpp__is_sort_entry(fmt)) \
1840 hse = container_of(fmt, struct hpp_sort_entry, hpp); \
1841 return hse->se == &sort_ ## key ; \
1844 MK_SORT_ENTRY_CHK(trace
)
1845 MK_SORT_ENTRY_CHK(srcline
)
1846 MK_SORT_ENTRY_CHK(srcfile
)
1847 MK_SORT_ENTRY_CHK(thread
)
1848 MK_SORT_ENTRY_CHK(comm
)
1849 MK_SORT_ENTRY_CHK(dso
)
1850 MK_SORT_ENTRY_CHK(sym
)
1853 static bool __sort__hpp_equal(struct perf_hpp_fmt
*a
, struct perf_hpp_fmt
*b
)
1855 struct hpp_sort_entry
*hse_a
;
1856 struct hpp_sort_entry
*hse_b
;
1858 if (!perf_hpp__is_sort_entry(a
) || !perf_hpp__is_sort_entry(b
))
1861 hse_a
= container_of(a
, struct hpp_sort_entry
, hpp
);
1862 hse_b
= container_of(b
, struct hpp_sort_entry
, hpp
);
1864 return hse_a
->se
== hse_b
->se
;
1867 static void hse_free(struct perf_hpp_fmt
*fmt
)
1869 struct hpp_sort_entry
*hse
;
1871 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1875 static struct hpp_sort_entry
*
1876 __sort_dimension__alloc_hpp(struct sort_dimension
*sd
, int level
)
1878 struct hpp_sort_entry
*hse
;
1880 hse
= malloc(sizeof(*hse
));
1882 pr_err("Memory allocation failed\n");
1886 hse
->se
= sd
->entry
;
1887 hse
->hpp
.name
= sd
->entry
->se_header
;
1888 hse
->hpp
.header
= __sort__hpp_header
;
1889 hse
->hpp
.width
= __sort__hpp_width
;
1890 hse
->hpp
.entry
= __sort__hpp_entry
;
1891 hse
->hpp
.color
= NULL
;
1893 hse
->hpp
.cmp
= __sort__hpp_cmp
;
1894 hse
->hpp
.collapse
= __sort__hpp_collapse
;
1895 hse
->hpp
.sort
= __sort__hpp_sort
;
1896 hse
->hpp
.equal
= __sort__hpp_equal
;
1897 hse
->hpp
.free
= hse_free
;
1899 INIT_LIST_HEAD(&hse
->hpp
.list
);
1900 INIT_LIST_HEAD(&hse
->hpp
.sort_list
);
1901 hse
->hpp
.elide
= false;
1903 hse
->hpp
.user_len
= 0;
1904 hse
->hpp
.level
= level
;
1909 static void hpp_free(struct perf_hpp_fmt
*fmt
)
1914 static struct perf_hpp_fmt
*__hpp_dimension__alloc_hpp(struct hpp_dimension
*hd
,
1917 struct perf_hpp_fmt
*fmt
;
1919 fmt
= memdup(hd
->fmt
, sizeof(*fmt
));
1921 INIT_LIST_HEAD(&fmt
->list
);
1922 INIT_LIST_HEAD(&fmt
->sort_list
);
1923 fmt
->free
= hpp_free
;
1930 int hist_entry__filter(struct hist_entry
*he
, int type
, const void *arg
)
1932 struct perf_hpp_fmt
*fmt
;
1933 struct hpp_sort_entry
*hse
;
1937 perf_hpp_list__for_each_format(he
->hpp_list
, fmt
) {
1938 if (!perf_hpp__is_sort_entry(fmt
))
1941 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
1942 if (hse
->se
->se_filter
== NULL
)
1946 * hist entry is filtered if any of sort key in the hpp list
1947 * is applied. But it should skip non-matched filter types.
1949 r
= hse
->se
->se_filter(he
, type
, arg
);
1960 static int __sort_dimension__add_hpp_sort(struct sort_dimension
*sd
,
1961 struct perf_hpp_list
*list
,
1964 struct hpp_sort_entry
*hse
= __sort_dimension__alloc_hpp(sd
, level
);
1969 perf_hpp_list__register_sort_field(list
, &hse
->hpp
);
1973 static int __sort_dimension__add_hpp_output(struct sort_dimension
*sd
,
1974 struct perf_hpp_list
*list
)
1976 struct hpp_sort_entry
*hse
= __sort_dimension__alloc_hpp(sd
, 0);
1981 perf_hpp_list__column_register(list
, &hse
->hpp
);
1985 struct hpp_dynamic_entry
{
1986 struct perf_hpp_fmt hpp
;
1987 struct perf_evsel
*evsel
;
1988 struct tep_format_field
*field
;
1989 unsigned dynamic_len
;
1993 static int hde_width(struct hpp_dynamic_entry
*hde
)
1995 if (!hde
->hpp
.len
) {
1996 int len
= hde
->dynamic_len
;
1997 int namelen
= strlen(hde
->field
->name
);
1998 int fieldlen
= hde
->field
->size
;
2003 if (!(hde
->field
->flags
& TEP_FIELD_IS_STRING
)) {
2004 /* length for print hex numbers */
2005 fieldlen
= hde
->field
->size
* 2 + 2;
2012 return hde
->hpp
.len
;
2015 static void update_dynamic_len(struct hpp_dynamic_entry
*hde
,
2016 struct hist_entry
*he
)
2019 struct tep_format_field
*field
= hde
->field
;
2026 /* parse pretty print result and update max length */
2027 if (!he
->trace_output
)
2028 he
->trace_output
= get_trace_output(he
);
2030 namelen
= strlen(field
->name
);
2031 str
= he
->trace_output
;
2034 pos
= strchr(str
, ' ');
2037 pos
= str
+ strlen(str
);
2040 if (!strncmp(str
, field
->name
, namelen
)) {
2046 if (len
> hde
->dynamic_len
)
2047 hde
->dynamic_len
= len
;
2058 static int __sort__hde_header(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
2059 struct hists
*hists __maybe_unused
,
2060 int line __maybe_unused
,
2061 int *span __maybe_unused
)
2063 struct hpp_dynamic_entry
*hde
;
2064 size_t len
= fmt
->user_len
;
2066 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
2069 len
= hde_width(hde
);
2071 return scnprintf(hpp
->buf
, hpp
->size
, "%*.*s", len
, len
, hde
->field
->name
);
2074 static int __sort__hde_width(struct perf_hpp_fmt
*fmt
,
2075 struct perf_hpp
*hpp __maybe_unused
,
2076 struct hists
*hists __maybe_unused
)
2078 struct hpp_dynamic_entry
*hde
;
2079 size_t len
= fmt
->user_len
;
2081 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
2084 len
= hde_width(hde
);
2089 bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt
*fmt
, struct hists
*hists
)
2091 struct hpp_dynamic_entry
*hde
;
2093 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
2095 return hists_to_evsel(hists
) == hde
->evsel
;
2098 static int __sort__hde_entry(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
2099 struct hist_entry
*he
)
2101 struct hpp_dynamic_entry
*hde
;
2102 size_t len
= fmt
->user_len
;
2104 struct tep_format_field
*field
;
2109 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
2112 len
= hde_width(hde
);
2117 if (!he
->trace_output
)
2118 he
->trace_output
= get_trace_output(he
);
2121 namelen
= strlen(field
->name
);
2122 str
= he
->trace_output
;
2125 pos
= strchr(str
, ' ');
2128 pos
= str
+ strlen(str
);
2131 if (!strncmp(str
, field
->name
, namelen
)) {
2133 str
= strndup(str
, pos
- str
);
2136 return scnprintf(hpp
->buf
, hpp
->size
,
2137 "%*.*s", len
, len
, "ERROR");
2148 struct trace_seq seq
;
2150 trace_seq_init(&seq
);
2151 tep_print_field(&seq
, he
->raw_data
, hde
->field
);
2155 ret
= scnprintf(hpp
->buf
, hpp
->size
, "%*.*s", len
, len
, str
);
2160 static int64_t __sort__hde_cmp(struct perf_hpp_fmt
*fmt
,
2161 struct hist_entry
*a
, struct hist_entry
*b
)
2163 struct hpp_dynamic_entry
*hde
;
2164 struct tep_format_field
*field
;
2165 unsigned offset
, size
;
2167 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
2170 update_dynamic_len(hde
, a
);
2175 if (field
->flags
& TEP_FIELD_IS_DYNAMIC
) {
2176 unsigned long long dyn
;
2178 tep_read_number_field(field
, a
->raw_data
, &dyn
);
2179 offset
= dyn
& 0xffff;
2180 size
= (dyn
>> 16) & 0xffff;
2182 /* record max width for output */
2183 if (size
> hde
->dynamic_len
)
2184 hde
->dynamic_len
= size
;
2186 offset
= field
->offset
;
2190 return memcmp(a
->raw_data
+ offset
, b
->raw_data
+ offset
, size
);
2193 bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt
*fmt
)
2195 return fmt
->cmp
== __sort__hde_cmp
;
2198 static bool __sort__hde_equal(struct perf_hpp_fmt
*a
, struct perf_hpp_fmt
*b
)
2200 struct hpp_dynamic_entry
*hde_a
;
2201 struct hpp_dynamic_entry
*hde_b
;
2203 if (!perf_hpp__is_dynamic_entry(a
) || !perf_hpp__is_dynamic_entry(b
))
2206 hde_a
= container_of(a
, struct hpp_dynamic_entry
, hpp
);
2207 hde_b
= container_of(b
, struct hpp_dynamic_entry
, hpp
);
2209 return hde_a
->field
== hde_b
->field
;
2212 static void hde_free(struct perf_hpp_fmt
*fmt
)
2214 struct hpp_dynamic_entry
*hde
;
2216 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
2220 static struct hpp_dynamic_entry
*
2221 __alloc_dynamic_entry(struct perf_evsel
*evsel
, struct tep_format_field
*field
,
2224 struct hpp_dynamic_entry
*hde
;
2226 hde
= malloc(sizeof(*hde
));
2228 pr_debug("Memory allocation failed\n");
2234 hde
->dynamic_len
= 0;
2236 hde
->hpp
.name
= field
->name
;
2237 hde
->hpp
.header
= __sort__hde_header
;
2238 hde
->hpp
.width
= __sort__hde_width
;
2239 hde
->hpp
.entry
= __sort__hde_entry
;
2240 hde
->hpp
.color
= NULL
;
2242 hde
->hpp
.cmp
= __sort__hde_cmp
;
2243 hde
->hpp
.collapse
= __sort__hde_cmp
;
2244 hde
->hpp
.sort
= __sort__hde_cmp
;
2245 hde
->hpp
.equal
= __sort__hde_equal
;
2246 hde
->hpp
.free
= hde_free
;
2248 INIT_LIST_HEAD(&hde
->hpp
.list
);
2249 INIT_LIST_HEAD(&hde
->hpp
.sort_list
);
2250 hde
->hpp
.elide
= false;
2252 hde
->hpp
.user_len
= 0;
2253 hde
->hpp
.level
= level
;
2258 struct perf_hpp_fmt
*perf_hpp_fmt__dup(struct perf_hpp_fmt
*fmt
)
2260 struct perf_hpp_fmt
*new_fmt
= NULL
;
2262 if (perf_hpp__is_sort_entry(fmt
)) {
2263 struct hpp_sort_entry
*hse
, *new_hse
;
2265 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
2266 new_hse
= memdup(hse
, sizeof(*hse
));
2268 new_fmt
= &new_hse
->hpp
;
2269 } else if (perf_hpp__is_dynamic_entry(fmt
)) {
2270 struct hpp_dynamic_entry
*hde
, *new_hde
;
2272 hde
= container_of(fmt
, struct hpp_dynamic_entry
, hpp
);
2273 new_hde
= memdup(hde
, sizeof(*hde
));
2275 new_fmt
= &new_hde
->hpp
;
2277 new_fmt
= memdup(fmt
, sizeof(*fmt
));
2280 INIT_LIST_HEAD(&new_fmt
->list
);
2281 INIT_LIST_HEAD(&new_fmt
->sort_list
);
2286 static int parse_field_name(char *str
, char **event
, char **field
, char **opt
)
2288 char *event_name
, *field_name
, *opt_name
;
2291 field_name
= strchr(str
, '.');
2294 *field_name
++ = '\0';
2300 opt_name
= strchr(field_name
, '/');
2304 *event
= event_name
;
2305 *field
= field_name
;
2311 /* find match evsel using a given event name. The event name can be:
2312 * 1. '%' + event index (e.g. '%1' for first event)
2313 * 2. full event name (e.g. sched:sched_switch)
2314 * 3. partial event name (should not contain ':')
2316 static struct perf_evsel
*find_evsel(struct perf_evlist
*evlist
, char *event_name
)
2318 struct perf_evsel
*evsel
= NULL
;
2319 struct perf_evsel
*pos
;
2323 if (event_name
[0] == '%') {
2324 int nr
= strtol(event_name
+1, NULL
, 0);
2326 if (nr
> evlist
->nr_entries
)
2329 evsel
= perf_evlist__first(evlist
);
2331 evsel
= perf_evsel__next(evsel
);
2336 full_name
= !!strchr(event_name
, ':');
2337 evlist__for_each_entry(evlist
, pos
) {
2339 if (full_name
&& !strcmp(pos
->name
, event_name
))
2342 if (!full_name
&& strstr(pos
->name
, event_name
)) {
2344 pr_debug("'%s' event is ambiguous: it can be %s or %s\n",
2345 event_name
, evsel
->name
, pos
->name
);
2355 static int __dynamic_dimension__add(struct perf_evsel
*evsel
,
2356 struct tep_format_field
*field
,
2357 bool raw_trace
, int level
)
2359 struct hpp_dynamic_entry
*hde
;
2361 hde
= __alloc_dynamic_entry(evsel
, field
, level
);
2365 hde
->raw_trace
= raw_trace
;
2367 perf_hpp__register_sort_field(&hde
->hpp
);
2371 static int add_evsel_fields(struct perf_evsel
*evsel
, bool raw_trace
, int level
)
2374 struct tep_format_field
*field
;
2376 field
= evsel
->tp_format
->format
.fields
;
2378 ret
= __dynamic_dimension__add(evsel
, field
, raw_trace
, level
);
2382 field
= field
->next
;
2387 static int add_all_dynamic_fields(struct perf_evlist
*evlist
, bool raw_trace
,
2391 struct perf_evsel
*evsel
;
2393 evlist__for_each_entry(evlist
, evsel
) {
2394 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
)
2397 ret
= add_evsel_fields(evsel
, raw_trace
, level
);
2404 static int add_all_matching_fields(struct perf_evlist
*evlist
,
2405 char *field_name
, bool raw_trace
, int level
)
2408 struct perf_evsel
*evsel
;
2409 struct tep_format_field
*field
;
2411 evlist__for_each_entry(evlist
, evsel
) {
2412 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
)
2415 field
= tep_find_any_field(evsel
->tp_format
, field_name
);
2419 ret
= __dynamic_dimension__add(evsel
, field
, raw_trace
, level
);
2426 static int add_dynamic_entry(struct perf_evlist
*evlist
, const char *tok
,
2429 char *str
, *event_name
, *field_name
, *opt_name
;
2430 struct perf_evsel
*evsel
;
2431 struct tep_format_field
*field
;
2432 bool raw_trace
= symbol_conf
.raw_trace
;
2442 if (parse_field_name(str
, &event_name
, &field_name
, &opt_name
) < 0) {
2448 if (strcmp(opt_name
, "raw")) {
2449 pr_debug("unsupported field option %s\n", opt_name
);
2456 if (!strcmp(field_name
, "trace_fields")) {
2457 ret
= add_all_dynamic_fields(evlist
, raw_trace
, level
);
2461 if (event_name
== NULL
) {
2462 ret
= add_all_matching_fields(evlist
, field_name
, raw_trace
, level
);
2466 evsel
= find_evsel(evlist
, event_name
);
2467 if (evsel
== NULL
) {
2468 pr_debug("Cannot find event: %s\n", event_name
);
2473 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
) {
2474 pr_debug("%s is not a tracepoint event\n", event_name
);
2479 if (!strcmp(field_name
, "*")) {
2480 ret
= add_evsel_fields(evsel
, raw_trace
, level
);
2482 field
= tep_find_any_field(evsel
->tp_format
, field_name
);
2483 if (field
== NULL
) {
2484 pr_debug("Cannot find event field for %s.%s\n",
2485 event_name
, field_name
);
2489 ret
= __dynamic_dimension__add(evsel
, field
, raw_trace
, level
);
2497 static int __sort_dimension__add(struct sort_dimension
*sd
,
2498 struct perf_hpp_list
*list
,
2504 if (__sort_dimension__add_hpp_sort(sd
, list
, level
) < 0)
2507 if (sd
->entry
->se_collapse
)
2508 list
->need_collapse
= 1;
2515 static int __hpp_dimension__add(struct hpp_dimension
*hd
,
2516 struct perf_hpp_list
*list
,
2519 struct perf_hpp_fmt
*fmt
;
2524 fmt
= __hpp_dimension__alloc_hpp(hd
, level
);
2529 perf_hpp_list__register_sort_field(list
, fmt
);
2533 static int __sort_dimension__add_output(struct perf_hpp_list
*list
,
2534 struct sort_dimension
*sd
)
2539 if (__sort_dimension__add_hpp_output(sd
, list
) < 0)
2546 static int __hpp_dimension__add_output(struct perf_hpp_list
*list
,
2547 struct hpp_dimension
*hd
)
2549 struct perf_hpp_fmt
*fmt
;
2554 fmt
= __hpp_dimension__alloc_hpp(hd
, 0);
2559 perf_hpp_list__column_register(list
, fmt
);
2563 int hpp_dimension__add_output(unsigned col
)
2565 BUG_ON(col
>= PERF_HPP__MAX_INDEX
);
2566 return __hpp_dimension__add_output(&perf_hpp_list
, &hpp_sort_dimensions
[col
]);
2569 int sort_dimension__add(struct perf_hpp_list
*list
, const char *tok
,
2570 struct perf_evlist
*evlist
,
2575 for (i
= 0; i
< ARRAY_SIZE(common_sort_dimensions
); i
++) {
2576 struct sort_dimension
*sd
= &common_sort_dimensions
[i
];
2578 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2581 if (sd
->entry
== &sort_parent
) {
2582 int ret
= regcomp(&parent_regex
, parent_pattern
, REG_EXTENDED
);
2586 regerror(ret
, &parent_regex
, err
, sizeof(err
));
2587 pr_err("Invalid regex: %s\n%s", parent_pattern
, err
);
2591 } else if (sd
->entry
== &sort_sym
) {
2594 * perf diff displays the performance difference amongst
2595 * two or more perf.data files. Those files could come
2596 * from different binaries. So we should not compare
2597 * their ips, but the name of symbol.
2599 if (sort__mode
== SORT_MODE__DIFF
)
2600 sd
->entry
->se_collapse
= sort__sym_sort
;
2602 } else if (sd
->entry
== &sort_dso
) {
2604 } else if (sd
->entry
== &sort_socket
) {
2606 } else if (sd
->entry
== &sort_thread
) {
2608 } else if (sd
->entry
== &sort_comm
) {
2612 return __sort_dimension__add(sd
, list
, level
);
2615 for (i
= 0; i
< ARRAY_SIZE(hpp_sort_dimensions
); i
++) {
2616 struct hpp_dimension
*hd
= &hpp_sort_dimensions
[i
];
2618 if (strncasecmp(tok
, hd
->name
, strlen(tok
)))
2621 return __hpp_dimension__add(hd
, list
, level
);
2624 for (i
= 0; i
< ARRAY_SIZE(bstack_sort_dimensions
); i
++) {
2625 struct sort_dimension
*sd
= &bstack_sort_dimensions
[i
];
2627 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2630 if (sort__mode
!= SORT_MODE__BRANCH
)
2633 if (sd
->entry
== &sort_sym_from
|| sd
->entry
== &sort_sym_to
)
2636 __sort_dimension__add(sd
, list
, level
);
2640 for (i
= 0; i
< ARRAY_SIZE(memory_sort_dimensions
); i
++) {
2641 struct sort_dimension
*sd
= &memory_sort_dimensions
[i
];
2643 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2646 if (sort__mode
!= SORT_MODE__MEMORY
)
2649 if (sd
->entry
== &sort_mem_dcacheline
&& cacheline_size() == 0)
2652 if (sd
->entry
== &sort_mem_daddr_sym
)
2655 __sort_dimension__add(sd
, list
, level
);
2659 if (!add_dynamic_entry(evlist
, tok
, level
))
2665 static int setup_sort_list(struct perf_hpp_list
*list
, char *str
,
2666 struct perf_evlist
*evlist
)
2672 bool in_group
= false;
2676 tmp
= strpbrk(str
, "{}, ");
2681 next_level
= level
+ 1;
2685 else if (*tmp
== '}')
2693 ret
= sort_dimension__add(list
, tok
, evlist
, level
);
2694 if (ret
== -EINVAL
) {
2695 if (!cacheline_size() && !strncasecmp(tok
, "dcacheline", strlen(tok
)))
2696 pr_err("The \"dcacheline\" --sort key needs to know the cacheline size and it couldn't be determined on this system");
2698 pr_err("Invalid --sort key: `%s'", tok
);
2700 } else if (ret
== -ESRCH
) {
2701 pr_err("Unknown --sort key: `%s'", tok
);
2712 static const char *get_default_sort_order(struct perf_evlist
*evlist
)
2714 const char *default_sort_orders
[] = {
2716 default_branch_sort_order
,
2717 default_mem_sort_order
,
2718 default_top_sort_order
,
2719 default_diff_sort_order
,
2720 default_tracepoint_sort_order
,
2722 bool use_trace
= true;
2723 struct perf_evsel
*evsel
;
2725 BUG_ON(sort__mode
>= ARRAY_SIZE(default_sort_orders
));
2727 if (evlist
== NULL
|| perf_evlist__empty(evlist
))
2730 evlist__for_each_entry(evlist
, evsel
) {
2731 if (evsel
->attr
.type
!= PERF_TYPE_TRACEPOINT
) {
2738 sort__mode
= SORT_MODE__TRACEPOINT
;
2739 if (symbol_conf
.raw_trace
)
2740 return "trace_fields";
2743 return default_sort_orders
[sort__mode
];
2746 static int setup_sort_order(struct perf_evlist
*evlist
)
2748 char *new_sort_order
;
2751 * Append '+'-prefixed sort order to the default sort
2754 if (!sort_order
|| is_strict_order(sort_order
))
2757 if (sort_order
[1] == '\0') {
2758 pr_err("Invalid --sort key: `+'");
2763 * We allocate new sort_order string, but we never free it,
2764 * because it's checked over the rest of the code.
2766 if (asprintf(&new_sort_order
, "%s,%s",
2767 get_default_sort_order(evlist
), sort_order
+ 1) < 0) {
2768 pr_err("Not enough memory to set up --sort");
2772 sort_order
= new_sort_order
;
2777 * Adds 'pre,' prefix into 'str' is 'pre' is
2778 * not already part of 'str'.
2780 static char *prefix_if_not_in(const char *pre
, char *str
)
2784 if (!str
|| strstr(str
, pre
))
2787 if (asprintf(&n
, "%s,%s", pre
, str
) < 0)
2794 static char *setup_overhead(char *keys
)
2796 if (sort__mode
== SORT_MODE__DIFF
)
2799 keys
= prefix_if_not_in("overhead", keys
);
2801 if (symbol_conf
.cumulate_callchain
)
2802 keys
= prefix_if_not_in("overhead_children", keys
);
2807 static int __setup_sorting(struct perf_evlist
*evlist
)
2810 const char *sort_keys
;
2813 ret
= setup_sort_order(evlist
);
2817 sort_keys
= sort_order
;
2818 if (sort_keys
== NULL
) {
2819 if (is_strict_order(field_order
)) {
2821 * If user specified field order but no sort order,
2822 * we'll honor it and not add default sort orders.
2827 sort_keys
= get_default_sort_order(evlist
);
2830 str
= strdup(sort_keys
);
2832 pr_err("Not enough memory to setup sort keys");
2837 * Prepend overhead fields for backward compatibility.
2839 if (!is_strict_order(field_order
)) {
2840 str
= setup_overhead(str
);
2842 pr_err("Not enough memory to setup overhead keys");
2847 ret
= setup_sort_list(&perf_hpp_list
, str
, evlist
);
2853 void perf_hpp__set_elide(int idx
, bool elide
)
2855 struct perf_hpp_fmt
*fmt
;
2856 struct hpp_sort_entry
*hse
;
2858 perf_hpp_list__for_each_format(&perf_hpp_list
, fmt
) {
2859 if (!perf_hpp__is_sort_entry(fmt
))
2862 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
2863 if (hse
->se
->se_width_idx
== idx
) {
2870 static bool __get_elide(struct strlist
*list
, const char *list_name
, FILE *fp
)
2872 if (list
&& strlist__nr_entries(list
) == 1) {
2874 fprintf(fp
, "# %s: %s\n", list_name
,
2875 strlist__entry(list
, 0)->s
);
2881 static bool get_elide(int idx
, FILE *output
)
2885 return __get_elide(symbol_conf
.sym_list
, "symbol", output
);
2887 return __get_elide(symbol_conf
.dso_list
, "dso", output
);
2889 return __get_elide(symbol_conf
.comm_list
, "comm", output
);
2894 if (sort__mode
!= SORT_MODE__BRANCH
)
2898 case HISTC_SYMBOL_FROM
:
2899 return __get_elide(symbol_conf
.sym_from_list
, "sym_from", output
);
2900 case HISTC_SYMBOL_TO
:
2901 return __get_elide(symbol_conf
.sym_to_list
, "sym_to", output
);
2902 case HISTC_DSO_FROM
:
2903 return __get_elide(symbol_conf
.dso_from_list
, "dso_from", output
);
2905 return __get_elide(symbol_conf
.dso_to_list
, "dso_to", output
);
2913 void sort__setup_elide(FILE *output
)
2915 struct perf_hpp_fmt
*fmt
;
2916 struct hpp_sort_entry
*hse
;
2918 perf_hpp_list__for_each_format(&perf_hpp_list
, fmt
) {
2919 if (!perf_hpp__is_sort_entry(fmt
))
2922 hse
= container_of(fmt
, struct hpp_sort_entry
, hpp
);
2923 fmt
->elide
= get_elide(hse
->se
->se_width_idx
, output
);
2927 * It makes no sense to elide all of sort entries.
2928 * Just revert them to show up again.
2930 perf_hpp_list__for_each_format(&perf_hpp_list
, fmt
) {
2931 if (!perf_hpp__is_sort_entry(fmt
))
2938 perf_hpp_list__for_each_format(&perf_hpp_list
, fmt
) {
2939 if (!perf_hpp__is_sort_entry(fmt
))
2946 int output_field_add(struct perf_hpp_list
*list
, char *tok
)
2950 for (i
= 0; i
< ARRAY_SIZE(common_sort_dimensions
); i
++) {
2951 struct sort_dimension
*sd
= &common_sort_dimensions
[i
];
2953 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2956 return __sort_dimension__add_output(list
, sd
);
2959 for (i
= 0; i
< ARRAY_SIZE(hpp_sort_dimensions
); i
++) {
2960 struct hpp_dimension
*hd
= &hpp_sort_dimensions
[i
];
2962 if (strncasecmp(tok
, hd
->name
, strlen(tok
)))
2965 return __hpp_dimension__add_output(list
, hd
);
2968 for (i
= 0; i
< ARRAY_SIZE(bstack_sort_dimensions
); i
++) {
2969 struct sort_dimension
*sd
= &bstack_sort_dimensions
[i
];
2971 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2974 return __sort_dimension__add_output(list
, sd
);
2977 for (i
= 0; i
< ARRAY_SIZE(memory_sort_dimensions
); i
++) {
2978 struct sort_dimension
*sd
= &memory_sort_dimensions
[i
];
2980 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
2983 return __sort_dimension__add_output(list
, sd
);
2989 static int setup_output_list(struct perf_hpp_list
*list
, char *str
)
2994 for (tok
= strtok_r(str
, ", ", &tmp
);
2995 tok
; tok
= strtok_r(NULL
, ", ", &tmp
)) {
2996 ret
= output_field_add(list
, tok
);
2997 if (ret
== -EINVAL
) {
2998 ui__error("Invalid --fields key: `%s'", tok
);
3000 } else if (ret
== -ESRCH
) {
3001 ui__error("Unknown --fields key: `%s'", tok
);
3009 void reset_dimensions(void)
3013 for (i
= 0; i
< ARRAY_SIZE(common_sort_dimensions
); i
++)
3014 common_sort_dimensions
[i
].taken
= 0;
3016 for (i
= 0; i
< ARRAY_SIZE(hpp_sort_dimensions
); i
++)
3017 hpp_sort_dimensions
[i
].taken
= 0;
3019 for (i
= 0; i
< ARRAY_SIZE(bstack_sort_dimensions
); i
++)
3020 bstack_sort_dimensions
[i
].taken
= 0;
3022 for (i
= 0; i
< ARRAY_SIZE(memory_sort_dimensions
); i
++)
3023 memory_sort_dimensions
[i
].taken
= 0;
3026 bool is_strict_order(const char *order
)
3028 return order
&& (*order
!= '+');
3031 static int __setup_output_field(void)
3036 if (field_order
== NULL
)
3039 strp
= str
= strdup(field_order
);
3041 pr_err("Not enough memory to setup output fields");
3045 if (!is_strict_order(field_order
))
3048 if (!strlen(strp
)) {
3049 pr_err("Invalid --fields key: `+'");
3053 ret
= setup_output_list(&perf_hpp_list
, strp
);
3060 int setup_sorting(struct perf_evlist
*evlist
)
3064 err
= __setup_sorting(evlist
);
3068 if (parent_pattern
!= default_parent_pattern
) {
3069 err
= sort_dimension__add(&perf_hpp_list
, "parent", evlist
, -1);
3077 * perf diff doesn't use default hpp output fields.
3079 if (sort__mode
!= SORT_MODE__DIFF
)
3082 err
= __setup_output_field();
3086 /* copy sort keys to output fields */
3087 perf_hpp__setup_output_field(&perf_hpp_list
);
3088 /* and then copy output fields to sort keys */
3089 perf_hpp__append_sort_keys(&perf_hpp_list
);
3091 /* setup hists-specific output fields */
3092 if (perf_hpp__setup_hists_formats(&perf_hpp_list
, evlist
) < 0)
3098 void reset_output_field(void)
3100 perf_hpp_list
.need_collapse
= 0;
3101 perf_hpp_list
.parent
= 0;
3102 perf_hpp_list
.sym
= 0;
3103 perf_hpp_list
.dso
= 0;
3109 perf_hpp__reset_output_field(&perf_hpp_list
);
3112 #define INDENT (3*8 + 1)
3114 static void add_key(struct strbuf
*sb
, const char *str
, int *llen
)
3117 strbuf_addstr(sb
, "\n\t\t\t ");
3120 strbuf_addf(sb
, " %s", str
);
3121 *llen
+= strlen(str
) + 1;
3124 static void add_sort_string(struct strbuf
*sb
, struct sort_dimension
*s
, int n
,
3129 for (i
= 0; i
< n
; i
++)
3130 add_key(sb
, s
[i
].name
, llen
);
3133 static void add_hpp_sort_string(struct strbuf
*sb
, struct hpp_dimension
*s
, int n
,
3138 for (i
= 0; i
< n
; i
++)
3139 add_key(sb
, s
[i
].name
, llen
);
3142 const char *sort_help(const char *prefix
)
3146 int len
= strlen(prefix
) + INDENT
;
3148 strbuf_init(&sb
, 300);
3149 strbuf_addstr(&sb
, prefix
);
3150 add_hpp_sort_string(&sb
, hpp_sort_dimensions
,
3151 ARRAY_SIZE(hpp_sort_dimensions
), &len
);
3152 add_sort_string(&sb
, common_sort_dimensions
,
3153 ARRAY_SIZE(common_sort_dimensions
), &len
);
3154 add_sort_string(&sb
, bstack_sort_dimensions
,
3155 ARRAY_SIZE(bstack_sort_dimensions
), &len
);
3156 add_sort_string(&sb
, memory_sort_dimensions
,
3157 ARRAY_SIZE(memory_sort_dimensions
), &len
);
3158 s
= strbuf_detach(&sb
, NULL
);
3159 strbuf_release(&sb
);