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