projects/linux·blob master · refs

4519 lines · 110.8 KiB

1// SPDX-License-Identifier: GPL-2.0
2#include "sort.h"
3
4#include <ctype.h>
5#include <errno.h>
6#include <inttypes.h>
7#include <stdlib.h>
8
9#include <elf.h>
10#include <linux/kernel.h>
11#include <linux/mman.h>
12#include <linux/string.h>
13#include <linux/time64.h>
14
15#include <regex.h>
16
17#include "annotate-data.h"
18#include "annotate.h"
19#include "branch.h"
20#include "cacheline.h"
21#include "cgroup.h"
22#include "comm.h"
23#include "debug.h"
24#include "dso.h"
25#include "event.h"
26#include "evlist.h"
27#include "evsel.h"
28#include "hist.h"
29#include "machine.h"
30#include "map.h"
31#include "map_symbol.h"
32#include "maps.h"
33#include "mem-events.h"
34#include "mem-info.h"
35#include "session.h"
36#include "srcline.h"
37#include "strbuf.h"
38#include "strlist.h"
39#include "symbol.h"
40#include "thread.h"
41#include "time-utils.h"
42#include "trace-event.h"
43
44#ifdef HAVE_LIBTRACEEVENT
45#include <event-parse.h>
46#endif
47
48regex_t parent_regex;
49const char default_parent_pattern[] = "^sys_|^do_page_fault";
50const char *parent_pattern = default_parent_pattern;
51const char *default_sort_order = "comm,dso,symbol";
52static const char default_branch_sort_order[] = "comm,dso_from,symbol_from,symbol_to,cycles";
53const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked,blocked,local_ins_lat,local_p_stage_cyc";
54static const char default_top_sort_order[] = "dso,symbol";
55static const char default_diff_sort_order[] = "dso,symbol";
56static const char default_tracepoint_sort_order[] = "trace";
57const char *sort_order;
58const char *field_order;
59regex_t ignore_callees_regex;
60int have_ignore_callees = 0;
61enum sort_mode sort__mode = SORT_MODE__NORMAL;
62static const char *const dynamic_headers[] = {"local_ins_lat", "ins_lat", "local_p_stage_cyc", "p_stage_cyc"};
63static const char *const arch_specific_sort_keys[] = {"local_p_stage_cyc", "p_stage_cyc"};
64
65/*
66 * Some architectures have Adjacent Cacheline Prefetch feature, which
67 * behaves like the cacheline size is doubled. Enable this flag to
68 * check things in double cacheline granularity.
69 */
70bool chk_double_cl;
71
72/*
73 * Replaces all occurrences of a char used with the:
74 *
75 * -t, --field-separator
76 *
77 * option, that uses a special separator character and don't pad with spaces,
78 * replacing all occurrences of this separator in symbol names (and other
79 * output) with a '.' character, that thus it's the only non valid separator.
80*/
81static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
82{
83 int n;
84 va_list ap;
85
86 va_start(ap, fmt);
87 n = vsnprintf(bf, size, fmt, ap);
88 if (symbol_conf.field_sep && n > 0) {
89 char *sep = bf;
90
91 while (1) {
92 sep = strchr(sep, *symbol_conf.field_sep);
93 if (sep == NULL)
94 break;
95 *sep = '.';
96 }
97 }
98 va_end(ap);
99
100 if (n >= (int)size)
101 return size - 1;
102 return n;
103}
104
105static int64_t cmp_null(const void *l, const void *r)
106{
107 if (!l && !r)
108 return 0;
109 else if (!l)
110 return -1;
111 else
112 return 1;
113}
114
115/* --sort pid */
116
117static int64_t
118sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
119{
120 return thread__tid(right->thread) - thread__tid(left->thread);
121}
122
123static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,
124 size_t size, unsigned int width)
125{
126 const char *comm = thread__comm_str(he->thread);
127
128 width = max(7U, width) - 8;
129 return repsep_snprintf(bf, size, "%7d:%-*.*s", thread__tid(he->thread),
130 width, width, comm ?: "");
131}
132
133static int hist_entry__thread_filter(struct hist_entry *he, int type, const void *arg)
134{
135 const struct thread *th = arg;
136
137 if (type != HIST_FILTER__THREAD)
138 return -1;
139
140 return th && !RC_CHK_EQUAL(he->thread, th);
141}
142
143struct sort_entry sort_thread = {
144 .se_header = " Pid:Command",
145 .se_cmp = sort__thread_cmp,
146 .se_snprintf = hist_entry__thread_snprintf,
147 .se_filter = hist_entry__thread_filter,
148 .se_width_idx = HISTC_THREAD,
149};
150
151/* --sort tgid */
152
153static int64_t
154sort__tgid_cmp(struct hist_entry *left, struct hist_entry *right)
155{
156 return thread__pid(right->thread) - thread__pid(left->thread);
157}
158
159static int hist_entry__tgid_snprintf(struct hist_entry *he, char *bf,
160 size_t size, unsigned int width)
161{
162 int tgid = thread__pid(he->thread);
163 const char *comm = NULL;
164
165 /* display comm of the thread-group leader */
166 if (thread__pid(he->thread) == thread__tid(he->thread)) {
167 comm = thread__comm_str(he->thread);
168 } else {
169 struct maps *maps = thread__maps(he->thread);
170 struct thread *leader = machine__find_thread(maps__machine(maps),
171 tgid, tgid);
172 if (leader) {
173 comm = thread__comm_str(leader);
174 thread__put(leader);
175 }
176 }
177 width = max(7U, width) - 8;
178 return repsep_snprintf(bf, size, "%7d:%-*.*s", tgid, width, width, comm ?: "");
179}
180
181static struct sort_entry sort_tgid = {
182 .se_header = " Tgid:Command",
183 .se_cmp = sort__tgid_cmp,
184 .se_snprintf = hist_entry__tgid_snprintf,
185 .se_width_idx = HISTC_TGID,
186};
187
188/* --sort simd */
189
190static int64_t
191sort__simd_cmp(struct hist_entry *left, struct hist_entry *right)
192{
193 if (left->simd_flags.arch != right->simd_flags.arch)
194 return (int64_t) left->simd_flags.arch - right->simd_flags.arch;
195
196 return (int64_t) left->simd_flags.pred - right->simd_flags.pred;
197}
198
199static const char *hist_entry__get_simd_name(struct simd_flags *simd_flags)
200{
201 u64 arch = simd_flags->arch;
202
203 if (arch == SIMD_OP_FLAGS_ARCH_SVE)
204 return "SVE";
205 else if (arch == SIMD_OP_FLAGS_ARCH_SME)
206 return "SME";
207 else if (arch == SIMD_OP_FLAGS_ARCH_ASE)
208 return "ASE";
209 else
210 return "n/a";
211}
212
213static int hist_entry__simd_snprintf(struct hist_entry *he, char *bf,
214 size_t size, unsigned int width __maybe_unused)
215{
216 const char *name;
217 const char *pred_str = ".";
218
219 if (!he->simd_flags.arch)
220 return repsep_snprintf(bf, size, "");
221
222 name = hist_entry__get_simd_name(&he->simd_flags);
223
224 if (he->simd_flags.pred == SIMD_OP_FLAGS_PRED_EMPTY)
225 pred_str = "e";
226 else if (he->simd_flags.pred == SIMD_OP_FLAGS_PRED_PARTIAL)
227 pred_str = "p";
228 else if (he->simd_flags.pred == SIMD_OP_FLAGS_PRED_DISABLED)
229 pred_str = "d";
230 else if (he->simd_flags.pred == SIMD_OP_FLAGS_PRED_FULL)
231 pred_str = "f";
232
233 return repsep_snprintf(bf, size, "[%s] %s", pred_str, name);
234}
235
236static struct sort_entry sort_simd = {
237 .se_header = "Simd ",
238 .se_cmp = sort__simd_cmp,
239 .se_snprintf = hist_entry__simd_snprintf,
240 .se_width_idx = HISTC_SIMD,
241};
242
243/* --sort comm */
244
245/*
246 * We can't use pointer comparison in functions below,
247 * because it gives different results based on pointer
248 * values, which could break some sorting assumptions.
249 */
250static int64_t
251sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
252{
253 return strcmp(comm__str(right->comm), comm__str(left->comm));
254}
255
256static int64_t
257sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
258{
259 return strcmp(comm__str(right->comm), comm__str(left->comm));
260}
261
262static int64_t
263sort__comm_sort(struct hist_entry *left, struct hist_entry *right)
264{
265 return strcmp(comm__str(right->comm), comm__str(left->comm));
266}
267
268static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
269 size_t size, unsigned int width)
270{
271 return repsep_snprintf(bf, size, "%-*.*s", width, width, comm__str(he->comm));
272}
273
274struct sort_entry sort_comm = {
275 .se_header = "Command",
276 .se_cmp = sort__comm_cmp,
277 .se_collapse = sort__comm_collapse,
278 .se_sort = sort__comm_sort,
279 .se_snprintf = hist_entry__comm_snprintf,
280 .se_filter = hist_entry__thread_filter,
281 .se_width_idx = HISTC_COMM,
282};
283
284/* --sort comm_nodigit */
285
286size_t sort__comm_nodigit_len(struct hist_entry *entry)
287{
288 const char *comm = comm__str(entry->comm);
289 size_t index, len_nodigit = 0;
290 bool in_number = false;
291
292 if (!comm)
293 return 0;
294
295 for (index = 0; comm[index]; index++) {
296 if (!isdigit((unsigned char)comm[index])) {
297 in_number = false;
298 len_nodigit++;
299 } else if (!in_number) {
300 in_number = true;
301 len_nodigit += 3; /* <N> */
302 }
303 }
304
305 return len_nodigit;
306}
307
308static int64_t strcmp_nodigit(const char *left, const char *right)
309{
310 for (;;) {
311 while (*left && isdigit((unsigned char)*left))
312 left++;
313 while (*right && isdigit((unsigned char)*right))
314 right++;
315 if (*left == *right && !*left) {
316 return 0;
317 } else if (*left == *right) {
318 left++;
319 right++;
320 } else {
321 return (int64_t)((unsigned char)*left - (unsigned char)*right);
322 }
323 }
324}
325
326static int64_t
327sort__comm_nodigit_cmp(struct hist_entry *left, struct hist_entry *right)
328{
329 return strcmp_nodigit(comm__str(right->comm), comm__str(left->comm));
330}
331
332static int64_t
333sort__comm_nodigit_collapse(struct hist_entry *left, struct hist_entry *right)
334{
335 return strcmp_nodigit(comm__str(right->comm), comm__str(left->comm));
336}
337
338static int64_t
339sort__comm_nodigit_sort(struct hist_entry *left, struct hist_entry *right)
340{
341 return strcmp_nodigit(comm__str(right->comm), comm__str(left->comm));
342}
343
344static int hist_entry__comm_nodigit_snprintf(struct hist_entry *he, char *bf,
345 size_t size, unsigned int width)
346{
347 int ret = 0;
348 unsigned int print_len, printed = 0, start = 0, end = 0;
349 bool in_digit;
350 const char *comm = comm__str(he->comm), *print;
351
352 while (printed < width && printed < size && comm[start]) {
353 in_digit = !!isdigit((unsigned char)comm[start]);
354 end = start + 1;
355 while (comm[end] && !!isdigit((unsigned char)comm[end]) == in_digit)
356 end++;
357 if (in_digit) {
358 print_len = 3; /* <N> */
359 print = "<N>";
360 } else {
361 print_len = end - start;
362 print = &comm[start];
363 }
364 print_len = min(print_len, width - printed);
365 ret = repsep_snprintf(bf + printed, size - printed, "%-.*s",
366 print_len, print);
367 if (ret < 0)
368 return ret;
369 start = end;
370 printed += ret;
371 }
372 /* Pad to width if necessary */
373 if (printed < width && printed < size) {
374 ret = repsep_snprintf(bf + printed, size - printed, "%-*.*s",
375 width - printed, width - printed, "");
376 if (ret < 0)
377 return ret;
378 printed += ret;
379 }
380 return printed;
381}
382
383struct sort_entry sort_comm_nodigit = {
384 .se_header = "CommandNoDigit",
385 .se_cmp = sort__comm_nodigit_cmp,
386 .se_collapse = sort__comm_nodigit_collapse,
387 .se_sort = sort__comm_nodigit_sort,
388 .se_snprintf = hist_entry__comm_nodigit_snprintf,
389 .se_filter = hist_entry__thread_filter,
390 .se_width_idx = HISTC_COMM_NODIGIT,
391};
392
393/* --sort dso */
394
395static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
396{
397 struct dso *dso_l = map_l ? map__dso(map_l) : NULL;
398 struct dso *dso_r = map_r ? map__dso(map_r) : NULL;
399 const char *dso_name_l, *dso_name_r;
400
401 if (!dso_l || !dso_r)
402 return cmp_null(dso_r, dso_l);
403
404 if (verbose > 0) {
405 dso_name_l = dso__long_name(dso_l);
406 dso_name_r = dso__long_name(dso_r);
407 } else {
408 dso_name_l = dso__short_name(dso_l);
409 dso_name_r = dso__short_name(dso_r);
410 }
411
412 return strcmp(dso_name_l, dso_name_r);
413}
414
415static int64_t
416sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
417{
418 return _sort__dso_cmp(right->ms.map, left->ms.map);
419}
420
421static int _hist_entry__dso_snprintf(struct map *map, char *bf,
422 size_t size, unsigned int width)
423{
424 const struct dso *dso = map ? map__dso(map) : NULL;
425 const char *dso_name = "[unknown]";
426
427 if (dso)
428 dso_name = verbose > 0 ? dso__long_name(dso) : dso__short_name(dso);
429
430 return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name);
431}
432
433static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,
434 size_t size, unsigned int width)
435{
436 return _hist_entry__dso_snprintf(he->ms.map, bf, size, width);
437}
438
439static int hist_entry__dso_filter(struct hist_entry *he, int type, const void *arg)
440{
441 const struct dso *dso = arg;
442
443 if (type != HIST_FILTER__DSO)
444 return -1;
445
446 return dso && (!he->ms.map || map__dso(he->ms.map) != dso);
447}
448
449struct sort_entry sort_dso = {
450 .se_header = "Shared Object",
451 .se_cmp = sort__dso_cmp,
452 .se_snprintf = hist_entry__dso_snprintf,
453 .se_filter = hist_entry__dso_filter,
454 .se_width_idx = HISTC_DSO,
455};
456
457/* --sort symbol */
458
459static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip)
460{
461 return (int64_t)(right_ip - left_ip);
462}
463
464int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
465{
466 if (!sym_l || !sym_r)
467 return cmp_null(sym_l, sym_r);
468
469 if (sym_l == sym_r)
470 return 0;
471
472 if (symbol__inlined(sym_l) || symbol__inlined(sym_r)) {
473 int ret = strcmp(sym_l->name, sym_r->name);
474
475 if (ret)
476 return ret;
477 if ((sym_l->start <= sym_r->end) && (sym_l->end >= sym_r->start))
478 return 0;
479 }
480
481 if (sym_l->start != sym_r->start)
482 return (int64_t)(sym_r->start - sym_l->start);
483
484 return (int64_t)(sym_r->end - sym_l->end);
485}
486
487static int64_t
488sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
489{
490 int64_t ret;
491
492 if (!left->ms.sym && !right->ms.sym)
493 return _sort__addr_cmp(left->ip, right->ip);
494
495 /*
496 * comparing symbol address alone is not enough since it's a
497 * relative address within a dso.
498 */
499 if (!hists__has(left->hists, dso)) {
500 ret = sort__dso_cmp(left, right);
501 if (ret != 0)
502 return ret;
503 }
504
505 return _sort__sym_cmp(left->ms.sym, right->ms.sym);
506}
507
508static int64_t
509sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
510{
511 if (!left->ms.sym || !right->ms.sym)
512 return cmp_null(left->ms.sym, right->ms.sym);
513
514 return strcmp(right->ms.sym->name, left->ms.sym->name);
515}
516
517static int _hist_entry__sym_snprintf(struct map_symbol *ms,
518 u64 ip, char level, char *bf, size_t size,
519 unsigned int width)
520{
521 struct symbol *sym = ms->sym;
522 struct map *map = ms->map;
523 size_t ret = 0;
524
525 if (verbose > 0) {
526 struct dso *dso = map ? map__dso(map) : NULL;
527 char o = dso ? dso__symtab_origin(dso) : '!';
528 u64 rip = ip;
529
530 if (dso && dso__kernel(dso) && dso__adjust_symbols(dso))
531 rip = map__unmap_ip(map, ip);
532
533 ret += repsep_snprintf(bf, size, "%-#*llx %c ",
534 BITS_PER_LONG / 4 + 2, rip, o);
535 }
536
537 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
538 if (sym && map) {
539 if (symbol__type(sym) == STT_OBJECT) {
540 ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
541 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
542 ip - map__unmap_ip(map, sym->start));
543 } else {
544 ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
545 width - ret,
546 sym->name);
547 if (symbol__inlined(sym))
548 ret += repsep_snprintf(bf + ret, size - ret,
549 " (inlined)");
550 }
551 } else {
552 size_t len = BITS_PER_LONG / 4;
553 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
554 len, ip);
555 }
556
557 return ret;
558}
559
560int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width)
561{
562 return _hist_entry__sym_snprintf(&he->ms, he->ip,
563 he->level, bf, size, width);
564}
565
566static int hist_entry__sym_filter(struct hist_entry *he, int type, const void *arg)
567{
568 const char *sym = arg;
569
570 if (type != HIST_FILTER__SYMBOL)
571 return -1;
572
573 return sym && (!he->ms.sym || !strstr(he->ms.sym->name, sym));
574}
575
576struct sort_entry sort_sym = {
577 .se_header = "Symbol",
578 .se_cmp = sort__sym_cmp,
579 .se_sort = sort__sym_sort,
580 .se_snprintf = hist_entry__sym_snprintf,
581 .se_filter = hist_entry__sym_filter,
582 .se_width_idx = HISTC_SYMBOL,
583};
584
585/* --sort symoff */
586
587static int64_t
588sort__symoff_cmp(struct hist_entry *left, struct hist_entry *right)
589{
590 int64_t ret;
591
592 ret = sort__sym_cmp(left, right);
593 if (ret)
594 return ret;
595
596 return left->ip - right->ip;
597}
598
599static int64_t
600sort__symoff_sort(struct hist_entry *left, struct hist_entry *right)
601{
602 int64_t ret;
603
604 ret = sort__sym_sort(left, right);
605 if (ret)
606 return ret;
607
608 return left->ip - right->ip;
609}
610
611static int
612hist_entry__symoff_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width)
613{
614 struct symbol *sym = he->ms.sym;
615
616 if (sym == NULL)
617 return repsep_snprintf(bf, size, "[%c] %-#.*llx", he->level, width - 4, he->ip);
618
619 return repsep_snprintf(bf, size, "[%c] %s+0x%llx", he->level, sym->name, he->ip - sym->start);
620}
621
622static struct sort_entry sort_sym_offset = {
623 .se_header = "Symbol Offset",
624 .se_cmp = sort__symoff_cmp,
625 .se_sort = sort__symoff_sort,
626 .se_snprintf = hist_entry__symoff_snprintf,
627 .se_filter = hist_entry__sym_filter,
628 .se_width_idx = HISTC_SYMBOL_OFFSET,
629};
630
631/* --sort srcline */
632
633char *hist_entry__srcline(struct hist_entry *he)
634{
635 return map__srcline(he->ms.map, he->ip, he->ms.sym);
636}
637
638static int64_t
639sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
640{
641 int64_t ret;
642
643 ret = _sort__addr_cmp(left->ip, right->ip);
644 if (ret)
645 return ret;
646
647 return sort__dso_cmp(left, right);
648}
649
650static int64_t
651sort__srcline_collapse(struct hist_entry *left, struct hist_entry *right)
652{
653 if (!left->srcline)
654 left->srcline = hist_entry__srcline(left);
655 if (!right->srcline)
656 right->srcline = hist_entry__srcline(right);
657
658 return strcmp(right->srcline, left->srcline);
659}
660
661static int64_t
662sort__srcline_sort(struct hist_entry *left, struct hist_entry *right)
663{
664 return sort__srcline_collapse(left, right);
665}
666
667static void
668sort__srcline_init(struct hist_entry *he)
669{
670 if (!he->srcline)
671 he->srcline = hist_entry__srcline(he);
672}
673
674static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
675 size_t size, unsigned int width)
676{
677 return repsep_snprintf(bf, size, "%-.*s", width, he->srcline);
678}
679
680struct sort_entry sort_srcline = {
681 .se_header = "Source:Line",
682 .se_cmp = sort__srcline_cmp,
683 .se_collapse = sort__srcline_collapse,
684 .se_sort = sort__srcline_sort,
685 .se_init = sort__srcline_init,
686 .se_snprintf = hist_entry__srcline_snprintf,
687 .se_width_idx = HISTC_SRCLINE,
688};
689
690/* --sort srcline_from */
691
692static char *addr_map_symbol__srcline(struct addr_map_symbol *ams)
693{
694 return map__srcline(ams->ms.map, ams->al_addr, ams->ms.sym);
695}
696
697static int64_t
698sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right)
699{
700 return left->branch_info->from.addr - right->branch_info->from.addr;
701}
702
703static int64_t
704sort__srcline_from_collapse(struct hist_entry *left, struct hist_entry *right)
705{
706 if (!left->branch_info->srcline_from)
707 left->branch_info->srcline_from = addr_map_symbol__srcline(&left->branch_info->from);
708
709 if (!right->branch_info->srcline_from)
710 right->branch_info->srcline_from = addr_map_symbol__srcline(&right->branch_info->from);
711
712 return strcmp(right->branch_info->srcline_from, left->branch_info->srcline_from);
713}
714
715static int64_t
716sort__srcline_from_sort(struct hist_entry *left, struct hist_entry *right)
717{
718 return sort__srcline_from_collapse(left, right);
719}
720
721static void sort__srcline_from_init(struct hist_entry *he)
722{
723 if (!he->branch_info->srcline_from)
724 he->branch_info->srcline_from = addr_map_symbol__srcline(&he->branch_info->from);
725}
726
727static int hist_entry__srcline_from_snprintf(struct hist_entry *he, char *bf,
728 size_t size, unsigned int width)
729{
730 return repsep_snprintf(bf, size, "%-*.*s", width, width, he->branch_info->srcline_from);
731}
732
733static struct sort_entry sort_srcline_from = {
734 .se_header = "From Source:Line",
735 .se_cmp = sort__srcline_from_cmp,
736 .se_collapse = sort__srcline_from_collapse,
737 .se_sort = sort__srcline_from_sort,
738 .se_init = sort__srcline_from_init,
739 .se_snprintf = hist_entry__srcline_from_snprintf,
740 .se_width_idx = HISTC_SRCLINE_FROM,
741};
742
743/* --sort srcline_to */
744
745static int64_t
746sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right)
747{
748 return left->branch_info->to.addr - right->branch_info->to.addr;
749}
750
751static int64_t
752sort__srcline_to_collapse(struct hist_entry *left, struct hist_entry *right)
753{
754 if (!left->branch_info->srcline_to)
755 left->branch_info->srcline_to = addr_map_symbol__srcline(&left->branch_info->to);
756
757 if (!right->branch_info->srcline_to)
758 right->branch_info->srcline_to = addr_map_symbol__srcline(&right->branch_info->to);
759
760 return strcmp(right->branch_info->srcline_to, left->branch_info->srcline_to);
761}
762
763static int64_t
764sort__srcline_to_sort(struct hist_entry *left, struct hist_entry *right)
765{
766 return sort__srcline_to_collapse(left, right);
767}
768
769static void sort__srcline_to_init(struct hist_entry *he)
770{
771 if (!he->branch_info->srcline_to)
772 he->branch_info->srcline_to = addr_map_symbol__srcline(&he->branch_info->to);
773}
774
775static int hist_entry__srcline_to_snprintf(struct hist_entry *he, char *bf,
776 size_t size, unsigned int width)
777{
778 return repsep_snprintf(bf, size, "%-*.*s", width, width, he->branch_info->srcline_to);
779}
780
781static struct sort_entry sort_srcline_to = {
782 .se_header = "To Source:Line",
783 .se_cmp = sort__srcline_to_cmp,
784 .se_collapse = sort__srcline_to_collapse,
785 .se_sort = sort__srcline_to_sort,
786 .se_init = sort__srcline_to_init,
787 .se_snprintf = hist_entry__srcline_to_snprintf,
788 .se_width_idx = HISTC_SRCLINE_TO,
789};
790
791static int hist_entry__sym_ipc_snprintf(struct hist_entry *he, char *bf,
792 size_t size, unsigned int width)
793{
794
795 struct symbol *sym = he->ms.sym;
796 struct annotated_branch *branch;
797 double ipc = 0.0, coverage = 0.0;
798 char tmp[64];
799
800 if (!sym)
801 return repsep_snprintf(bf, size, "%-*s", width, "-");
802
803 branch = symbol__annotation(sym)->branch;
804
805 if (branch && branch->hit_cycles)
806 ipc = branch->hit_insn / ((double)branch->hit_cycles);
807
808 if (branch && branch->total_insn) {
809 coverage = branch->cover_insn * 100.0 /
810 ((double)branch->total_insn);
811 }
812
813 snprintf(tmp, sizeof(tmp), "%-5.2f [%5.1f%%]", ipc, coverage);
814 return repsep_snprintf(bf, size, "%-*s", width, tmp);
815}
816
817static struct sort_entry sort_sym_ipc = {
818 .se_header = "IPC [IPC Coverage]",
819 .se_cmp = sort__sym_cmp,
820 .se_snprintf = hist_entry__sym_ipc_snprintf,
821 .se_width_idx = HISTC_SYMBOL_IPC,
822};
823
824static int hist_entry__sym_ipc_null_snprintf(struct hist_entry *he
825 __maybe_unused,
826 char *bf, size_t size,
827 unsigned int width)
828{
829 char tmp[64];
830
831 snprintf(tmp, sizeof(tmp), "%-5s %2s", "-", "-");
832 return repsep_snprintf(bf, size, "%-*s", width, tmp);
833}
834
835static struct sort_entry sort_sym_ipc_null = {
836 .se_header = "IPC [IPC Coverage]",
837 .se_cmp = sort__sym_cmp,
838 .se_snprintf = hist_entry__sym_ipc_null_snprintf,
839 .se_width_idx = HISTC_SYMBOL_IPC,
840};
841
842/* --sort callchain_branch_predicted */
843
844static int64_t
845sort__callchain_branch_predicted_cmp(struct hist_entry *left __maybe_unused,
846 struct hist_entry *right __maybe_unused)
847{
848 return 0;
849}
850
851static int hist_entry__callchain_branch_predicted_snprintf(
852 struct hist_entry *he, char *bf, size_t size, unsigned int width)
853{
854 u64 branch_count, predicted_count;
855 double percent = 0.0;
856 char str[32];
857
858 callchain_branch_counts(he->callchain, &branch_count,
859 &predicted_count, NULL, NULL);
860
861 if (branch_count)
862 percent = predicted_count * 100.0 / branch_count;
863
864 snprintf(str, sizeof(str), "%.1f%%", percent);
865 return repsep_snprintf(bf, size, "%-*.*s", width, width, str);
866}
867
868static struct sort_entry sort_callchain_branch_predicted = {
869 .se_header = "Predicted",
870 .se_cmp = sort__callchain_branch_predicted_cmp,
871 .se_snprintf = hist_entry__callchain_branch_predicted_snprintf,
872 .se_width_idx = HISTC_CALLCHAIN_BRANCH_PREDICTED,
873};
874
875/* --sort callchain_branch_abort */
876
877static int64_t
878sort__callchain_branch_abort_cmp(struct hist_entry *left __maybe_unused,
879 struct hist_entry *right __maybe_unused)
880{
881 return 0;
882}
883
884static int hist_entry__callchain_branch_abort_snprintf(struct hist_entry *he,
885 char *bf, size_t size,
886 unsigned int width)
887{
888 u64 branch_count, abort_count;
889 char str[32];
890
891 callchain_branch_counts(he->callchain, &branch_count,
892 NULL, &abort_count, NULL);
893
894 snprintf(str, sizeof(str), "%" PRId64, abort_count);
895 return repsep_snprintf(bf, size, "%-*.*s", width, width, str);
896}
897
898static struct sort_entry sort_callchain_branch_abort = {
899 .se_header = "Abort",
900 .se_cmp = sort__callchain_branch_abort_cmp,
901 .se_snprintf = hist_entry__callchain_branch_abort_snprintf,
902 .se_width_idx = HISTC_CALLCHAIN_BRANCH_ABORT,
903};
904
905/* --sort callchain_branch_cycles */
906
907static int64_t
908sort__callchain_branch_cycles_cmp(struct hist_entry *left __maybe_unused,
909 struct hist_entry *right __maybe_unused)
910{
911 return 0;
912}
913
914static int hist_entry__callchain_branch_cycles_snprintf(struct hist_entry *he,
915 char *bf, size_t size,
916 unsigned int width)
917{
918 u64 branch_count, cycles_count, cycles = 0;
919 char str[32];
920
921 callchain_branch_counts(he->callchain, &branch_count,
922 NULL, NULL, &cycles_count);
923
924 if (branch_count)
925 cycles = cycles_count / branch_count;
926
927 snprintf(str, sizeof(str), "%" PRId64 "", cycles);
928 return repsep_snprintf(bf, size, "%-*.*s", width, width, str);
929}
930
931static struct sort_entry sort_callchain_branch_cycles = {
932 .se_header = "Cycles",
933 .se_cmp = sort__callchain_branch_cycles_cmp,
934 .se_snprintf = hist_entry__callchain_branch_cycles_snprintf,
935 .se_width_idx = HISTC_CALLCHAIN_BRANCH_CYCLES,
936};
937
938/* --sort srcfile */
939
940static char no_srcfile[1];
941
942static char *hist_entry__get_srcfile(struct hist_entry *e)
943{
944 char *sf, *p;
945 struct map *map = e->ms.map;
946
947 if (!map)
948 return no_srcfile;
949
950 sf = __get_srcline(map__dso(map), map__rip_2objdump(map, e->ip),
951 e->ms.sym, false, true, true, e->ip);
952 if (sf == SRCLINE_UNKNOWN)
953 return no_srcfile;
954 p = strchr(sf, ':');
955 if (p && *sf) {
956 *p = 0;
957 return sf;
958 }
959 free(sf);
960 return no_srcfile;
961}
962
963static int64_t
964sort__srcfile_cmp(struct hist_entry *left, struct hist_entry *right)
965{
966 return sort__srcline_cmp(left, right);
967}
968
969static int64_t
970sort__srcfile_collapse(struct hist_entry *left, struct hist_entry *right)
971{
972 if (!left->srcfile)
973 left->srcfile = hist_entry__get_srcfile(left);
974 if (!right->srcfile)
975 right->srcfile = hist_entry__get_srcfile(right);
976
977 return strcmp(right->srcfile, left->srcfile);
978}
979
980static int64_t
981sort__srcfile_sort(struct hist_entry *left, struct hist_entry *right)
982{
983 return sort__srcfile_collapse(left, right);
984}
985
986static void sort__srcfile_init(struct hist_entry *he)
987{
988 if (!he->srcfile)
989 he->srcfile = hist_entry__get_srcfile(he);
990}
991
992static int hist_entry__srcfile_snprintf(struct hist_entry *he, char *bf,
993 size_t size, unsigned int width)
994{
995 return repsep_snprintf(bf, size, "%-.*s", width, he->srcfile);
996}
997
998static struct sort_entry sort_srcfile = {
999 .se_header = "Source File",
1000 .se_cmp = sort__srcfile_cmp,
1001 .se_collapse = sort__srcfile_collapse,
1002 .se_sort = sort__srcfile_sort,
1003 .se_init = sort__srcfile_init,
1004 .se_snprintf = hist_entry__srcfile_snprintf,
1005 .se_width_idx = HISTC_SRCFILE,
1006};
1007
1008/* --sort parent */
1009
1010static int64_t
1011sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
1012{
1013 struct symbol *sym_l = left->parent;
1014 struct symbol *sym_r = right->parent;
1015
1016 if (!sym_l || !sym_r)
1017 return cmp_null(sym_l, sym_r);
1018
1019 return strcmp(sym_r->name, sym_l->name);
1020}
1021
1022static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
1023 size_t size, unsigned int width)
1024{
1025 return repsep_snprintf(bf, size, "%-*.*s", width, width,
1026 he->parent ? he->parent->name : "[other]");
1027}
1028
1029struct sort_entry sort_parent = {
1030 .se_header = "Parent symbol",
1031 .se_cmp = sort__parent_cmp,
1032 .se_snprintf = hist_entry__parent_snprintf,
1033 .se_width_idx = HISTC_PARENT,
1034};
1035
1036/* --sort cpu */
1037
1038static int64_t
1039sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
1040{
1041 return right->cpu - left->cpu;
1042}
1043
1044static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf,
1045 size_t size, unsigned int width)
1046{
1047 return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu);
1048}
1049
1050static struct sort_entry sort_cpu = {
1051 .se_header = "CPU",
1052 .se_cmp = sort__cpu_cmp,
1053 .se_snprintf = hist_entry__cpu_snprintf,
1054 .se_width_idx = HISTC_CPU,
1055};
1056
1057/* --sort parallelism */
1058
1059static int64_t
1060sort__parallelism_cmp(struct hist_entry *left, struct hist_entry *right)
1061{
1062 return right->parallelism - left->parallelism;
1063}
1064
1065static int hist_entry__parallelism_filter(struct hist_entry *he, int type, const void *arg)
1066{
1067 const unsigned long *parallelism_filter = arg;
1068
1069 if (type != HIST_FILTER__PARALLELISM)
1070 return -1;
1071
1072 return test_bit(he->parallelism, parallelism_filter);
1073}
1074
1075static int hist_entry__parallelism_snprintf(struct hist_entry *he, char *bf,
1076 size_t size, unsigned int width)
1077{
1078 return repsep_snprintf(bf, size, "%*d", width, he->parallelism);
1079}
1080
1081static struct sort_entry sort_parallelism = {
1082 .se_header = "Parallelism",
1083 .se_cmp = sort__parallelism_cmp,
1084 .se_filter = hist_entry__parallelism_filter,
1085 .se_snprintf = hist_entry__parallelism_snprintf,
1086 .se_width_idx = HISTC_PARALLELISM,
1087};
1088
1089/* --sort cgroup_id */
1090
1091static int64_t _sort__cgroup_dev_cmp(u64 left_dev, u64 right_dev)
1092{
1093 return (int64_t)(right_dev - left_dev);
1094}
1095
1096static int64_t _sort__cgroup_inode_cmp(u64 left_ino, u64 right_ino)
1097{
1098 return (int64_t)(right_ino - left_ino);
1099}
1100
1101static int64_t
1102sort__cgroup_id_cmp(struct hist_entry *left, struct hist_entry *right)
1103{
1104 int64_t ret;
1105
1106 ret = _sort__cgroup_dev_cmp(right->cgroup_id.dev, left->cgroup_id.dev);
1107 if (ret != 0)
1108 return ret;
1109
1110 return _sort__cgroup_inode_cmp(right->cgroup_id.ino,
1111 left->cgroup_id.ino);
1112}
1113
1114static int hist_entry__cgroup_id_snprintf(struct hist_entry *he,
1115 char *bf, size_t size,
1116 unsigned int width __maybe_unused)
1117{
1118 return repsep_snprintf(bf, size, "%lu/0x%lx", he->cgroup_id.dev,
1119 he->cgroup_id.ino);
1120}
1121
1122static struct sort_entry sort_cgroup_id = {
1123 .se_header = "cgroup id (dev/inode)",
1124 .se_cmp = sort__cgroup_id_cmp,
1125 .se_snprintf = hist_entry__cgroup_id_snprintf,
1126 .se_width_idx = HISTC_CGROUP_ID,
1127};
1128
1129/* --sort cgroup */
1130
1131static int64_t
1132sort__cgroup_cmp(struct hist_entry *left, struct hist_entry *right)
1133{
1134 return right->cgroup - left->cgroup;
1135}
1136
1137static int hist_entry__cgroup_snprintf(struct hist_entry *he,
1138 char *bf, size_t size,
1139 unsigned int width __maybe_unused)
1140{
1141 const char *cgrp_name = "N/A";
1142
1143 if (he->cgroup) {
1144 struct cgroup *cgrp = cgroup__find(maps__machine(thread__maps(he->ms.thread))->env,
1145 he->cgroup);
1146 if (cgrp != NULL)
1147 cgrp_name = cgrp->name;
1148 else
1149 cgrp_name = "unknown";
1150 }
1151
1152 return repsep_snprintf(bf, size, "%s", cgrp_name);
1153}
1154
1155static struct sort_entry sort_cgroup = {
1156 .se_header = "Cgroup",
1157 .se_cmp = sort__cgroup_cmp,
1158 .se_snprintf = hist_entry__cgroup_snprintf,
1159 .se_width_idx = HISTC_CGROUP,
1160};
1161
1162/* --sort socket */
1163
1164static int64_t
1165sort__socket_cmp(struct hist_entry *left, struct hist_entry *right)
1166{
1167 return right->socket - left->socket;
1168}
1169
1170static int hist_entry__socket_snprintf(struct hist_entry *he, char *bf,
1171 size_t size, unsigned int width)
1172{
1173 return repsep_snprintf(bf, size, "%*.*d", width, width-3, he->socket);
1174}
1175
1176static int hist_entry__socket_filter(struct hist_entry *he, int type, const void *arg)
1177{
1178 int sk = *(const int *)arg;
1179
1180 if (type != HIST_FILTER__SOCKET)
1181 return -1;
1182
1183 return sk >= 0 && he->socket != sk;
1184}
1185
1186static struct sort_entry sort_socket = {
1187 .se_header = "Socket",
1188 .se_cmp = sort__socket_cmp,
1189 .se_snprintf = hist_entry__socket_snprintf,
1190 .se_filter = hist_entry__socket_filter,
1191 .se_width_idx = HISTC_SOCKET,
1192};
1193
1194/* --sort time */
1195
1196static int64_t
1197sort__time_cmp(struct hist_entry *left, struct hist_entry *right)
1198{
1199 return right->time - left->time;
1200}
1201
1202static int hist_entry__time_snprintf(struct hist_entry *he, char *bf,
1203 size_t size, unsigned int width)
1204{
1205 char he_time[32];
1206
1207 if (symbol_conf.nanosecs)
1208 timestamp__scnprintf_nsec(he->time, he_time,
1209 sizeof(he_time));
1210 else
1211 timestamp__scnprintf_usec(he->time, he_time,
1212 sizeof(he_time));
1213
1214 return repsep_snprintf(bf, size, "%-.*s", width, he_time);
1215}
1216
1217static struct sort_entry sort_time = {
1218 .se_header = "Time",
1219 .se_cmp = sort__time_cmp,
1220 .se_snprintf = hist_entry__time_snprintf,
1221 .se_width_idx = HISTC_TIME,
1222};
1223
1224/* --sort trace */
1225
1226#ifdef HAVE_LIBTRACEEVENT
1227static char *get_trace_output(struct hist_entry *he)
1228{
1229 struct trace_seq seq;
1230 struct evsel *evsel;
1231 struct tep_record rec = {
1232 .data = he->raw_data,
1233 .size = he->raw_size,
1234 };
1235 struct tep_event *tp_format;
1236
1237 evsel = hists_to_evsel(he->hists);
1238
1239 trace_seq_init(&seq);
1240 tp_format = evsel__tp_format(evsel);
1241 if (tp_format) {
1242 if (symbol_conf.raw_trace)
1243 tep_print_fields(&seq, he->raw_data, he->raw_size, tp_format);
1244 else
1245 tep_print_event(tp_format->tep, &seq, &rec, "%s", TEP_PRINT_INFO);
1246 }
1247
1248 /*
1249 * Trim the buffer, it starts at 4KB and we're not going to
1250 * add anything more to this buffer.
1251 */
1252 return realloc(seq.buffer, seq.len + 1);
1253}
1254
1255static int64_t
1256sort__trace_cmp(struct hist_entry *left, struct hist_entry *right)
1257{
1258 struct evsel *evsel;
1259
1260 evsel = hists_to_evsel(left->hists);
1261 if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
1262 return 0;
1263
1264 if (left->trace_output == NULL)
1265 left->trace_output = get_trace_output(left);
1266 if (right->trace_output == NULL)
1267 right->trace_output = get_trace_output(right);
1268
1269 return strcmp(right->trace_output, left->trace_output);
1270}
1271
1272static int hist_entry__trace_snprintf(struct hist_entry *he, char *bf,
1273 size_t size, unsigned int width)
1274{
1275 struct evsel *evsel;
1276
1277 evsel = hists_to_evsel(he->hists);
1278 if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
1279 return scnprintf(bf, size, "%-.*s", width, "N/A");
1280
1281 if (he->trace_output == NULL)
1282 he->trace_output = get_trace_output(he);
1283 return repsep_snprintf(bf, size, "%-.*s", width, he->trace_output);
1284}
1285
1286static struct sort_entry sort_trace = {
1287 .se_header = "Trace output",
1288 .se_cmp = sort__trace_cmp,
1289 .se_snprintf = hist_entry__trace_snprintf,
1290 .se_width_idx = HISTC_TRACE,
1291};
1292#endif /* HAVE_LIBTRACEEVENT */
1293
1294/* sort keys for branch stacks */
1295
1296static int64_t
1297sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
1298{
1299 if (!left->branch_info || !right->branch_info)
1300 return cmp_null(left->branch_info, right->branch_info);
1301
1302 return _sort__dso_cmp(left->branch_info->from.ms.map,
1303 right->branch_info->from.ms.map);
1304}
1305
1306static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,
1307 size_t size, unsigned int width)
1308{
1309 if (he->branch_info)
1310 return _hist_entry__dso_snprintf(he->branch_info->from.ms.map,
1311 bf, size, width);
1312 else
1313 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
1314}
1315
1316static int hist_entry__dso_from_filter(struct hist_entry *he, int type,
1317 const void *arg)
1318{
1319 const struct dso *dso = arg;
1320
1321 if (type != HIST_FILTER__DSO)
1322 return -1;
1323
1324 return dso && (!he->branch_info || !he->branch_info->from.ms.map ||
1325 map__dso(he->branch_info->from.ms.map) != dso);
1326}
1327
1328static int64_t
1329sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
1330{
1331 if (!left->branch_info || !right->branch_info)
1332 return cmp_null(left->branch_info, right->branch_info);
1333
1334 return _sort__dso_cmp(left->branch_info->to.ms.map,
1335 right->branch_info->to.ms.map);
1336}
1337
1338static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,
1339 size_t size, unsigned int width)
1340{
1341 if (he->branch_info)
1342 return _hist_entry__dso_snprintf(he->branch_info->to.ms.map,
1343 bf, size, width);
1344 else
1345 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
1346}
1347
1348static int hist_entry__dso_to_filter(struct hist_entry *he, int type,
1349 const void *arg)
1350{
1351 const struct dso *dso = arg;
1352
1353 if (type != HIST_FILTER__DSO)
1354 return -1;
1355
1356 return dso && (!he->branch_info || !he->branch_info->to.ms.map ||
1357 map__dso(he->branch_info->to.ms.map) != dso);
1358}
1359
1360static int64_t
1361sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
1362{
1363 struct addr_map_symbol *from_l, *from_r;
1364
1365 if (!left->branch_info || !right->branch_info)
1366 return cmp_null(left->branch_info, right->branch_info);
1367
1368 from_l = &left->branch_info->from;
1369 from_r = &right->branch_info->from;
1370
1371 if (!from_l->ms.sym && !from_r->ms.sym)
1372 return _sort__addr_cmp(from_l->addr, from_r->addr);
1373
1374 return _sort__sym_cmp(from_l->ms.sym, from_r->ms.sym);
1375}
1376
1377static int64_t
1378sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
1379{
1380 struct addr_map_symbol *to_l, *to_r;
1381
1382 if (!left->branch_info || !right->branch_info)
1383 return cmp_null(left->branch_info, right->branch_info);
1384
1385 to_l = &left->branch_info->to;
1386 to_r = &right->branch_info->to;
1387
1388 if (!to_l->ms.sym && !to_r->ms.sym)
1389 return _sort__addr_cmp(to_l->addr, to_r->addr);
1390
1391 return _sort__sym_cmp(to_l->ms.sym, to_r->ms.sym);
1392}
1393
1394static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,
1395 size_t size, unsigned int width)
1396{
1397 if (he->branch_info) {
1398 struct addr_map_symbol *from = &he->branch_info->from;
1399
1400 return _hist_entry__sym_snprintf(&from->ms, from->al_addr,
1401 from->al_level, bf, size, width);
1402 }
1403
1404 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
1405}
1406
1407static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,
1408 size_t size, unsigned int width)
1409{
1410 if (he->branch_info) {
1411 struct addr_map_symbol *to = &he->branch_info->to;
1412
1413 return _hist_entry__sym_snprintf(&to->ms, to->al_addr,
1414 to->al_level, bf, size, width);
1415 }
1416
1417 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
1418}
1419
1420static int hist_entry__sym_from_filter(struct hist_entry *he, int type,
1421 const void *arg)
1422{
1423 const char *sym = arg;
1424
1425 if (type != HIST_FILTER__SYMBOL)
1426 return -1;
1427
1428 return sym && !(he->branch_info && he->branch_info->from.ms.sym &&
1429 strstr(he->branch_info->from.ms.sym->name, sym));
1430}
1431
1432static int hist_entry__sym_to_filter(struct hist_entry *he, int type,
1433 const void *arg)
1434{
1435 const char *sym = arg;
1436
1437 if (type != HIST_FILTER__SYMBOL)
1438 return -1;
1439
1440 return sym && !(he->branch_info && he->branch_info->to.ms.sym &&
1441 strstr(he->branch_info->to.ms.sym->name, sym));
1442}
1443
1444struct sort_entry sort_dso_from = {
1445 .se_header = "Source Shared Object",
1446 .se_cmp = sort__dso_from_cmp,
1447 .se_snprintf = hist_entry__dso_from_snprintf,
1448 .se_filter = hist_entry__dso_from_filter,
1449 .se_width_idx = HISTC_DSO_FROM,
1450};
1451
1452struct sort_entry sort_dso_to = {
1453 .se_header = "Target Shared Object",
1454 .se_cmp = sort__dso_to_cmp,
1455 .se_snprintf = hist_entry__dso_to_snprintf,
1456 .se_filter = hist_entry__dso_to_filter,
1457 .se_width_idx = HISTC_DSO_TO,
1458};
1459
1460struct sort_entry sort_sym_from = {
1461 .se_header = "Source Symbol",
1462 .se_cmp = sort__sym_from_cmp,
1463 .se_snprintf = hist_entry__sym_from_snprintf,
1464 .se_filter = hist_entry__sym_from_filter,
1465 .se_width_idx = HISTC_SYMBOL_FROM,
1466};
1467
1468struct sort_entry sort_sym_to = {
1469 .se_header = "Target Symbol",
1470 .se_cmp = sort__sym_to_cmp,
1471 .se_snprintf = hist_entry__sym_to_snprintf,
1472 .se_filter = hist_entry__sym_to_filter,
1473 .se_width_idx = HISTC_SYMBOL_TO,
1474};
1475
1476static int _hist_entry__addr_snprintf(struct map_symbol *ms,
1477 u64 ip, char level, char *bf, size_t size,
1478 unsigned int width)
1479{
1480 struct symbol *sym = ms->sym;
1481 struct map *map = ms->map;
1482 size_t ret = 0, offs;
1483
1484 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
1485 if (sym && map) {
1486 if (symbol__type(sym) == STT_OBJECT) {
1487 ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
1488 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
1489 ip - map__unmap_ip(map, sym->start));
1490 } else {
1491 ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
1492 width - ret,
1493 sym->name);
1494 offs = ip - sym->start;
1495 if (offs)
1496 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", offs);
1497 }
1498 } else {
1499 size_t len = BITS_PER_LONG / 4;
1500 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
1501 len, ip);
1502 }
1503
1504 return ret;
1505}
1506
1507static int hist_entry__addr_from_snprintf(struct hist_entry *he, char *bf,
1508 size_t size, unsigned int width)
1509{
1510 if (he->branch_info) {
1511 struct addr_map_symbol *from = &he->branch_info->from;
1512
1513 return _hist_entry__addr_snprintf(&from->ms, from->al_addr,
1514 he->level, bf, size, width);
1515 }
1516
1517 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
1518}
1519
1520static int hist_entry__addr_to_snprintf(struct hist_entry *he, char *bf,
1521 size_t size, unsigned int width)
1522{
1523 if (he->branch_info) {
1524 struct addr_map_symbol *to = &he->branch_info->to;
1525
1526 return _hist_entry__addr_snprintf(&to->ms, to->al_addr,
1527 he->level, bf, size, width);
1528 }
1529
1530 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
1531}
1532
1533static int64_t
1534sort__addr_from_cmp(struct hist_entry *left, struct hist_entry *right)
1535{
1536 struct addr_map_symbol *from_l;
1537 struct addr_map_symbol *from_r;
1538 int64_t ret;
1539
1540 if (!left->branch_info || !right->branch_info)
1541 return cmp_null(left->branch_info, right->branch_info);
1542
1543 from_l = &left->branch_info->from;
1544 from_r = &right->branch_info->from;
1545
1546 /*
1547 * comparing symbol address alone is not enough since it's a
1548 * relative address within a dso.
1549 */
1550 ret = _sort__dso_cmp(from_l->ms.map, from_r->ms.map);
1551 if (ret != 0)
1552 return ret;
1553
1554 return _sort__addr_cmp(from_l->addr, from_r->addr);
1555}
1556
1557static int64_t
1558sort__addr_to_cmp(struct hist_entry *left, struct hist_entry *right)
1559{
1560 struct addr_map_symbol *to_l;
1561 struct addr_map_symbol *to_r;
1562 int64_t ret;
1563
1564 if (!left->branch_info || !right->branch_info)
1565 return cmp_null(left->branch_info, right->branch_info);
1566
1567 to_l = &left->branch_info->to;
1568 to_r = &right->branch_info->to;
1569
1570 /*
1571 * comparing symbol address alone is not enough since it's a
1572 * relative address within a dso.
1573 */
1574 ret = _sort__dso_cmp(to_l->ms.map, to_r->ms.map);
1575 if (ret != 0)
1576 return ret;
1577
1578 return _sort__addr_cmp(to_l->addr, to_r->addr);
1579}
1580
1581static struct sort_entry sort_addr_from = {
1582 .se_header = "Source Address",
1583 .se_cmp = sort__addr_from_cmp,
1584 .se_snprintf = hist_entry__addr_from_snprintf,
1585 .se_filter = hist_entry__sym_from_filter, /* shared with sym_from */
1586 .se_width_idx = HISTC_ADDR_FROM,
1587};
1588
1589static struct sort_entry sort_addr_to = {
1590 .se_header = "Target Address",
1591 .se_cmp = sort__addr_to_cmp,
1592 .se_snprintf = hist_entry__addr_to_snprintf,
1593 .se_filter = hist_entry__sym_to_filter, /* shared with sym_to */
1594 .se_width_idx = HISTC_ADDR_TO,
1595};
1596
1597
1598static int64_t
1599sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
1600{
1601 unsigned char mp, p;
1602
1603 if (!left->branch_info || !right->branch_info)
1604 return cmp_null(left->branch_info, right->branch_info);
1605
1606 mp = left->branch_info->flags.mispred != right->branch_info->flags.mispred;
1607 p = left->branch_info->flags.predicted != right->branch_info->flags.predicted;
1608 return mp || p;
1609}
1610
1611static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,
1612 size_t size, unsigned int width){
1613 static const char *out = "N/A";
1614
1615 if (he->branch_info) {
1616 if (he->branch_info->flags.predicted)
1617 out = "N";
1618 else if (he->branch_info->flags.mispred)
1619 out = "Y";
1620 }
1621
1622 return repsep_snprintf(bf, size, "%-*.*s", width, width, out);
1623}
1624
1625static int64_t
1626sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right)
1627{
1628 if (!left->branch_info || !right->branch_info)
1629 return cmp_null(left->branch_info, right->branch_info);
1630
1631 return left->branch_info->flags.cycles -
1632 right->branch_info->flags.cycles;
1633}
1634
1635static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf,
1636 size_t size, unsigned int width)
1637{
1638 if (!he->branch_info)
1639 return scnprintf(bf, size, "%-.*s", width, "N/A");
1640 if (he->branch_info->flags.cycles == 0)
1641 return repsep_snprintf(bf, size, "%-*s", width, "-");
1642 return repsep_snprintf(bf, size, "%-*hd", width,
1643 he->branch_info->flags.cycles);
1644}
1645
1646static struct sort_entry sort_cycles = {
1647 .se_header = "Basic Block Cycles",
1648 .se_cmp = sort__cycles_cmp,
1649 .se_snprintf = hist_entry__cycles_snprintf,
1650 .se_width_idx = HISTC_CYCLES,
1651};
1652
1653/* --sort daddr_sym */
1654int64_t
1655sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
1656{
1657 uint64_t l = 0, r = 0;
1658
1659 if (left->mem_info)
1660 l = mem_info__daddr(left->mem_info)->addr;
1661 if (right->mem_info)
1662 r = mem_info__daddr(right->mem_info)->addr;
1663
1664 return (int64_t)(r - l);
1665}
1666
1667static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,
1668 size_t size, unsigned int width)
1669{
1670 uint64_t addr = 0;
1671 struct map_symbol *ms = NULL;
1672
1673 if (he->mem_info) {
1674 addr = mem_info__daddr(he->mem_info)->addr;
1675 ms = &mem_info__daddr(he->mem_info)->ms;
1676 }
1677 return _hist_entry__sym_snprintf(ms, addr, he->level, bf, size, width);
1678}
1679
1680int64_t
1681sort__iaddr_cmp(struct hist_entry *left, struct hist_entry *right)
1682{
1683 uint64_t l = 0, r = 0;
1684
1685 if (left->mem_info)
1686 l = mem_info__iaddr(left->mem_info)->addr;
1687 if (right->mem_info)
1688 r = mem_info__iaddr(right->mem_info)->addr;
1689
1690 return (int64_t)(r - l);
1691}
1692
1693static int hist_entry__iaddr_snprintf(struct hist_entry *he, char *bf,
1694 size_t size, unsigned int width)
1695{
1696 uint64_t addr = 0;
1697 struct map_symbol *ms = NULL;
1698
1699 if (he->mem_info) {
1700 addr = mem_info__iaddr(he->mem_info)->addr;
1701 ms = &mem_info__iaddr(he->mem_info)->ms;
1702 }
1703 return _hist_entry__sym_snprintf(ms, addr, he->level, bf, size, width);
1704}
1705
1706static int64_t
1707sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
1708{
1709 struct map *map_l = NULL;
1710 struct map *map_r = NULL;
1711
1712 if (left->mem_info)
1713 map_l = mem_info__daddr(left->mem_info)->ms.map;
1714 if (right->mem_info)
1715 map_r = mem_info__daddr(right->mem_info)->ms.map;
1716
1717 return _sort__dso_cmp(map_l, map_r);
1718}
1719
1720static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,
1721 size_t size, unsigned int width)
1722{
1723 struct map *map = NULL;
1724
1725 if (he->mem_info)
1726 map = mem_info__daddr(he->mem_info)->ms.map;
1727
1728 return _hist_entry__dso_snprintf(map, bf, size, width);
1729}
1730
1731static int64_t
1732sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
1733{
1734 union perf_mem_data_src data_src_l;
1735 union perf_mem_data_src data_src_r;
1736
1737 if (left->mem_info)
1738 data_src_l = *mem_info__data_src(left->mem_info);
1739 else
1740 data_src_l.mem_lock = PERF_MEM_LOCK_NA;
1741
1742 if (right->mem_info)
1743 data_src_r = *mem_info__data_src(right->mem_info);
1744 else
1745 data_src_r.mem_lock = PERF_MEM_LOCK_NA;
1746
1747 return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
1748}
1749
1750static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,
1751 size_t size, unsigned int width)
1752{
1753 char out[10];
1754
1755 perf_mem__lck_scnprintf(out, sizeof(out), he->mem_info);
1756 return repsep_snprintf(bf, size, "%.*s", width, out);
1757}
1758
1759static int64_t
1760sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
1761{
1762 union perf_mem_data_src data_src_l;
1763 union perf_mem_data_src data_src_r;
1764
1765 if (left->mem_info)
1766 data_src_l = *mem_info__data_src(left->mem_info);
1767 else
1768 data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
1769
1770 if (right->mem_info)
1771 data_src_r = *mem_info__data_src(right->mem_info);
1772 else
1773 data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
1774
1775 return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
1776}
1777
1778static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,
1779 size_t size, unsigned int width)
1780{
1781 char out[64];
1782
1783 perf_mem__tlb_scnprintf(out, sizeof(out), he->mem_info);
1784 return repsep_snprintf(bf, size, "%-*s", width, out);
1785}
1786
1787static int64_t
1788sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
1789{
1790 union perf_mem_data_src data_src_l;
1791 union perf_mem_data_src data_src_r;
1792
1793 if (left->mem_info)
1794 data_src_l = *mem_info__data_src(left->mem_info);
1795 else
1796 data_src_l.mem_lvl = PERF_MEM_LVL_NA;
1797
1798 if (right->mem_info)
1799 data_src_r = *mem_info__data_src(right->mem_info);
1800 else
1801 data_src_r.mem_lvl = PERF_MEM_LVL_NA;
1802
1803 return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
1804}
1805
1806static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,
1807 size_t size, unsigned int width)
1808{
1809 char out[64];
1810
1811 perf_mem__lvl_scnprintf(out, sizeof(out), he->mem_info);
1812 return repsep_snprintf(bf, size, "%-*s", width, out);
1813}
1814
1815static int64_t
1816sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
1817{
1818 union perf_mem_data_src data_src_l;
1819 union perf_mem_data_src data_src_r;
1820
1821 if (left->mem_info)
1822 data_src_l = *mem_info__data_src(left->mem_info);
1823 else
1824 data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
1825
1826 if (right->mem_info)
1827 data_src_r = *mem_info__data_src(right->mem_info);
1828 else
1829 data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
1830
1831 return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
1832}
1833
1834static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
1835 size_t size, unsigned int width)
1836{
1837 char out[64];
1838
1839 perf_mem__snp_scnprintf(out, sizeof(out), he->mem_info);
1840 return repsep_snprintf(bf, size, "%-*s", width, out);
1841}
1842
1843int64_t
1844sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
1845{
1846 u64 l, r;
1847 struct map *l_map, *r_map;
1848 struct dso *l_dso, *r_dso;
1849 int rc;
1850
1851 if (!left->mem_info) return -1;
1852 if (!right->mem_info) return 1;
1853
1854 /* group event types together */
1855 if (left->cpumode > right->cpumode) return -1;
1856 if (left->cpumode < right->cpumode) return 1;
1857
1858 l_map = mem_info__daddr(left->mem_info)->ms.map;
1859 r_map = mem_info__daddr(right->mem_info)->ms.map;
1860
1861 /* if both are NULL, jump to sort on al_addr instead */
1862 if (!l_map && !r_map)
1863 goto addr;
1864
1865 if (!l_map) return -1;
1866 if (!r_map) return 1;
1867
1868 l_dso = map__dso(l_map);
1869 r_dso = map__dso(r_map);
1870 rc = dso__cmp_id(l_dso, r_dso);
1871 if (rc)
1872 return rc;
1873 /*
1874 * Addresses with no major/minor numbers or build ID are assumed to be
1875 * anonymous in userspace. Sort those on pid then address.
1876 *
1877 * The kernel and non-zero major/minor mapped areas are
1878 * assumed to be unity mapped. Sort those on address.
1879 */
1880 if (left->cpumode != PERF_RECORD_MISC_KERNEL && (map__flags(l_map) & MAP_SHARED) == 0) {
1881 const struct dso_id *dso_id = dso__id_const(l_dso);
1882
1883 if (!dso_id->mmap2_valid)
1884 dso_id = dso__id_const(r_dso);
1885
1886 if (!build_id__is_defined(&dso_id->build_id) &&
1887 (!dso_id->mmap2_valid || (dso_id->maj == 0 && dso_id->min == 0))) {
1888 /* userspace anonymous */
1889
1890 if (thread__pid(left->thread) > thread__pid(right->thread))
1891 return -1;
1892 if (thread__pid(left->thread) < thread__pid(right->thread))
1893 return 1;
1894 }
1895 }
1896
1897addr:
1898 /* al_addr does all the right addr - start + offset calculations */
1899 l = cl_address(mem_info__daddr(left->mem_info)->al_addr, chk_double_cl);
1900 r = cl_address(mem_info__daddr(right->mem_info)->al_addr, chk_double_cl);
1901
1902 if (l > r) return -1;
1903 if (l < r) return 1;
1904
1905 return 0;
1906}
1907
1908static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf,
1909 size_t size, unsigned int width)
1910{
1911
1912 uint64_t addr = 0;
1913 struct map_symbol *ms = NULL;
1914 char level = he->level;
1915
1916 if (he->mem_info) {
1917 struct map *map = mem_info__daddr(he->mem_info)->ms.map;
1918 struct dso *dso = map ? map__dso(map) : NULL;
1919 const struct dso_id *dso_id = dso ? dso__id_const(dso) : &dso_id_empty;
1920
1921 addr = cl_address(mem_info__daddr(he->mem_info)->al_addr, chk_double_cl);
1922 ms = &mem_info__daddr(he->mem_info)->ms;
1923
1924 /* print [s] for shared data mmaps */
1925 if ((he->cpumode != PERF_RECORD_MISC_KERNEL) &&
1926 map && !(map__prot(map) & PROT_EXEC) &&
1927 (map__flags(map) & MAP_SHARED) &&
1928 (!dso_id->mmap2_valid || (dso_id->maj == 0 && dso_id->min == 0)))
1929 level = 's';
1930 else if (!map)
1931 level = 'X';
1932 }
1933 return _hist_entry__sym_snprintf(ms, addr, level, bf, size, width);
1934}
1935
1936static struct sort_entry sort_mispredict = {
1937 .se_header = "Branch Mispredicted",
1938 .se_cmp = sort__mispredict_cmp,
1939 .se_snprintf = hist_entry__mispredict_snprintf,
1940 .se_width_idx = HISTC_MISPREDICT,
1941};
1942
1943static int64_t
1944sort__weight_cmp(struct hist_entry *left, struct hist_entry *right)
1945{
1946 return left->weight - right->weight;
1947}
1948
1949static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,
1950 size_t size, unsigned int width)
1951{
1952 return repsep_snprintf(bf, size, "%-*llu", width, he->weight);
1953}
1954
1955static struct sort_entry sort_local_weight = {
1956 .se_header = "Local Weight",
1957 .se_cmp = sort__weight_cmp,
1958 .se_snprintf = hist_entry__local_weight_snprintf,
1959 .se_width_idx = HISTC_LOCAL_WEIGHT,
1960};
1961
1962static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,
1963 size_t size, unsigned int width)
1964{
1965 return repsep_snprintf(bf, size, "%-*llu", width,
1966 he->weight * he->stat.nr_events);
1967}
1968
1969static struct sort_entry sort_global_weight = {
1970 .se_header = "Weight",
1971 .se_cmp = sort__weight_cmp,
1972 .se_snprintf = hist_entry__global_weight_snprintf,
1973 .se_width_idx = HISTC_GLOBAL_WEIGHT,
1974};
1975
1976static int64_t
1977sort__ins_lat_cmp(struct hist_entry *left, struct hist_entry *right)
1978{
1979 return left->ins_lat - right->ins_lat;
1980}
1981
1982static int hist_entry__local_ins_lat_snprintf(struct hist_entry *he, char *bf,
1983 size_t size, unsigned int width)
1984{
1985 return repsep_snprintf(bf, size, "%-*u", width, he->ins_lat);
1986}
1987
1988static struct sort_entry sort_local_ins_lat = {
1989 .se_header = "Local INSTR Latency",
1990 .se_cmp = sort__ins_lat_cmp,
1991 .se_snprintf = hist_entry__local_ins_lat_snprintf,
1992 .se_width_idx = HISTC_LOCAL_INS_LAT,
1993};
1994
1995static int hist_entry__global_ins_lat_snprintf(struct hist_entry *he, char *bf,
1996 size_t size, unsigned int width)
1997{
1998 return repsep_snprintf(bf, size, "%-*u", width,
1999 he->ins_lat * he->stat.nr_events);
2000}
2001
2002static struct sort_entry sort_global_ins_lat = {
2003 .se_header = "INSTR Latency",
2004 .se_cmp = sort__ins_lat_cmp,
2005 .se_snprintf = hist_entry__global_ins_lat_snprintf,
2006 .se_width_idx = HISTC_GLOBAL_INS_LAT,
2007};
2008
2009static int64_t
2010sort__p_stage_cyc_cmp(struct hist_entry *left, struct hist_entry *right)
2011{
2012 return left->weight3 - right->weight3;
2013}
2014
2015static int hist_entry__global_p_stage_cyc_snprintf(struct hist_entry *he, char *bf,
2016 size_t size, unsigned int width)
2017{
2018 return repsep_snprintf(bf, size, "%-*u", width, he->weight3 * he->stat.nr_events);
2019}
2020
2021
2022static int hist_entry__p_stage_cyc_snprintf(struct hist_entry *he, char *bf,
2023 size_t size, unsigned int width)
2024{
2025 return repsep_snprintf(bf, size, "%-*u", width, he->weight3);
2026}
2027
2028static struct sort_entry sort_local_p_stage_cyc = {
2029 .se_header = "Local Pipeline Stage Cycle",
2030 .se_cmp = sort__p_stage_cyc_cmp,
2031 .se_snprintf = hist_entry__p_stage_cyc_snprintf,
2032 .se_width_idx = HISTC_LOCAL_P_STAGE_CYC,
2033};
2034
2035static struct sort_entry sort_global_p_stage_cyc = {
2036 .se_header = "Pipeline Stage Cycle",
2037 .se_cmp = sort__p_stage_cyc_cmp,
2038 .se_snprintf = hist_entry__global_p_stage_cyc_snprintf,
2039 .se_width_idx = HISTC_GLOBAL_P_STAGE_CYC,
2040};
2041
2042static struct sort_entry sort_mem_daddr_sym = {
2043 .se_header = "Data Symbol",
2044 .se_cmp = sort__daddr_cmp,
2045 .se_snprintf = hist_entry__daddr_snprintf,
2046 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
2047};
2048
2049static struct sort_entry sort_mem_iaddr_sym = {
2050 .se_header = "Code Symbol",
2051 .se_cmp = sort__iaddr_cmp,
2052 .se_snprintf = hist_entry__iaddr_snprintf,
2053 .se_width_idx = HISTC_MEM_IADDR_SYMBOL,
2054};
2055
2056static struct sort_entry sort_mem_daddr_dso = {
2057 .se_header = "Data Object",
2058 .se_cmp = sort__dso_daddr_cmp,
2059 .se_snprintf = hist_entry__dso_daddr_snprintf,
2060 .se_width_idx = HISTC_MEM_DADDR_DSO,
2061};
2062
2063static struct sort_entry sort_mem_locked = {
2064 .se_header = "Locked",
2065 .se_cmp = sort__locked_cmp,
2066 .se_snprintf = hist_entry__locked_snprintf,
2067 .se_width_idx = HISTC_MEM_LOCKED,
2068};
2069
2070static struct sort_entry sort_mem_tlb = {
2071 .se_header = "TLB access",
2072 .se_cmp = sort__tlb_cmp,
2073 .se_snprintf = hist_entry__tlb_snprintf,
2074 .se_width_idx = HISTC_MEM_TLB,
2075};
2076
2077static struct sort_entry sort_mem_lvl = {
2078 .se_header = "Memory access",
2079 .se_cmp = sort__lvl_cmp,
2080 .se_snprintf = hist_entry__lvl_snprintf,
2081 .se_width_idx = HISTC_MEM_LVL,
2082};
2083
2084static struct sort_entry sort_mem_snoop = {
2085 .se_header = "Snoop",
2086 .se_cmp = sort__snoop_cmp,
2087 .se_snprintf = hist_entry__snoop_snprintf,
2088 .se_width_idx = HISTC_MEM_SNOOP,
2089};
2090
2091static struct sort_entry sort_mem_dcacheline = {
2092 .se_header = "Data Cacheline",
2093 .se_cmp = sort__dcacheline_cmp,
2094 .se_snprintf = hist_entry__dcacheline_snprintf,
2095 .se_width_idx = HISTC_MEM_DCACHELINE,
2096};
2097
2098static int64_t
2099sort__blocked_cmp(struct hist_entry *left, struct hist_entry *right)
2100{
2101 union perf_mem_data_src data_src_l;
2102 union perf_mem_data_src data_src_r;
2103
2104 if (left->mem_info)
2105 data_src_l = *mem_info__data_src(left->mem_info);
2106 else
2107 data_src_l.mem_blk = PERF_MEM_BLK_NA;
2108
2109 if (right->mem_info)
2110 data_src_r = *mem_info__data_src(right->mem_info);
2111 else
2112 data_src_r.mem_blk = PERF_MEM_BLK_NA;
2113
2114 return (int64_t)(data_src_r.mem_blk - data_src_l.mem_blk);
2115}
2116
2117static int hist_entry__blocked_snprintf(struct hist_entry *he, char *bf,
2118 size_t size, unsigned int width)
2119{
2120 char out[16];
2121
2122 perf_mem__blk_scnprintf(out, sizeof(out), he->mem_info);
2123 return repsep_snprintf(bf, size, "%.*s", width, out);
2124}
2125
2126static struct sort_entry sort_mem_blocked = {
2127 .se_header = "Blocked",
2128 .se_cmp = sort__blocked_cmp,
2129 .se_snprintf = hist_entry__blocked_snprintf,
2130 .se_width_idx = HISTC_MEM_BLOCKED,
2131};
2132
2133static int64_t
2134sort__phys_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
2135{
2136 uint64_t l = 0, r = 0;
2137
2138 if (left->mem_info)
2139 l = mem_info__daddr(left->mem_info)->phys_addr;
2140 if (right->mem_info)
2141 r = mem_info__daddr(right->mem_info)->phys_addr;
2142
2143 return (int64_t)(r - l);
2144}
2145
2146static int hist_entry__phys_daddr_snprintf(struct hist_entry *he, char *bf,
2147 size_t size, unsigned int width)
2148{
2149 uint64_t addr = 0;
2150 size_t ret = 0;
2151 size_t len = BITS_PER_LONG / 4;
2152
2153 addr = mem_info__daddr(he->mem_info)->phys_addr;
2154
2155 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", he->level);
2156
2157 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", len, addr);
2158
2159 ret += repsep_snprintf(bf + ret, size - ret, "%-*s", width - ret, "");
2160
2161 if (ret > width)
2162 bf[width] = '\0';
2163
2164 return width;
2165}
2166
2167static struct sort_entry sort_mem_phys_daddr = {
2168 .se_header = "Data Physical Address",
2169 .se_cmp = sort__phys_daddr_cmp,
2170 .se_snprintf = hist_entry__phys_daddr_snprintf,
2171 .se_width_idx = HISTC_MEM_PHYS_DADDR,
2172};
2173
2174static int64_t
2175sort__data_page_size_cmp(struct hist_entry *left, struct hist_entry *right)
2176{
2177 uint64_t l = 0, r = 0;
2178
2179 if (left->mem_info)
2180 l = mem_info__daddr(left->mem_info)->data_page_size;
2181 if (right->mem_info)
2182 r = mem_info__daddr(right->mem_info)->data_page_size;
2183
2184 return (int64_t)(r - l);
2185}
2186
2187static int hist_entry__data_page_size_snprintf(struct hist_entry *he, char *bf,
2188 size_t size, unsigned int width)
2189{
2190 char str[PAGE_SIZE_NAME_LEN];
2191
2192 return repsep_snprintf(bf, size, "%-*s", width,
2193 get_page_size_name(mem_info__daddr(he->mem_info)->data_page_size, str));
2194}
2195
2196static struct sort_entry sort_mem_data_page_size = {
2197 .se_header = "Data Page Size",
2198 .se_cmp = sort__data_page_size_cmp,
2199 .se_snprintf = hist_entry__data_page_size_snprintf,
2200 .se_width_idx = HISTC_MEM_DATA_PAGE_SIZE,
2201};
2202
2203static int64_t
2204sort__code_page_size_cmp(struct hist_entry *left, struct hist_entry *right)
2205{
2206 uint64_t l = left->code_page_size;
2207 uint64_t r = right->code_page_size;
2208
2209 return (int64_t)(r - l);
2210}
2211
2212static int hist_entry__code_page_size_snprintf(struct hist_entry *he, char *bf,
2213 size_t size, unsigned int width)
2214{
2215 char str[PAGE_SIZE_NAME_LEN];
2216
2217 return repsep_snprintf(bf, size, "%-*s", width,
2218 get_page_size_name(he->code_page_size, str));
2219}
2220
2221static struct sort_entry sort_code_page_size = {
2222 .se_header = "Code Page Size",
2223 .se_cmp = sort__code_page_size_cmp,
2224 .se_snprintf = hist_entry__code_page_size_snprintf,
2225 .se_width_idx = HISTC_CODE_PAGE_SIZE,
2226};
2227
2228static int64_t
2229sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
2230{
2231 if (!left->branch_info || !right->branch_info)
2232 return cmp_null(left->branch_info, right->branch_info);
2233
2234 return left->branch_info->flags.abort !=
2235 right->branch_info->flags.abort;
2236}
2237
2238static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf,
2239 size_t size, unsigned int width)
2240{
2241 static const char *out = "N/A";
2242
2243 if (he->branch_info) {
2244 if (he->branch_info->flags.abort)
2245 out = "A";
2246 else
2247 out = ".";
2248 }
2249
2250 return repsep_snprintf(bf, size, "%-*s", width, out);
2251}
2252
2253static struct sort_entry sort_abort = {
2254 .se_header = "Transaction abort",
2255 .se_cmp = sort__abort_cmp,
2256 .se_snprintf = hist_entry__abort_snprintf,
2257 .se_width_idx = HISTC_ABORT,
2258};
2259
2260static int64_t
2261sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right)
2262{
2263 if (!left->branch_info || !right->branch_info)
2264 return cmp_null(left->branch_info, right->branch_info);
2265
2266 return left->branch_info->flags.in_tx !=
2267 right->branch_info->flags.in_tx;
2268}
2269
2270static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf,
2271 size_t size, unsigned int width)
2272{
2273 static const char *out = "N/A";
2274
2275 if (he->branch_info) {
2276 if (he->branch_info->flags.in_tx)
2277 out = "T";
2278 else
2279 out = ".";
2280 }
2281
2282 return repsep_snprintf(bf, size, "%-*s", width, out);
2283}
2284
2285static struct sort_entry sort_in_tx = {
2286 .se_header = "Branch in transaction",
2287 .se_cmp = sort__in_tx_cmp,
2288 .se_snprintf = hist_entry__in_tx_snprintf,
2289 .se_width_idx = HISTC_IN_TX,
2290};
2291
2292static int64_t
2293sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
2294{
2295 return left->transaction - right->transaction;
2296}
2297
2298static inline char *add_str(char *p, const char *str)
2299{
2300 strcpy(p, str);
2301 return p + strlen(str);
2302}
2303
2304static struct txbit {
2305 unsigned flag;
2306 const char *name;
2307 int skip_for_len;
2308} txbits[] = {
2309 { PERF_TXN_ELISION, "EL ", 0 },
2310 { PERF_TXN_TRANSACTION, "TX ", 1 },
2311 { PERF_TXN_SYNC, "SYNC ", 1 },
2312 { PERF_TXN_ASYNC, "ASYNC ", 0 },
2313 { PERF_TXN_RETRY, "RETRY ", 0 },
2314 { PERF_TXN_CONFLICT, "CON ", 0 },
2315 { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 },
2316 { PERF_TXN_CAPACITY_READ, "CAP-READ ", 0 },
2317 { 0, NULL, 0 }
2318};
2319
2320int hist_entry__transaction_len(void)
2321{
2322 int i;
2323 int len = 0;
2324
2325 for (i = 0; txbits[i].name; i++) {
2326 if (!txbits[i].skip_for_len)
2327 len += strlen(txbits[i].name);
2328 }
2329 len += 4; /* :XX<space> */
2330 return len;
2331}
2332
2333static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf,
2334 size_t size, unsigned int width)
2335{
2336 u64 t = he->transaction;
2337 char buf[128];
2338 char *p = buf;
2339 int i;
2340
2341 buf[0] = 0;
2342 for (i = 0; txbits[i].name; i++)
2343 if (txbits[i].flag & t)
2344 p = add_str(p, txbits[i].name);
2345 if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC)))
2346 p = add_str(p, "NEITHER ");
2347 if (t & PERF_TXN_ABORT_MASK) {
2348 sprintf(p, ":%" PRIx64,
2349 (t & PERF_TXN_ABORT_MASK) >>
2350 PERF_TXN_ABORT_SHIFT);
2351 p += strlen(p);
2352 }
2353
2354 return repsep_snprintf(bf, size, "%-*s", width, buf);
2355}
2356
2357static struct sort_entry sort_transaction = {
2358 .se_header = "Transaction ",
2359 .se_cmp = sort__transaction_cmp,
2360 .se_snprintf = hist_entry__transaction_snprintf,
2361 .se_width_idx = HISTC_TRANSACTION,
2362};
2363
2364/* --sort symbol_size */
2365
2366static int64_t _sort__sym_size_cmp(struct symbol *sym_l, struct symbol *sym_r)
2367{
2368 int64_t size_l = sym_l != NULL ? symbol__size(sym_l) : 0;
2369 int64_t size_r = sym_r != NULL ? symbol__size(sym_r) : 0;
2370
2371 return size_l < size_r ? -1 :
2372 size_l == size_r ? 0 : 1;
2373}
2374
2375static int64_t
2376sort__sym_size_cmp(struct hist_entry *left, struct hist_entry *right)
2377{
2378 return _sort__sym_size_cmp(right->ms.sym, left->ms.sym);
2379}
2380
2381static int _hist_entry__sym_size_snprintf(struct symbol *sym, char *bf,
2382 size_t bf_size, unsigned int width)
2383{
2384 if (sym)
2385 return repsep_snprintf(bf, bf_size, "%*d", width, symbol__size(sym));
2386
2387 return repsep_snprintf(bf, bf_size, "%*s", width, "unknown");
2388}
2389
2390static int hist_entry__sym_size_snprintf(struct hist_entry *he, char *bf,
2391 size_t size, unsigned int width)
2392{
2393 return _hist_entry__sym_size_snprintf(he->ms.sym, bf, size, width);
2394}
2395
2396static struct sort_entry sort_sym_size = {
2397 .se_header = "Symbol size",
2398 .se_cmp = sort__sym_size_cmp,
2399 .se_snprintf = hist_entry__sym_size_snprintf,
2400 .se_width_idx = HISTC_SYM_SIZE,
2401};
2402
2403/* --sort dso_size */
2404
2405static int64_t _sort__dso_size_cmp(struct map *map_l, struct map *map_r)
2406{
2407 int64_t size_l = map_l != NULL ? map__size(map_l) : 0;
2408 int64_t size_r = map_r != NULL ? map__size(map_r) : 0;
2409
2410 return size_l < size_r ? -1 :
2411 size_l == size_r ? 0 : 1;
2412}
2413
2414static int64_t
2415sort__dso_size_cmp(struct hist_entry *left, struct hist_entry *right)
2416{
2417 return _sort__dso_size_cmp(right->ms.map, left->ms.map);
2418}
2419
2420static int _hist_entry__dso_size_snprintf(struct map *map, char *bf,
2421 size_t bf_size, unsigned int width)
2422{
2423 if (map && map__dso(map))
2424 return repsep_snprintf(bf, bf_size, "%*d", width, map__size(map));
2425
2426 return repsep_snprintf(bf, bf_size, "%*s", width, "unknown");
2427}
2428
2429static int hist_entry__dso_size_snprintf(struct hist_entry *he, char *bf,
2430 size_t size, unsigned int width)
2431{
2432 return _hist_entry__dso_size_snprintf(he->ms.map, bf, size, width);
2433}
2434
2435static struct sort_entry sort_dso_size = {
2436 .se_header = "DSO size",
2437 .se_cmp = sort__dso_size_cmp,
2438 .se_snprintf = hist_entry__dso_size_snprintf,
2439 .se_width_idx = HISTC_DSO_SIZE,
2440};
2441
2442/* --sort addr */
2443
2444static int64_t
2445sort__addr_cmp(struct hist_entry *left, struct hist_entry *right)
2446{
2447 u64 left_ip = left->ip;
2448 u64 right_ip = right->ip;
2449 struct map *left_map = left->ms.map;
2450 struct map *right_map = right->ms.map;
2451
2452 if (left_map)
2453 left_ip = map__unmap_ip(left_map, left_ip);
2454 if (right_map)
2455 right_ip = map__unmap_ip(right_map, right_ip);
2456
2457 return _sort__addr_cmp(left_ip, right_ip);
2458}
2459
2460static int hist_entry__addr_snprintf(struct hist_entry *he, char *bf,
2461 size_t size, unsigned int width)
2462{
2463 u64 ip = he->ip;
2464 struct map *map = he->ms.map;
2465
2466 if (map)
2467 ip = map__unmap_ip(map, ip);
2468
2469 return repsep_snprintf(bf, size, "%-#*llx", width, ip);
2470}
2471
2472static struct sort_entry sort_addr = {
2473 .se_header = "Address",
2474 .se_cmp = sort__addr_cmp,
2475 .se_snprintf = hist_entry__addr_snprintf,
2476 .se_width_idx = HISTC_ADDR,
2477};
2478
2479/* --sort type */
2480
2481struct annotated_data_type unknown_type = {
2482 .self = {
2483 .type_name = (char *)"(unknown)",
2484 .children = LIST_HEAD_INIT(unknown_type.self.children),
2485 },
2486};
2487
2488static int64_t
2489sort__type_cmp(struct hist_entry *left, struct hist_entry *right)
2490{
2491 return sort__addr_cmp(left, right);
2492}
2493
2494static void sort__type_init(struct hist_entry *he)
2495{
2496 if (he->mem_type)
2497 return;
2498
2499 he->mem_type = hist_entry__get_data_type(he);
2500 if (he->mem_type == NULL) {
2501 he->mem_type = &unknown_type;
2502 he->mem_type_off = 0;
2503 }
2504}
2505
2506static int64_t
2507sort__type_collapse(struct hist_entry *left, struct hist_entry *right)
2508{
2509 struct annotated_data_type *left_type = left->mem_type;
2510 struct annotated_data_type *right_type = right->mem_type;
2511
2512 if (!left_type) {
2513 sort__type_init(left);
2514 left_type = left->mem_type;
2515 }
2516
2517 if (!right_type) {
2518 sort__type_init(right);
2519 right_type = right->mem_type;
2520 }
2521
2522 return strcmp(left_type->self.type_name, right_type->self.type_name);
2523}
2524
2525static int64_t
2526sort__type_sort(struct hist_entry *left, struct hist_entry *right)
2527{
2528 return sort__type_collapse(left, right);
2529}
2530
2531static int hist_entry__type_snprintf(struct hist_entry *he, char *bf,
2532 size_t size, unsigned int width)
2533{
2534 return repsep_snprintf(bf, size, "%-*s", width, he->mem_type->self.type_name);
2535}
2536
2537struct sort_entry sort_type = {
2538 .se_header = "Data Type",
2539 .se_cmp = sort__type_cmp,
2540 .se_collapse = sort__type_collapse,
2541 .se_sort = sort__type_sort,
2542 .se_init = sort__type_init,
2543 .se_snprintf = hist_entry__type_snprintf,
2544 .se_width_idx = HISTC_TYPE,
2545};
2546
2547/* --sort typeoff */
2548
2549static int64_t
2550sort__typeoff_sort(struct hist_entry *left, struct hist_entry *right)
2551{
2552 struct annotated_data_type *left_type = left->mem_type;
2553 struct annotated_data_type *right_type = right->mem_type;
2554 int64_t ret;
2555
2556 if (!left_type) {
2557 sort__type_init(left);
2558 left_type = left->mem_type;
2559 }
2560
2561 if (!right_type) {
2562 sort__type_init(right);
2563 right_type = right->mem_type;
2564 }
2565
2566 ret = strcmp(left_type->self.type_name, right_type->self.type_name);
2567 if (ret)
2568 return ret;
2569 return left->mem_type_off - right->mem_type_off;
2570}
2571
2572static int hist_entry__typeoff_snprintf(struct hist_entry *he, char *bf,
2573 size_t size, unsigned int width __maybe_unused)
2574{
2575 struct annotated_data_type *he_type = he->mem_type;
2576 char buf[4096];
2577
2578 if (he_type == &unknown_type || he_type == &stackop_type ||
2579 he_type == &canary_type)
2580 return repsep_snprintf(bf, size, "%s", he_type->self.type_name);
2581
2582 if (!annotated_data_type__get_member_name(he_type, buf, sizeof(buf),
2583 he->mem_type_off))
2584 scnprintf(buf, sizeof(buf), "no field");
2585
2586 return repsep_snprintf(bf, size, "%s +%#x (%s)", he_type->self.type_name,
2587 he->mem_type_off, buf);
2588}
2589
2590static struct sort_entry sort_type_offset = {
2591 .se_header = "Data Type Offset",
2592 .se_cmp = sort__type_cmp,
2593 .se_collapse = sort__typeoff_sort,
2594 .se_sort = sort__typeoff_sort,
2595 .se_init = sort__type_init,
2596 .se_snprintf = hist_entry__typeoff_snprintf,
2597 .se_width_idx = HISTC_TYPE_OFFSET,
2598};
2599
2600/* --sort typecln */
2601
2602static int
2603hist_entry__cln_size(struct hist_entry *he)
2604{
2605 int ret = 0;
2606
2607 if (he && he->hists) {
2608 struct evsel *evsel = hists_to_evsel(he->hists);
2609
2610 if (evsel) {
2611 struct perf_session *session = evsel__session(evsel);
2612
2613 ret = session->header.env.cln_size;
2614 }
2615 }
2616
2617 if (ret < 1)
2618 ret = DEFAULT_CACHELINE_SIZE; // avoid div/0 later
2619
2620 return ret;
2621}
2622
2623static int64_t
2624sort__typecln_sort(struct hist_entry *left, struct hist_entry *right)
2625{
2626 struct annotated_data_type *left_type = left->mem_type;
2627 struct annotated_data_type *right_type = right->mem_type;
2628 int64_t left_cln, right_cln;
2629 int64_t cln_size_left = hist_entry__cln_size(left);
2630 int64_t cln_size_right = hist_entry__cln_size(right);
2631 int64_t ret;
2632
2633 if (!left_type) {
2634 sort__type_init(left);
2635 left_type = left->mem_type;
2636 }
2637
2638 if (!right_type) {
2639 sort__type_init(right);
2640 right_type = right->mem_type;
2641 }
2642
2643 ret = strcmp(left_type->self.type_name, right_type->self.type_name);
2644 if (ret)
2645 return ret;
2646
2647 left_cln = left->mem_type_off / cln_size_left;
2648 right_cln = right->mem_type_off / cln_size_right;
2649 return left_cln - right_cln;
2650}
2651
2652static int hist_entry__typecln_snprintf(struct hist_entry *he, char *bf,
2653 size_t size, unsigned int width __maybe_unused)
2654{
2655 struct annotated_data_type *he_type = he->mem_type;
2656 int cln_size = hist_entry__cln_size(he);
2657
2658 return repsep_snprintf(bf, size, "%s: cache-line %d", he_type->self.type_name,
2659 he->mem_type_off / cln_size);
2660}
2661
2662static struct sort_entry sort_type_cacheline = {
2663 .se_header = "Data Type Cacheline",
2664 .se_cmp = sort__type_cmp,
2665 .se_collapse = sort__typecln_sort,
2666 .se_sort = sort__typecln_sort,
2667 .se_init = sort__type_init,
2668 .se_snprintf = hist_entry__typecln_snprintf,
2669 .se_width_idx = HISTC_TYPE_CACHELINE,
2670};
2671
2672
2673struct sort_dimension {
2674 const char *name;
2675 struct sort_entry *entry;
2676 int taken;
2677};
2678
2679static int arch_support_sort_key(const char *sort_key, struct perf_env *env)
2680{
2681 uint16_t e_machine = perf_env__e_machine(env, /*e_eflags=*/NULL);
2682
2683 if (e_machine == EM_X86_64 || e_machine == EM_386 || e_machine == EM_PPC64 ||
2684 e_machine == EM_PPC) {
2685 if (!strcmp(sort_key, "p_stage_cyc"))
2686 return 1;
2687 if (!strcmp(sort_key, "local_p_stage_cyc"))
2688 return 1;
2689 }
2690 return 0;
2691}
2692
2693static const char *arch_perf_header_entry(const char *se_header, struct perf_env *env)
2694{
2695 uint16_t e_machine = perf_env__e_machine(env, /*e_eflags=*/NULL);
2696
2697 if (e_machine == EM_X86_64 || e_machine == EM_386) {
2698 if (!strcmp(se_header, "Local Pipeline Stage Cycle"))
2699 return "Local Retire Latency";
2700 else if (!strcmp(se_header, "Pipeline Stage Cycle"))
2701 return "Retire Latency";
2702 } else if (e_machine == EM_PPC64 || e_machine == EM_PPC) {
2703 if (!strcmp(se_header, "Local INSTR Latency"))
2704 return "Finish Cyc";
2705 else if (!strcmp(se_header, "INSTR Latency"))
2706 return "Global Finish_cyc";
2707 else if (!strcmp(se_header, "Local Pipeline Stage Cycle"))
2708 return "Dispatch Cyc";
2709 else if (!strcmp(se_header, "Pipeline Stage Cycle"))
2710 return "Global Dispatch_cyc";
2711 }
2712 return se_header;
2713}
2714
2715static void sort_dimension_add_dynamic_header(struct sort_dimension *sd, struct perf_env *env)
2716{
2717 sd->entry->se_header = arch_perf_header_entry(sd->entry->se_header, env);
2718}
2719
2720#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
2721
2722static struct sort_dimension common_sort_dimensions[] = {
2723 DIM(SORT_PID, "pid", sort_thread),
2724 DIM(SORT_TGID, "tgid", sort_tgid),
2725 DIM(SORT_COMM, "comm", sort_comm),
2726 DIM(SORT_COMM_NODIGIT, "comm_nodigit", sort_comm_nodigit),
2727 DIM(SORT_DSO, "dso", sort_dso),
2728 DIM(SORT_SYM, "symbol", sort_sym),
2729 DIM(SORT_PARENT, "parent", sort_parent),
2730 DIM(SORT_CPU, "cpu", sort_cpu),
2731 DIM(SORT_SOCKET, "socket", sort_socket),
2732 DIM(SORT_SRCLINE, "srcline", sort_srcline),
2733 DIM(SORT_SRCFILE, "srcfile", sort_srcfile),
2734 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
2735 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
2736 DIM(SORT_TRANSACTION, "transaction", sort_transaction),
2737#ifdef HAVE_LIBTRACEEVENT
2738 DIM(SORT_TRACE, "trace", sort_trace),
2739#endif
2740 DIM(SORT_SYM_SIZE, "symbol_size", sort_sym_size),
2741 DIM(SORT_DSO_SIZE, "dso_size", sort_dso_size),
2742 DIM(SORT_CGROUP, "cgroup", sort_cgroup),
2743 DIM(SORT_CGROUP_ID, "cgroup_id", sort_cgroup_id),
2744 DIM(SORT_SYM_IPC_NULL, "ipc_null", sort_sym_ipc_null),
2745 DIM(SORT_TIME, "time", sort_time),
2746 DIM(SORT_CODE_PAGE_SIZE, "code_page_size", sort_code_page_size),
2747 DIM(SORT_LOCAL_INS_LAT, "local_ins_lat", sort_local_ins_lat),
2748 DIM(SORT_GLOBAL_INS_LAT, "ins_lat", sort_global_ins_lat),
2749 DIM(SORT_LOCAL_PIPELINE_STAGE_CYC, "local_p_stage_cyc", sort_local_p_stage_cyc),
2750 DIM(SORT_GLOBAL_PIPELINE_STAGE_CYC, "p_stage_cyc", sort_global_p_stage_cyc),
2751 DIM(SORT_ADDR, "addr", sort_addr),
2752 DIM(SORT_LOCAL_RETIRE_LAT, "local_retire_lat", sort_local_p_stage_cyc),
2753 DIM(SORT_GLOBAL_RETIRE_LAT, "retire_lat", sort_global_p_stage_cyc),
2754 DIM(SORT_SIMD, "simd", sort_simd),
2755 DIM(SORT_ANNOTATE_DATA_TYPE, "type", sort_type),
2756 DIM(SORT_ANNOTATE_DATA_TYPE_OFFSET, "typeoff", sort_type_offset),
2757 DIM(SORT_SYM_OFFSET, "symoff", sort_sym_offset),
2758 DIM(SORT_ANNOTATE_DATA_TYPE_CACHELINE, "typecln", sort_type_cacheline),
2759 DIM(SORT_PARALLELISM, "parallelism", sort_parallelism),
2760};
2761
2762#undef DIM
2763
2764#define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
2765
2766static struct sort_dimension bstack_sort_dimensions[] = {
2767 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
2768 DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
2769 DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
2770 DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
2771 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
2772 DIM(SORT_IN_TX, "in_tx", sort_in_tx),
2773 DIM(SORT_ABORT, "abort", sort_abort),
2774 DIM(SORT_CYCLES, "cycles", sort_cycles),
2775 DIM(SORT_SRCLINE_FROM, "srcline_from", sort_srcline_from),
2776 DIM(SORT_SRCLINE_TO, "srcline_to", sort_srcline_to),
2777 DIM(SORT_SYM_IPC, "ipc_lbr", sort_sym_ipc),
2778 DIM(SORT_ADDR_FROM, "addr_from", sort_addr_from),
2779 DIM(SORT_ADDR_TO, "addr_to", sort_addr_to),
2780 DIM(SORT_CALLCHAIN_BRANCH_PREDICTED,
2781 "callchain_branch_predicted",
2782 sort_callchain_branch_predicted),
2783 DIM(SORT_CALLCHAIN_BRANCH_ABORT,
2784 "callchain_branch_abort",
2785 sort_callchain_branch_abort),
2786 DIM(SORT_CALLCHAIN_BRANCH_CYCLES,
2787 "callchain_branch_cycles",
2788 sort_callchain_branch_cycles)
2789};
2790
2791#undef DIM
2792
2793#define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
2794
2795static struct sort_dimension memory_sort_dimensions[] = {
2796 DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
2797 DIM(SORT_MEM_IADDR_SYMBOL, "symbol_iaddr", sort_mem_iaddr_sym),
2798 DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
2799 DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
2800 DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
2801 DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
2802 DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
2803 DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline),
2804 DIM(SORT_MEM_PHYS_DADDR, "phys_daddr", sort_mem_phys_daddr),
2805 DIM(SORT_MEM_DATA_PAGE_SIZE, "data_page_size", sort_mem_data_page_size),
2806 DIM(SORT_MEM_BLOCKED, "blocked", sort_mem_blocked),
2807};
2808
2809#undef DIM
2810
2811struct hpp_dimension {
2812 const char *name;
2813 struct perf_hpp_fmt *fmt;
2814 int taken;
2815 int was_taken;
2816 int mem_mode;
2817};
2818
2819#define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
2820#define DIM_MEM(d, n) { .name = n, .fmt = &perf_hpp__format[d], .mem_mode = 1, }
2821
2822static struct hpp_dimension hpp_sort_dimensions[] = {
2823 DIM(PERF_HPP__OVERHEAD, "overhead"),
2824 DIM(PERF_HPP__LATENCY, "latency"),
2825 DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"),
2826 DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
2827 DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
2828 DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
2829 DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"),
2830 DIM(PERF_HPP__LATENCY_ACC, "latency_children"),
2831 DIM(PERF_HPP__SAMPLES, "sample"),
2832 DIM(PERF_HPP__PERIOD, "period"),
2833 DIM(PERF_HPP__WEIGHT1, "weight1"),
2834 DIM(PERF_HPP__WEIGHT2, "weight2"),
2835 DIM(PERF_HPP__WEIGHT3, "weight3"),
2836 /* aliases for weight_struct */
2837 DIM(PERF_HPP__WEIGHT2, "ins_lat"),
2838 DIM(PERF_HPP__WEIGHT3, "retire_lat"),
2839 DIM(PERF_HPP__WEIGHT3, "p_stage_cyc"),
2840 /* used for output only when SORT_MODE__MEM */
2841 DIM_MEM(PERF_HPP__MEM_STAT_OP, "op"),
2842 DIM_MEM(PERF_HPP__MEM_STAT_CACHE, "cache"),
2843 DIM_MEM(PERF_HPP__MEM_STAT_MEMORY, "memory"),
2844 DIM_MEM(PERF_HPP__MEM_STAT_SNOOP, "snoop"),
2845 DIM_MEM(PERF_HPP__MEM_STAT_DTLB, "dtlb"),
2846};
2847
2848#undef DIM_MEM
2849#undef DIM
2850
2851struct hpp_sort_entry {
2852 struct perf_hpp_fmt hpp;
2853 struct sort_entry *se;
2854};
2855
2856void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists)
2857{
2858 struct hpp_sort_entry *hse;
2859
2860 if (!perf_hpp__is_sort_entry(fmt))
2861 return;
2862
2863 hse = container_of(fmt, struct hpp_sort_entry, hpp);
2864 hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name));
2865}
2866
2867static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
2868 struct hists *hists, int line,
2869 int *span __maybe_unused)
2870{
2871 struct hpp_sort_entry *hse;
2872 size_t len = fmt->user_len;
2873 const char *hdr = "";
2874
2875 if (line == hists->hpp_list->nr_header_lines - 1)
2876 hdr = fmt->name;
2877
2878 hse = container_of(fmt, struct hpp_sort_entry, hpp);
2879
2880 if (!len)
2881 len = hists__col_len(hists, hse->se->se_width_idx);
2882
2883 return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, hdr);
2884}
2885
2886static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
2887 struct perf_hpp *hpp __maybe_unused,
2888 struct hists *hists)
2889{
2890 struct hpp_sort_entry *hse;
2891 size_t len = fmt->user_len;
2892
2893 hse = container_of(fmt, struct hpp_sort_entry, hpp);
2894
2895 if (!len)
2896 len = hists__col_len(hists, hse->se->se_width_idx);
2897
2898 return len;
2899}
2900
2901static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
2902 struct hist_entry *he)
2903{
2904 struct hpp_sort_entry *hse;
2905 size_t len = fmt->user_len;
2906
2907 hse = container_of(fmt, struct hpp_sort_entry, hpp);
2908
2909 if (!len)
2910 len = hists__col_len(he->hists, hse->se->se_width_idx);
2911
2912 return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
2913}
2914
2915static int64_t __sort__hpp_cmp(struct perf_hpp_fmt *fmt,
2916 struct hist_entry *a, struct hist_entry *b)
2917{
2918 struct hpp_sort_entry *hse;
2919
2920 hse = container_of(fmt, struct hpp_sort_entry, hpp);
2921 return hse->se->se_cmp(a, b);
2922}
2923
2924static int64_t __sort__hpp_collapse(struct perf_hpp_fmt *fmt,
2925 struct hist_entry *a, struct hist_entry *b)
2926{
2927 struct hpp_sort_entry *hse;
2928 int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
2929
2930 hse = container_of(fmt, struct hpp_sort_entry, hpp);
2931 collapse_fn = hse->se->se_collapse ?: hse->se->se_cmp;
2932 return collapse_fn(a, b);
2933}
2934
2935static int64_t __sort__hpp_sort(struct perf_hpp_fmt *fmt,
2936 struct hist_entry *a, struct hist_entry *b)
2937{
2938 struct hpp_sort_entry *hse;
2939 int64_t (*sort_fn)(struct hist_entry *, struct hist_entry *);
2940
2941 hse = container_of(fmt, struct hpp_sort_entry, hpp);
2942 sort_fn = hse->se->se_sort ?: hse->se->se_cmp;
2943 return sort_fn(a, b);
2944}
2945
2946bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
2947{
2948 return format->header == __sort__hpp_header;
2949}
2950
2951#define MK_SORT_ENTRY_CHK(key) \
2952bool perf_hpp__is_ ## key ## _entry(struct perf_hpp_fmt *fmt) \
2953{ \
2954 struct hpp_sort_entry *hse; \
2955 \
2956 if (!perf_hpp__is_sort_entry(fmt)) \
2957 return false; \
2958 \
2959 hse = container_of(fmt, struct hpp_sort_entry, hpp); \
2960 return hse->se == &sort_ ## key ; \
2961}
2962
2963#ifdef HAVE_LIBTRACEEVENT
2964MK_SORT_ENTRY_CHK(trace)
2965#else
2966bool perf_hpp__is_trace_entry(struct perf_hpp_fmt *fmt __maybe_unused)
2967{
2968 return false;
2969}
2970#endif
2971MK_SORT_ENTRY_CHK(srcline)
2972MK_SORT_ENTRY_CHK(srcfile)
2973MK_SORT_ENTRY_CHK(thread)
2974MK_SORT_ENTRY_CHK(comm)
2975MK_SORT_ENTRY_CHK(dso)
2976MK_SORT_ENTRY_CHK(sym)
2977MK_SORT_ENTRY_CHK(parallelism)
2978
2979
2980static bool __sort__hpp_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
2981{
2982 struct hpp_sort_entry *hse_a;
2983 struct hpp_sort_entry *hse_b;
2984
2985 if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
2986 return false;
2987
2988 hse_a = container_of(a, struct hpp_sort_entry, hpp);
2989 hse_b = container_of(b, struct hpp_sort_entry, hpp);
2990
2991 return hse_a->se == hse_b->se;
2992}
2993
2994static void hse_free(struct perf_hpp_fmt *fmt)
2995{
2996 struct hpp_sort_entry *hse;
2997
2998 hse = container_of(fmt, struct hpp_sort_entry, hpp);
2999 free(hse);
3000}
3001
3002static void hse_init(struct perf_hpp_fmt *fmt, struct hist_entry *he)
3003{
3004 struct hpp_sort_entry *hse;
3005
3006 if (!perf_hpp__is_sort_entry(fmt))
3007 return;
3008
3009 hse = container_of(fmt, struct hpp_sort_entry, hpp);
3010
3011 if (hse->se->se_init)
3012 hse->se->se_init(he);
3013}
3014
3015static struct hpp_sort_entry *
3016__sort_dimension__alloc_hpp(struct sort_dimension *sd, int level)
3017{
3018 struct hpp_sort_entry *hse;
3019
3020 hse = malloc(sizeof(*hse));
3021 if (hse == NULL) {
3022 pr_err("Memory allocation failed\n");
3023 return NULL;
3024 }
3025
3026 hse->se = sd->entry;
3027 hse->hpp.name = sd->entry->se_header;
3028 hse->hpp.header = __sort__hpp_header;
3029 hse->hpp.width = __sort__hpp_width;
3030 hse->hpp.entry = __sort__hpp_entry;
3031 hse->hpp.color = NULL;
3032
3033 hse->hpp.cmp = __sort__hpp_cmp;
3034 hse->hpp.collapse = __sort__hpp_collapse;
3035 hse->hpp.sort = __sort__hpp_sort;
3036 hse->hpp.equal = __sort__hpp_equal;
3037 hse->hpp.free = hse_free;
3038 hse->hpp.init = hse_init;
3039
3040 INIT_LIST_HEAD(&hse->hpp.list);
3041 INIT_LIST_HEAD(&hse->hpp.sort_list);
3042 hse->hpp.elide = false;
3043 hse->hpp.len = 0;
3044 hse->hpp.user_len = 0;
3045 hse->hpp.level = level;
3046
3047 return hse;
3048}
3049
3050static void hpp_free(struct perf_hpp_fmt *fmt)
3051{
3052 free(fmt);
3053}
3054
3055static struct perf_hpp_fmt *__hpp_dimension__alloc_hpp(struct hpp_dimension *hd,
3056 int level)
3057{
3058 struct perf_hpp_fmt *fmt;
3059
3060 fmt = memdup(hd->fmt, sizeof(*fmt));
3061 if (fmt) {
3062 INIT_LIST_HEAD(&fmt->list);
3063 INIT_LIST_HEAD(&fmt->sort_list);
3064 fmt->free = hpp_free;
3065 fmt->level = level;
3066 }
3067
3068 return fmt;
3069}
3070
3071int hist_entry__filter(struct hist_entry *he, int type, const void *arg)
3072{
3073 struct perf_hpp_fmt *fmt;
3074 struct hpp_sort_entry *hse;
3075 int ret = -1;
3076 int r;
3077
3078 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
3079 if (!perf_hpp__is_sort_entry(fmt))
3080 continue;
3081
3082 hse = container_of(fmt, struct hpp_sort_entry, hpp);
3083 if (hse->se->se_filter == NULL)
3084 continue;
3085
3086 /*
3087 * hist entry is filtered if any of sort key in the hpp list
3088 * is applied. But it should skip non-matched filter types.
3089 */
3090 r = hse->se->se_filter(he, type, arg);
3091 if (r >= 0) {
3092 if (ret < 0)
3093 ret = 0;
3094 ret |= r;
3095 }
3096 }
3097
3098 return ret;
3099}
3100
3101static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd,
3102 struct perf_hpp_list *list,
3103 int level)
3104{
3105 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, level);
3106
3107 if (hse == NULL)
3108 return -ENOMEM;
3109
3110 perf_hpp_list__register_sort_field(list, &hse->hpp);
3111 return 0;
3112}
3113
3114static int __sort_dimension__add_hpp_output(struct sort_dimension *sd,
3115 struct perf_hpp_list *list,
3116 int level)
3117{
3118 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, level);
3119
3120 if (hse == NULL)
3121 return -ENOMEM;
3122
3123 perf_hpp_list__column_register(list, &hse->hpp);
3124 return 0;
3125}
3126
3127#ifndef HAVE_LIBTRACEEVENT
3128bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *fmt __maybe_unused)
3129{
3130 return false;
3131}
3132bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt __maybe_unused,
3133 struct hists *hists __maybe_unused)
3134{
3135 return false;
3136}
3137#else
3138struct hpp_dynamic_entry {
3139 struct perf_hpp_fmt hpp;
3140 struct evsel *evsel;
3141 struct tep_format_field *field;
3142 unsigned dynamic_len;
3143 bool raw_trace;
3144};
3145
3146static int hde_width(struct hpp_dynamic_entry *hde)
3147{
3148 if (!hde->hpp.len) {
3149 int len = hde->dynamic_len;
3150 int namelen = strlen(hde->field->name);
3151 int fieldlen = hde->field->size;
3152
3153 if (namelen > len)
3154 len = namelen;
3155
3156 if (!(hde->field->flags & TEP_FIELD_IS_STRING)) {
3157 /* length for print hex numbers */
3158 fieldlen = hde->field->size * 2 + 2;
3159 }
3160 if (fieldlen > len)
3161 len = fieldlen;
3162
3163 hde->hpp.len = len;
3164 }
3165 return hde->hpp.len;
3166}
3167
3168static void update_dynamic_len(struct hpp_dynamic_entry *hde,
3169 struct hist_entry *he)
3170{
3171 char *str, *pos;
3172 struct tep_format_field *field = hde->field;
3173 size_t namelen;
3174 bool last = false;
3175
3176 if (hde->raw_trace)
3177 return;
3178
3179 /* parse pretty print result and update max length */
3180 if (!he->trace_output)
3181 he->trace_output = get_trace_output(he);
3182
3183 namelen = strlen(field->name);
3184 str = he->trace_output;
3185
3186 while (str) {
3187 pos = strchr(str, ' ');
3188 if (pos == NULL) {
3189 last = true;
3190 pos = str + strlen(str);
3191 }
3192
3193 if (!strncmp(str, field->name, namelen)) {
3194 size_t len;
3195
3196 str += namelen + 1;
3197 len = pos - str;
3198
3199 if (len > hde->dynamic_len)
3200 hde->dynamic_len = len;
3201 break;
3202 }
3203
3204 if (last)
3205 str = NULL;
3206 else
3207 str = pos + 1;
3208 }
3209}
3210
3211static int __sort__hde_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
3212 struct hists *hists __maybe_unused,
3213 int line __maybe_unused,
3214 int *span __maybe_unused)
3215{
3216 struct hpp_dynamic_entry *hde;
3217 size_t len = fmt->user_len;
3218
3219 hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
3220
3221 if (!len)
3222 len = hde_width(hde);
3223
3224 return scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, hde->field->name);
3225}
3226
3227static int __sort__hde_width(struct perf_hpp_fmt *fmt,
3228 struct perf_hpp *hpp __maybe_unused,
3229 struct hists *hists __maybe_unused)
3230{
3231 struct hpp_dynamic_entry *hde;
3232 size_t len = fmt->user_len;
3233
3234 hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
3235
3236 if (!len)
3237 len = hde_width(hde);
3238
3239 return len;
3240}
3241
3242bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *hists)
3243{
3244 struct hpp_dynamic_entry *hde;
3245
3246 hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
3247
3248 return hists_to_evsel(hists) == hde->evsel;
3249}
3250
3251static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
3252 struct hist_entry *he)
3253{
3254 struct hpp_dynamic_entry *hde;
3255 size_t len = fmt->user_len;
3256 char *str, *pos;
3257 struct tep_format_field *field;
3258 size_t namelen;
3259 bool last = false;
3260 int ret;
3261
3262 hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
3263
3264 if (!len)
3265 len = hde_width(hde);
3266
3267 if (hde->raw_trace)
3268 goto raw_field;
3269
3270 if (!he->trace_output)
3271 he->trace_output = get_trace_output(he);
3272
3273 field = hde->field;
3274 namelen = strlen(field->name);
3275 str = he->trace_output;
3276
3277 while (str) {
3278 pos = strchr(str, ' ');
3279 if (pos == NULL) {
3280 last = true;
3281 pos = str + strlen(str);
3282 }
3283
3284 if (!strncmp(str, field->name, namelen)) {
3285 str += namelen + 1;
3286 str = strndup(str, pos - str);
3287
3288 if (str == NULL)
3289 return scnprintf(hpp->buf, hpp->size,
3290 "%*.*s", len, len, "ERROR");
3291 break;
3292 }
3293
3294 if (last)
3295 str = NULL;
3296 else
3297 str = pos + 1;
3298 }
3299
3300 if (str == NULL) {
3301 struct trace_seq seq;
3302raw_field:
3303 trace_seq_init(&seq);
3304 tep_print_field(&seq, he->raw_data, hde->field);
3305 str = seq.buffer;
3306 }
3307
3308 ret = scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, str);
3309 free(str);
3310 return ret;
3311}
3312
3313static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt,
3314 struct hist_entry *a, struct hist_entry *b)
3315{
3316 struct hpp_dynamic_entry *hde;
3317 struct tep_format_field *field;
3318 unsigned offset, size;
3319
3320 hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
3321
3322 field = hde->field;
3323 if (field->flags & TEP_FIELD_IS_DYNAMIC) {
3324 unsigned long long dyn;
3325
3326 tep_read_number_field(field, a->raw_data, &dyn);
3327 offset = dyn & 0xffff;
3328 size = (dyn >> 16) & 0xffff;
3329 if (tep_field_is_relative(field->flags))
3330 offset += field->offset + field->size;
3331 /* record max width for output */
3332 if (size > hde->dynamic_len)
3333 hde->dynamic_len = size;
3334 } else {
3335 offset = field->offset;
3336 size = field->size;
3337 }
3338
3339 return memcmp(a->raw_data + offset, b->raw_data + offset, size);
3340}
3341
3342bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *fmt)
3343{
3344 return fmt->cmp == __sort__hde_cmp;
3345}
3346
3347static bool __sort__hde_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
3348{
3349 struct hpp_dynamic_entry *hde_a;
3350 struct hpp_dynamic_entry *hde_b;
3351
3352 if (!perf_hpp__is_dynamic_entry(a) || !perf_hpp__is_dynamic_entry(b))
3353 return false;
3354
3355 hde_a = container_of(a, struct hpp_dynamic_entry, hpp);
3356 hde_b = container_of(b, struct hpp_dynamic_entry, hpp);
3357
3358 return hde_a->field == hde_b->field;
3359}
3360
3361static void hde_free(struct perf_hpp_fmt *fmt)
3362{
3363 struct hpp_dynamic_entry *hde;
3364
3365 hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
3366 free(hde);
3367}
3368
3369static void __sort__hde_init(struct perf_hpp_fmt *fmt, struct hist_entry *he)
3370{
3371 struct hpp_dynamic_entry *hde;
3372
3373 if (!perf_hpp__is_dynamic_entry(fmt))
3374 return;
3375
3376 hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
3377 update_dynamic_len(hde, he);
3378}
3379
3380static struct hpp_dynamic_entry *
3381__alloc_dynamic_entry(struct evsel *evsel, struct tep_format_field *field,
3382 int level)
3383{
3384 struct hpp_dynamic_entry *hde;
3385
3386 hde = malloc(sizeof(*hde));
3387 if (hde == NULL) {
3388 pr_debug("Memory allocation failed\n");
3389 return NULL;
3390 }
3391
3392 hde->evsel = evsel;
3393 hde->field = field;
3394 hde->dynamic_len = 0;
3395
3396 hde->hpp.name = field->name;
3397 hde->hpp.header = __sort__hde_header;
3398 hde->hpp.width = __sort__hde_width;
3399 hde->hpp.entry = __sort__hde_entry;
3400 hde->hpp.color = NULL;
3401
3402 hde->hpp.init = __sort__hde_init;
3403 hde->hpp.cmp = __sort__hde_cmp;
3404 hde->hpp.collapse = __sort__hde_cmp;
3405 hde->hpp.sort = __sort__hde_cmp;
3406 hde->hpp.equal = __sort__hde_equal;
3407 hde->hpp.free = hde_free;
3408
3409 INIT_LIST_HEAD(&hde->hpp.list);
3410 INIT_LIST_HEAD(&hde->hpp.sort_list);
3411 hde->hpp.elide = false;
3412 hde->hpp.len = 0;
3413 hde->hpp.user_len = 0;
3414 hde->hpp.level = level;
3415
3416 return hde;
3417}
3418#endif /* HAVE_LIBTRACEEVENT */
3419
3420struct perf_hpp_fmt *perf_hpp_fmt__dup(struct perf_hpp_fmt *fmt)
3421{
3422 struct perf_hpp_fmt *new_fmt = NULL;
3423
3424 if (perf_hpp__is_sort_entry(fmt)) {
3425 struct hpp_sort_entry *hse, *new_hse;
3426
3427 hse = container_of(fmt, struct hpp_sort_entry, hpp);
3428 new_hse = memdup(hse, sizeof(*hse));
3429 if (new_hse)
3430 new_fmt = &new_hse->hpp;
3431#ifdef HAVE_LIBTRACEEVENT
3432 } else if (perf_hpp__is_dynamic_entry(fmt)) {
3433 struct hpp_dynamic_entry *hde, *new_hde;
3434
3435 hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
3436 new_hde = memdup(hde, sizeof(*hde));
3437 if (new_hde)
3438 new_fmt = &new_hde->hpp;
3439#endif
3440 } else {
3441 new_fmt = memdup(fmt, sizeof(*fmt));
3442 }
3443
3444 INIT_LIST_HEAD(&new_fmt->list);
3445 INIT_LIST_HEAD(&new_fmt->sort_list);
3446
3447 return new_fmt;
3448}
3449
3450static int parse_field_name(char *str, char **event, char **field, char **opt)
3451{
3452 char *event_name, *field_name, *opt_name;
3453
3454 event_name = str;
3455 field_name = strchr(str, '.');
3456
3457 if (field_name) {
3458 *field_name++ = '\0';
3459 } else {
3460 event_name = NULL;
3461 field_name = str;
3462 }
3463
3464 opt_name = strchr(field_name, '/');
3465 if (opt_name)
3466 *opt_name++ = '\0';
3467
3468 *event = event_name;
3469 *field = field_name;
3470 *opt = opt_name;
3471
3472 return 0;
3473}
3474
3475/* find match evsel using a given event name. The event name can be:
3476 * 1. '%' + event index (e.g. '%1' for first event)
3477 * 2. full event name (e.g. sched:sched_switch)
3478 * 3. partial event name (should not contain ':')
3479 */
3480static struct evsel *find_evsel(struct evlist *evlist, char *event_name)
3481{
3482 struct evsel *evsel = NULL;
3483 struct evsel *pos;
3484 bool full_name;
3485
3486 /* case 1 */
3487 if (event_name[0] == '%') {
3488 int nr = strtol(event_name+1, NULL, 0);
3489
3490 if (nr > evlist__nr_entries(evlist))
3491 return NULL;
3492
3493 evsel = evlist__first(evlist);
3494 while (--nr > 0)
3495 evsel = evsel__next(evsel);
3496
3497 return evsel;
3498 }
3499
3500 full_name = !!strchr(event_name, ':');
3501 evlist__for_each_entry(evlist, pos) {
3502 /* case 2 */
3503 if (full_name && evsel__name_is(pos, event_name))
3504 return pos;
3505 /* case 3 */
3506 if (!full_name && strstr(pos->name, event_name)) {
3507 if (evsel) {
3508 pr_debug("'%s' event is ambiguous: it can be %s or %s\n",
3509 event_name, evsel->name, pos->name);
3510 return NULL;
3511 }
3512 evsel = pos;
3513 }
3514 }
3515
3516 return evsel;
3517}
3518
3519#ifdef HAVE_LIBTRACEEVENT
3520static int __dynamic_dimension__add(struct evsel *evsel,
3521 struct tep_format_field *field,
3522 bool raw_trace, int level)
3523{
3524 struct hpp_dynamic_entry *hde;
3525
3526 hde = __alloc_dynamic_entry(evsel, field, level);
3527 if (hde == NULL)
3528 return -ENOMEM;
3529
3530 hde->raw_trace = raw_trace;
3531
3532 perf_hpp__register_sort_field(&hde->hpp);
3533 return 0;
3534}
3535
3536static int add_evsel_fields(struct evsel *evsel, bool raw_trace, int level)
3537{
3538 int ret;
3539 struct tep_event *tp_format = evsel__tp_format(evsel);
3540 struct tep_format_field *field = tp_format ? tp_format->format.fields : NULL;
3541 while (field) {
3542 ret = __dynamic_dimension__add(evsel, field, raw_trace, level);
3543 if (ret < 0)
3544 return ret;
3545
3546 field = field->next;
3547 }
3548 return 0;
3549}
3550
3551static int add_all_dynamic_fields(struct evlist *evlist, bool raw_trace,
3552 int level)
3553{
3554 int ret;
3555 struct evsel *evsel;
3556
3557 evlist__for_each_entry(evlist, evsel) {
3558 if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
3559 continue;
3560
3561 ret = add_evsel_fields(evsel, raw_trace, level);
3562 if (ret < 0)
3563 return ret;
3564 }
3565 return 0;
3566}
3567
3568static int add_all_matching_fields(struct evlist *evlist,
3569 char *field_name, bool raw_trace, int level)
3570{
3571 int ret = -ESRCH;
3572 struct evsel *evsel;
3573
3574 evlist__for_each_entry(evlist, evsel) {
3575 struct tep_event *tp_format;
3576 struct tep_format_field *field;
3577
3578 if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
3579 continue;
3580
3581 tp_format = evsel__tp_format(evsel);
3582 if (tp_format == NULL)
3583 continue;
3584
3585 field = tep_find_any_field(tp_format, field_name);
3586 if (field == NULL)
3587 continue;
3588
3589 ret = __dynamic_dimension__add(evsel, field, raw_trace, level);
3590 if (ret < 0)
3591 break;
3592 }
3593 return ret;
3594}
3595#endif /* HAVE_LIBTRACEEVENT */
3596
3597static int add_dynamic_entry(struct evlist *evlist, const char *tok,
3598 int level)
3599{
3600 char *str, *event_name, *field_name, *opt_name;
3601 struct evsel *evsel;
3602 bool raw_trace = symbol_conf.raw_trace;
3603 int ret = 0;
3604
3605 if (evlist == NULL)
3606 return -ENOENT;
3607
3608 str = strdup(tok);
3609 if (str == NULL)
3610 return -ENOMEM;
3611
3612 if (parse_field_name(str, &event_name, &field_name, &opt_name) < 0) {
3613 ret = -EINVAL;
3614 goto out;
3615 }
3616
3617 if (opt_name) {
3618 if (strcmp(opt_name, "raw")) {
3619 pr_debug("unsupported field option %s\n", opt_name);
3620 ret = -EINVAL;
3621 goto out;
3622 }
3623 raw_trace = true;
3624 }
3625
3626#ifdef HAVE_LIBTRACEEVENT
3627 if (!strcmp(field_name, "trace_fields")) {
3628 ret = add_all_dynamic_fields(evlist, raw_trace, level);
3629 goto out;
3630 }
3631
3632 if (event_name == NULL) {
3633 ret = add_all_matching_fields(evlist, field_name, raw_trace, level);
3634 goto out;
3635 }
3636#else
3637 evlist__for_each_entry(evlist, evsel) {
3638 if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT) {
3639 pr_err("%s %s", ret ? "," : "This perf binary isn't linked with libtraceevent, can't process", evsel__name(evsel));
3640 ret = -ENOTSUP;
3641 }
3642 }
3643
3644 if (ret) {
3645 pr_err("\n");
3646 goto out;
3647 }
3648#endif
3649
3650 evsel = find_evsel(evlist, event_name);
3651 if (evsel == NULL) {
3652 pr_debug("Cannot find event: %s\n", event_name);
3653 ret = -ENOENT;
3654 goto out;
3655 }
3656
3657 if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) {
3658 pr_debug("%s is not a tracepoint event\n", event_name);
3659 ret = -EINVAL;
3660 goto out;
3661 }
3662
3663#ifdef HAVE_LIBTRACEEVENT
3664 if (!strcmp(field_name, "*")) {
3665 ret = add_evsel_fields(evsel, raw_trace, level);
3666 } else {
3667 struct tep_event *tp_format = evsel__tp_format(evsel);
3668 struct tep_format_field *field =
3669 tp_format ? tep_find_any_field(tp_format, field_name) : NULL;
3670
3671 if (field == NULL) {
3672 pr_debug("Cannot find event field for %s.%s\n",
3673 event_name, field_name);
3674 return -ENOENT;
3675 }
3676
3677 ret = __dynamic_dimension__add(evsel, field, raw_trace, level);
3678 }
3679#else
3680 (void)level;
3681 (void)raw_trace;
3682#endif /* HAVE_LIBTRACEEVENT */
3683
3684out:
3685 free(str);
3686 return ret;
3687}
3688
3689static int __sort_dimension__update(struct sort_dimension *sd,
3690 struct perf_hpp_list *list)
3691{
3692 if (sd->entry == &sort_parent && parent_pattern) {
3693 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
3694 if (ret) {
3695 char err[BUFSIZ];
3696
3697 regerror(ret, &parent_regex, err, sizeof(err));
3698 pr_err("Invalid regex: %s\n%s", parent_pattern, err);
3699 return -EINVAL;
3700 }
3701 list->parent = 1;
3702 } else if (sd->entry == &sort_sym) {
3703 list->sym = 1;
3704 /*
3705 * perf diff displays the performance difference amongst
3706 * two or more perf.data files. Those files could come
3707 * from different binaries. So we should not compare
3708 * their ips, but the name of symbol.
3709 */
3710 if (sort__mode == SORT_MODE__DIFF)
3711 sd->entry->se_collapse = sort__sym_sort;
3712
3713 } else if (sd->entry == &sort_sym_offset) {
3714 list->sym = 1;
3715 } else if (sd->entry == &sort_dso) {
3716 list->dso = 1;
3717 } else if (sd->entry == &sort_socket) {
3718 list->socket = 1;
3719 } else if (sd->entry == &sort_thread) {
3720 list->thread = 1;
3721 } else if (sd->entry == &sort_comm) {
3722 list->comm = 1;
3723 } else if (sd->entry == &sort_comm_nodigit) {
3724 list->comm_nodigit = list->comm = 1;
3725 } else if (sd->entry == &sort_type_offset) {
3726 symbol_conf.annotate_data_member = true;
3727 } else if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) {
3728 list->sym = 1;
3729 } else if (sd->entry == &sort_mem_dcacheline && cacheline_size() == 0) {
3730 return -EINVAL;
3731 } else if (sd->entry == &sort_mem_daddr_sym) {
3732 list->sym = 1;
3733 }
3734
3735 if (sd->entry->se_collapse)
3736 list->need_collapse = 1;
3737
3738 return 0;
3739}
3740
3741static int __sort_dimension__add(struct sort_dimension *sd,
3742 struct perf_hpp_list *list,
3743 int level)
3744{
3745 int ret;
3746
3747 if (sd->taken)
3748 return 0;
3749
3750 ret = __sort_dimension__add_hpp_sort(sd, list, level);
3751 if (ret < 0)
3752 return ret;
3753
3754 ret = __sort_dimension__update(sd, list);
3755 if (ret < 0)
3756 return ret;
3757
3758 sd->taken = 1;
3759
3760 return 0;
3761}
3762
3763static int __hpp_dimension__add(struct hpp_dimension *hd,
3764 struct perf_hpp_list *list,
3765 int level)
3766{
3767 struct perf_hpp_fmt *fmt;
3768
3769 if (hd->taken)
3770 return 0;
3771
3772 fmt = __hpp_dimension__alloc_hpp(hd, level);
3773 if (!fmt)
3774 return -ENOMEM;
3775
3776 hd->taken = 1;
3777 hd->was_taken = 1;
3778 perf_hpp_list__register_sort_field(list, fmt);
3779 return 0;
3780}
3781
3782static int __sort_dimension__add_output(struct perf_hpp_list *list,
3783 struct sort_dimension *sd,
3784 int level)
3785{
3786 int ret;
3787
3788 if (sd->taken)
3789 return 0;
3790
3791 ret = __sort_dimension__add_hpp_output(sd, list, level);
3792 if (ret < 0)
3793 return ret;
3794
3795 ret = __sort_dimension__update(sd, list);
3796 if (ret < 0)
3797 return ret;
3798
3799 sd->taken = 1;
3800 return 0;
3801}
3802
3803static int __hpp_dimension__add_output(struct perf_hpp_list *list,
3804 struct hpp_dimension *hd,
3805 int level)
3806{
3807 struct perf_hpp_fmt *fmt;
3808
3809 if (hd->taken)
3810 return 0;
3811
3812 fmt = __hpp_dimension__alloc_hpp(hd, level);
3813 if (!fmt)
3814 return -ENOMEM;
3815
3816 hd->taken = 1;
3817 perf_hpp_list__column_register(list, fmt);
3818 return 0;
3819}
3820
3821int hpp_dimension__add_output(unsigned col, bool implicit)
3822{
3823 struct hpp_dimension *hd;
3824
3825 BUG_ON(col >= PERF_HPP__MAX_INDEX);
3826 hd = &hpp_sort_dimensions[col];
3827 if (implicit && !hd->was_taken)
3828 return 0;
3829 return __hpp_dimension__add_output(&perf_hpp_list, hd, /*level=*/0);
3830}
3831
3832int sort_dimension__add(struct perf_hpp_list *list, const char *tok,
3833 struct evlist *evlist, struct perf_env *env,
3834 int level)
3835{
3836 unsigned int i, j;
3837
3838 /*
3839 * Check to see if there are any arch specific
3840 * sort dimensions not applicable for the current
3841 * architecture. If so, Skip that sort key since
3842 * we don't want to display it in the output fields.
3843 */
3844 for (j = 0; j < ARRAY_SIZE(arch_specific_sort_keys); j++) {
3845 if (!strcmp(arch_specific_sort_keys[j], tok) &&
3846 !arch_support_sort_key(tok, env)) {
3847 return 0;
3848 }
3849 }
3850
3851 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
3852 struct sort_dimension *sd = &common_sort_dimensions[i];
3853
3854 if (!sd->name || strncasecmp(tok, sd->name, strlen(tok)))
3855 continue;
3856
3857 for (j = 0; j < ARRAY_SIZE(dynamic_headers); j++) {
3858 if (sd->name && !strcmp(dynamic_headers[j], sd->name))
3859 sort_dimension_add_dynamic_header(sd, env);
3860 }
3861
3862 return __sort_dimension__add(sd, list, level);
3863 }
3864
3865 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
3866 struct sort_dimension *sd = &bstack_sort_dimensions[i];
3867
3868 if (!sd->name || strncasecmp(tok, sd->name, strlen(tok)))
3869 continue;
3870
3871 if ((sort__mode != SORT_MODE__BRANCH) &&
3872 strncasecmp(tok, "callchain_branch_predicted",
3873 strlen(tok)) &&
3874 strncasecmp(tok, "callchain_branch_abort",
3875 strlen(tok)) &&
3876 strncasecmp(tok, "callchain_branch_cycles",
3877 strlen(tok)))
3878 return -EINVAL;
3879
3880 return __sort_dimension__add(sd, list, level);
3881 }
3882
3883 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
3884 struct sort_dimension *sd = &memory_sort_dimensions[i];
3885
3886 if (!sd->name || strncasecmp(tok, sd->name, strlen(tok)))
3887 continue;
3888
3889 if (sort__mode != SORT_MODE__MEMORY)
3890 return -EINVAL;
3891
3892 return __sort_dimension__add(sd, list, level);
3893 }
3894
3895 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
3896 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
3897
3898 if (strncasecmp(tok, hd->name, strlen(tok)))
3899 continue;
3900
3901 return __hpp_dimension__add(hd, list, level);
3902 }
3903
3904 if (!add_dynamic_entry(evlist, tok, level))
3905 return 0;
3906
3907 return -ESRCH;
3908}
3909
3910/* This should match with sort_dimension__add() above */
3911static bool is_hpp_sort_key(const char *key, struct perf_env *env)
3912{
3913 unsigned i;
3914
3915 for (i = 0; i < ARRAY_SIZE(arch_specific_sort_keys); i++) {
3916 if (!strcmp(arch_specific_sort_keys[i], key) &&
3917 !arch_support_sort_key(key, env)) {
3918 return false;
3919 }
3920 }
3921
3922 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
3923 struct sort_dimension *sd = &common_sort_dimensions[i];
3924
3925 if (sd->name && !strncasecmp(key, sd->name, strlen(key)))
3926 return false;
3927 }
3928
3929 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
3930 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
3931
3932 if (!strncasecmp(key, hd->name, strlen(key)))
3933 return true;
3934 }
3935 return false;
3936}
3937
3938static int setup_sort_list(struct perf_hpp_list *list, char *str,
3939 struct evlist *evlist, struct perf_env *env)
3940{
3941 char *tmp, *tok;
3942 int ret = 0;
3943 int level = 0;
3944 int next_level = 1;
3945 int prev_level = 0;
3946 bool in_group = false;
3947 bool prev_was_hpp = false;
3948
3949 do {
3950 tok = str;
3951 tmp = strpbrk(str, "{}, ");
3952 if (tmp) {
3953 if (in_group)
3954 next_level = level;
3955 else
3956 next_level = level + 1;
3957
3958 if (*tmp == '{')
3959 in_group = true;
3960 else if (*tmp == '}')
3961 in_group = false;
3962
3963 *tmp = '\0';
3964 str = tmp + 1;
3965 }
3966
3967 if (*tok) {
3968 if (is_hpp_sort_key(tok, env)) {
3969 /* keep output (hpp) sort keys in the same level */
3970 if (prev_was_hpp) {
3971 bool next_same = (level == next_level);
3972
3973 level = prev_level;
3974 next_level = next_same ? level : level+1;
3975 }
3976 prev_was_hpp = true;
3977 } else {
3978 prev_was_hpp = false;
3979 }
3980
3981 ret = sort_dimension__add(list, tok, evlist, env, level);
3982 switch (ret) {
3983 case 0:
3984 break;
3985 case -EINVAL:
3986 if (!cacheline_size() && !strncasecmp(tok, "dcacheline", strlen(tok)))
3987 ui__error("The \"dcacheline\" --sort key needs to know the cacheline size and it couldn't be determined on this system");
3988 else
3989 ui__error("Invalid --sort key: `%s'", tok);
3990 goto out;
3991 case -ESRCH:
3992 ui__error("Unknown --sort key: `%s'", tok);
3993 goto out;
3994 default: {
3995 char buf[STRERR_BUFSIZE];
3996
3997 ui__error("%s for --sort key: `%s'",
3998 str_error_r(-ret, buf, sizeof(buf)), tok);
3999 goto out;
4000 }
4001 }
4002 prev_level = level;
4003 }
4004
4005 level = next_level;
4006 } while (tmp);
4007
4008out:
4009 return ret;
4010}
4011
4012static const char *get_default_sort_order(struct evlist *evlist)
4013{
4014 const char *default_sort_orders[] = {
4015 default_sort_order,
4016 default_branch_sort_order,
4017 default_mem_sort_order,
4018 default_top_sort_order,
4019 default_diff_sort_order,
4020 default_tracepoint_sort_order,
4021 };
4022 bool use_trace = true;
4023 struct evsel *evsel;
4024
4025 BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
4026
4027 if (evlist == NULL || evlist__empty(evlist))
4028 goto out_no_evlist;
4029
4030 evlist__for_each_entry(evlist, evsel) {
4031 if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) {
4032 use_trace = false;
4033 break;
4034 }
4035 }
4036
4037 if (use_trace) {
4038 sort__mode = SORT_MODE__TRACEPOINT;
4039 if (symbol_conf.raw_trace)
4040 return "trace_fields";
4041 }
4042out_no_evlist:
4043 return default_sort_orders[sort__mode];
4044}
4045
4046static int setup_sort_order(struct evlist *evlist)
4047{
4048 char *new_sort_order;
4049
4050 /*
4051 * Append '+'-prefixed sort order to the default sort
4052 * order string.
4053 */
4054 if (!sort_order || is_strict_order(sort_order))
4055 return 0;
4056
4057 if (sort_order[1] == '\0') {
4058 ui__error("Invalid --sort key: `+'");
4059 return -EINVAL;
4060 }
4061
4062 /*
4063 * We allocate new sort_order string, but we never free it,
4064 * because it's checked over the rest of the code.
4065 */
4066 if (asprintf(&new_sort_order, "%s,%s",
4067 get_default_sort_order(evlist), sort_order + 1) < 0) {
4068 pr_err("Not enough memory to set up --sort");
4069 return -ENOMEM;
4070 }
4071
4072 sort_order = new_sort_order;
4073 return 0;
4074}
4075
4076/*
4077 * Adds 'pre,' prefix into 'str' is 'pre' is
4078 * not already part of 'str'.
4079 */
4080static char *prefix_if_not_in(const char *pre, char *str)
4081{
4082 char *n;
4083
4084 if (!str || strstr(str, pre))
4085 return str;
4086
4087 if (asprintf(&n, "%s,%s", pre, str) < 0)
4088 n = NULL;
4089
4090 free(str);
4091 return n;
4092}
4093
4094static char *setup_overhead(char *keys)
4095{
4096 if (sort__mode == SORT_MODE__DIFF)
4097 return keys;
4098
4099 if (symbol_conf.prefer_latency) {
4100 keys = prefix_if_not_in("overhead", keys);
4101 keys = prefix_if_not_in("latency", keys);
4102 if (symbol_conf.cumulate_callchain) {
4103 keys = prefix_if_not_in("overhead_children", keys);
4104 keys = prefix_if_not_in("latency_children", keys);
4105 }
4106 } else if (!keys || (!strstr(keys, "overhead") &&
4107 !strstr(keys, "latency"))) {
4108 if (symbol_conf.enable_latency)
4109 keys = prefix_if_not_in("latency", keys);
4110 keys = prefix_if_not_in("overhead", keys);
4111 if (symbol_conf.cumulate_callchain) {
4112 if (symbol_conf.enable_latency)
4113 keys = prefix_if_not_in("latency_children", keys);
4114 keys = prefix_if_not_in("overhead_children", keys);
4115 }
4116 }
4117
4118 return keys;
4119}
4120
4121static int __setup_sorting(struct evlist *evlist, struct perf_env *env)
4122{
4123 char *str;
4124 const char *sort_keys;
4125 int ret = 0;
4126
4127 ret = setup_sort_order(evlist);
4128 if (ret)
4129 return ret;
4130
4131 sort_keys = sort_order;
4132 if (sort_keys == NULL) {
4133 if (is_strict_order(field_order)) {
4134 /*
4135 * If user specified field order but no sort order,
4136 * we'll honor it and not add default sort orders.
4137 */
4138 return 0;
4139 }
4140
4141 sort_keys = get_default_sort_order(evlist);
4142 }
4143
4144 str = strdup(sort_keys);
4145 if (str == NULL) {
4146 pr_err("Not enough memory to setup sort keys");
4147 return -ENOMEM;
4148 }
4149
4150 /*
4151 * Prepend overhead fields for backward compatibility.
4152 */
4153 if (!is_strict_order(field_order)) {
4154 str = setup_overhead(str);
4155 if (str == NULL) {
4156 pr_err("Not enough memory to setup overhead keys");
4157 return -ENOMEM;
4158 }
4159 }
4160
4161 ret = setup_sort_list(&perf_hpp_list, str, evlist, env);
4162
4163 free(str);
4164 return ret;
4165}
4166
4167void perf_hpp__set_elide(int idx, bool elide)
4168{
4169 struct perf_hpp_fmt *fmt;
4170 struct hpp_sort_entry *hse;
4171
4172 perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
4173 if (!perf_hpp__is_sort_entry(fmt))
4174 continue;
4175
4176 hse = container_of(fmt, struct hpp_sort_entry, hpp);
4177 if (hse->se->se_width_idx == idx) {
4178 fmt->elide = elide;
4179 break;
4180 }
4181 }
4182}
4183
4184static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)
4185{
4186 if (list && strlist__nr_entries(list) == 1) {
4187 if (fp != NULL)
4188 fprintf(fp, "# %s: %s\n", list_name,
4189 strlist__entry(list, 0)->s);
4190 return true;
4191 }
4192 return false;
4193}
4194
4195static bool get_elide(int idx, FILE *output)
4196{
4197 switch (idx) {
4198 case HISTC_SYMBOL:
4199 return __get_elide(symbol_conf.sym_list, "symbol", output);
4200 case HISTC_DSO:
4201 return __get_elide(symbol_conf.dso_list, "dso", output);
4202 case HISTC_COMM:
4203 case HISTC_COMM_NODIGIT:
4204 return __get_elide(symbol_conf.comm_list, "comm", output);
4205 default:
4206 break;
4207 }
4208
4209 if (sort__mode != SORT_MODE__BRANCH)
4210 return false;
4211
4212 switch (idx) {
4213 case HISTC_SYMBOL_FROM:
4214 return __get_elide(symbol_conf.sym_from_list, "sym_from", output);
4215 case HISTC_SYMBOL_TO:
4216 return __get_elide(symbol_conf.sym_to_list, "sym_to", output);
4217 case HISTC_DSO_FROM:
4218 return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
4219 case HISTC_DSO_TO:
4220 return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
4221 case HISTC_ADDR_FROM:
4222 return __get_elide(symbol_conf.sym_from_list, "addr_from", output);
4223 case HISTC_ADDR_TO:
4224 return __get_elide(symbol_conf.sym_to_list, "addr_to", output);
4225 default:
4226 break;
4227 }
4228
4229 return false;
4230}
4231
4232void sort__setup_elide(FILE *output)
4233{
4234 struct perf_hpp_fmt *fmt;
4235 struct hpp_sort_entry *hse;
4236
4237 perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
4238 if (!perf_hpp__is_sort_entry(fmt))
4239 continue;
4240
4241 hse = container_of(fmt, struct hpp_sort_entry, hpp);
4242 fmt->elide = get_elide(hse->se->se_width_idx, output);
4243 }
4244
4245 /*
4246 * It makes no sense to elide all of sort entries.
4247 * Just revert them to show up again.
4248 */
4249 perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
4250 if (!perf_hpp__is_sort_entry(fmt))
4251 continue;
4252
4253 if (!fmt->elide)
4254 return;
4255 }
4256
4257 perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
4258 if (!perf_hpp__is_sort_entry(fmt))
4259 continue;
4260
4261 fmt->elide = false;
4262 }
4263}
4264
4265int output_field_add(struct perf_hpp_list *list, const char *tok, int *level)
4266{
4267 unsigned int i;
4268
4269 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
4270 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
4271
4272 if (strncasecmp(tok, hd->name, strlen(tok)))
4273 continue;
4274
4275 if (!strcasecmp(tok, "weight"))
4276 ui__warning("--fields weight shows the average value unlike in the --sort key.\n");
4277
4278 if (hd->mem_mode && sort__mode != SORT_MODE__MEMORY)
4279 continue;
4280
4281 return __hpp_dimension__add_output(list, hd, *level);
4282 }
4283
4284 /*
4285 * A non-output field will increase level so that it can be in a
4286 * different hierarchy.
4287 */
4288 (*level)++;
4289
4290 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
4291 struct sort_dimension *sd = &common_sort_dimensions[i];
4292
4293 if (!sd->name || strncasecmp(tok, sd->name, strlen(tok)))
4294 continue;
4295
4296 return __sort_dimension__add_output(list, sd, *level);
4297 }
4298
4299 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
4300 struct sort_dimension *sd = &bstack_sort_dimensions[i];
4301
4302 if (!sd->name || strncasecmp(tok, sd->name, strlen(tok)))
4303 continue;
4304
4305 if (sort__mode != SORT_MODE__BRANCH)
4306 return -EINVAL;
4307
4308 return __sort_dimension__add_output(list, sd, *level);
4309 }
4310
4311 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
4312 struct sort_dimension *sd = &memory_sort_dimensions[i];
4313
4314 if (!sd->name || strncasecmp(tok, sd->name, strlen(tok)))
4315 continue;
4316
4317 if (sort__mode != SORT_MODE__MEMORY)
4318 return -EINVAL;
4319
4320 return __sort_dimension__add_output(list, sd, *level);
4321 }
4322
4323 return -ESRCH;
4324}
4325
4326static int setup_output_list(struct perf_hpp_list *list, char *str)
4327{
4328 char *tmp, *tok;
4329 int ret = 0;
4330 int level = 0;
4331
4332 for (tok = strtok_r(str, ", ", &tmp);
4333 tok; tok = strtok_r(NULL, ", ", &tmp)) {
4334 ret = output_field_add(list, tok, &level);
4335 switch (ret) {
4336 case 0:
4337 break;
4338 case -EINVAL:
4339 ui__error("Invalid --fields key: `%s'", tok);
4340 goto out;
4341 case -ESRCH:
4342 ui__error("Unknown --fields key: `%s'", tok);
4343 goto out;
4344 default: {
4345 char buf[STRERR_BUFSIZE];
4346
4347 ui__error("%s for --fields key: `%s'",
4348 str_error_r(-ret, buf, sizeof(buf)), tok);
4349 goto out;
4350 }
4351 }
4352 }
4353
4354out:
4355 return ret;
4356}
4357
4358void reset_dimensions(void)
4359{
4360 unsigned int i;
4361
4362 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++)
4363 common_sort_dimensions[i].taken = 0;
4364
4365 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++)
4366 hpp_sort_dimensions[i].taken = 0;
4367
4368 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++)
4369 bstack_sort_dimensions[i].taken = 0;
4370
4371 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++)
4372 memory_sort_dimensions[i].taken = 0;
4373}
4374
4375bool is_strict_order(const char *order)
4376{
4377 return order && (*order != '+');
4378}
4379
4380static int __setup_output_field(void)
4381{
4382 char *str, *strp;
4383 int ret = -EINVAL;
4384
4385 if (field_order == NULL)
4386 return 0;
4387
4388 strp = str = strdup(field_order);
4389 if (str == NULL) {
4390 pr_err("Not enough memory to setup output fields");
4391 return -ENOMEM;
4392 }
4393
4394 if (!is_strict_order(field_order))
4395 strp++;
4396
4397 if (!strlen(strp)) {
4398 ui__error("Invalid --fields key: `+'");
4399 goto out;
4400 }
4401
4402 ret = setup_output_list(&perf_hpp_list, strp);
4403
4404out:
4405 free(str);
4406 return ret;
4407}
4408
4409int setup_sorting(struct evlist *evlist, struct perf_env *env)
4410{
4411 int err;
4412
4413 err = __setup_sorting(evlist, env);
4414 if (err < 0)
4415 return err;
4416
4417 if (parent_pattern != default_parent_pattern) {
4418 err = sort_dimension__add(&perf_hpp_list, "parent", evlist, env, -1);
4419 if (err < 0)
4420 return err;
4421 }
4422
4423 reset_dimensions();
4424
4425 /*
4426 * perf diff doesn't use default hpp output fields.
4427 */
4428 if (sort__mode != SORT_MODE__DIFF)
4429 perf_hpp__init();
4430
4431 err = __setup_output_field();
4432 if (err < 0)
4433 return err;
4434
4435 err = perf_hpp__alloc_mem_stats(&perf_hpp_list, evlist);
4436 if (err < 0)
4437 return err;
4438
4439 /* copy sort keys to output fields */
4440 perf_hpp__setup_output_field(&perf_hpp_list);
4441 /* and then copy output fields to sort keys */
4442 perf_hpp__append_sort_keys(&perf_hpp_list);
4443
4444 /* setup hists-specific output fields */
4445 if (perf_hpp__setup_hists_formats(&perf_hpp_list, evlist) < 0)
4446 return -1;
4447
4448 return 0;
4449}
4450
4451void reset_output_field(void)
4452{
4453 perf_hpp_list.need_collapse = 0;
4454 perf_hpp_list.parent = 0;
4455 perf_hpp_list.sym = 0;
4456 perf_hpp_list.dso = 0;
4457
4458 field_order = NULL;
4459 sort_order = NULL;
4460
4461 reset_dimensions();
4462 perf_hpp__reset_output_field(&perf_hpp_list);
4463}
4464
4465#define INDENT (3*8 + 1)
4466
4467static void add_key(struct strbuf *sb, const char *str, int *llen)
4468{
4469 if (!str)
4470 return;
4471
4472 if (*llen >= 75) {
4473 strbuf_addstr(sb, "\n\t\t\t ");
4474 *llen = INDENT;
4475 }
4476 strbuf_addf(sb, " %s", str);
4477 *llen += strlen(str) + 1;
4478}
4479
4480static void add_sort_string(struct strbuf *sb, struct sort_dimension *s, int n,
4481 int *llen)
4482{
4483 int i;
4484
4485 for (i = 0; i < n; i++)
4486 add_key(sb, s[i].name, llen);
4487}
4488
4489static void add_hpp_sort_string(struct strbuf *sb, struct hpp_dimension *s, int n,
4490 int *llen)
4491{
4492 int i;
4493
4494 for (i = 0; i < n; i++)
4495 add_key(sb, s[i].name, llen);
4496}
4497
4498char *sort_help(const char *prefix, enum sort_mode mode)
4499{
4500 struct strbuf sb;
4501 char *s;
4502 int len = strlen(prefix) + INDENT;
4503
4504 strbuf_init(&sb, 300);
4505 strbuf_addstr(&sb, prefix);
4506 add_hpp_sort_string(&sb, hpp_sort_dimensions,
4507 ARRAY_SIZE(hpp_sort_dimensions), &len);
4508 add_sort_string(&sb, common_sort_dimensions,
4509 ARRAY_SIZE(common_sort_dimensions), &len);
4510 if (mode == SORT_MODE__NORMAL || mode == SORT_MODE__BRANCH)
4511 add_sort_string(&sb, bstack_sort_dimensions,
4512 ARRAY_SIZE(bstack_sort_dimensions), &len);
4513 if (mode == SORT_MODE__NORMAL || mode == SORT_MODE__MEMORY)
4514 add_sort_string(&sb, memory_sort_dimensions,
4515 ARRAY_SIZE(memory_sort_dimensions), &len);
4516 s = strbuf_detach(&sb, NULL);
4517 strbuf_release(&sb);
4518 return s;
4519}