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