1
2#include <errno.h>
3#include <inttypes.h>
4#include <regex.h>
5#include <linux/mman.h>
6#include "sort.h"
7#include "hist.h"
8#include "comm.h"
9#include "symbol.h"
10#include "thread.h"
11#include "evsel.h"
12#include "evlist.h"
13#include "strlist.h"
14#include <traceevent/event-parse.h>
15#include "mem-events.h"
16#include "annotate.h"
17#include <linux/kernel.h>
18
19regex_t parent_regex;
20const char default_parent_pattern[] = "^sys_|^do_page_fault";
21const char *parent_pattern = default_parent_pattern;
22const char *default_sort_order = "comm,dso,symbol";
23const char default_branch_sort_order[] = "comm,dso_from,symbol_from,symbol_to,cycles";
24const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
25const char default_top_sort_order[] = "dso,symbol";
26const char default_diff_sort_order[] = "dso,symbol";
27const char default_tracepoint_sort_order[] = "trace";
28const char *sort_order;
29const char *field_order;
30regex_t ignore_callees_regex;
31int have_ignore_callees = 0;
32enum sort_mode sort__mode = SORT_MODE__NORMAL;
33
34
35
36
37
38
39
40
41
42
43static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
44{
45 int n;
46 va_list ap;
47
48 va_start(ap, fmt);
49 n = vsnprintf(bf, size, fmt, ap);
50 if (symbol_conf.field_sep && n > 0) {
51 char *sep = bf;
52
53 while (1) {
54 sep = strchr(sep, *symbol_conf.field_sep);
55 if (sep == NULL)
56 break;
57 *sep = '.';
58 }
59 }
60 va_end(ap);
61
62 if (n >= (int)size)
63 return size - 1;
64 return n;
65}
66
67static int64_t cmp_null(const void *l, const void *r)
68{
69 if (!l && !r)
70 return 0;
71 else if (!l)
72 return -1;
73 else
74 return 1;
75}
76
77
78
79static int64_t
80sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
81{
82 return right->thread->tid - left->thread->tid;
83}
84
85static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,
86 size_t size, unsigned int width)
87{
88 const char *comm = thread__comm_str(he->thread);
89
90 width = max(7U, width) - 8;
91 return repsep_snprintf(bf, size, "%7d:%-*.*s", he->thread->tid,
92 width, width, comm ?: "");
93}
94
95static int hist_entry__thread_filter(struct hist_entry *he, int type, const void *arg)
96{
97 const struct thread *th = arg;
98
99 if (type != HIST_FILTER__THREAD)
100 return -1;
101
102 return th && he->thread != th;
103}
104
105struct sort_entry sort_thread = {
106 .se_header = " Pid:Command",
107 .se_cmp = sort__thread_cmp,
108 .se_snprintf = hist_entry__thread_snprintf,
109 .se_filter = hist_entry__thread_filter,
110 .se_width_idx = HISTC_THREAD,
111};
112
113
114
115
116
117
118
119
120static int64_t
121sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
122{
123 return strcmp(comm__str(right->comm), comm__str(left->comm));
124}
125
126static int64_t
127sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
128{
129 return strcmp(comm__str(right->comm), comm__str(left->comm));
130}
131
132static int64_t
133sort__comm_sort(struct hist_entry *left, struct hist_entry *right)
134{
135 return strcmp(comm__str(right->comm), comm__str(left->comm));
136}
137
138static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
139 size_t size, unsigned int width)
140{
141 return repsep_snprintf(bf, size, "%-*.*s", width, width, comm__str(he->comm));
142}
143
144struct sort_entry sort_comm = {
145 .se_header = "Command",
146 .se_cmp = sort__comm_cmp,
147 .se_collapse = sort__comm_collapse,
148 .se_sort = sort__comm_sort,
149 .se_snprintf = hist_entry__comm_snprintf,
150 .se_filter = hist_entry__thread_filter,
151 .se_width_idx = HISTC_COMM,
152};
153
154
155
156static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
157{
158 struct dso *dso_l = map_l ? map_l->dso : NULL;
159 struct dso *dso_r = map_r ? map_r->dso : NULL;
160 const char *dso_name_l, *dso_name_r;
161
162 if (!dso_l || !dso_r)
163 return cmp_null(dso_r, dso_l);
164
165 if (verbose > 0) {
166 dso_name_l = dso_l->long_name;
167 dso_name_r = dso_r->long_name;
168 } else {
169 dso_name_l = dso_l->short_name;
170 dso_name_r = dso_r->short_name;
171 }
172
173 return strcmp(dso_name_l, dso_name_r);
174}
175
176static int64_t
177sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
178{
179 return _sort__dso_cmp(right->ms.map, left->ms.map);
180}
181
182static int _hist_entry__dso_snprintf(struct map *map, char *bf,
183 size_t size, unsigned int width)
184{
185 if (map && map->dso) {
186 const char *dso_name = verbose > 0 ? map->dso->long_name :
187 map->dso->short_name;
188 return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name);
189 }
190
191 return repsep_snprintf(bf, size, "%-*.*s", width, width, "[unknown]");
192}
193
194static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,
195 size_t size, unsigned int width)
196{
197 return _hist_entry__dso_snprintf(he->ms.map, bf, size, width);
198}
199
200static int hist_entry__dso_filter(struct hist_entry *he, int type, const void *arg)
201{
202 const struct dso *dso = arg;
203
204 if (type != HIST_FILTER__DSO)
205 return -1;
206
207 return dso && (!he->ms.map || he->ms.map->dso != dso);
208}
209
210struct sort_entry sort_dso = {
211 .se_header = "Shared Object",
212 .se_cmp = sort__dso_cmp,
213 .se_snprintf = hist_entry__dso_snprintf,
214 .se_filter = hist_entry__dso_filter,
215 .se_width_idx = HISTC_DSO,
216};
217
218
219
220static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip)
221{
222 return (int64_t)(right_ip - left_ip);
223}
224
225static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
226{
227 if (!sym_l || !sym_r)
228 return cmp_null(sym_l, sym_r);
229
230 if (sym_l == sym_r)
231 return 0;
232
233 if (sym_l->inlined || sym_r->inlined)
234 return strcmp(sym_l->name, sym_r->name);
235
236 if (sym_l->start != sym_r->start)
237 return (int64_t)(sym_r->start - sym_l->start);
238
239 return (int64_t)(sym_r->end - sym_l->end);
240}
241
242static int64_t
243sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
244{
245 int64_t ret;
246
247 if (!left->ms.sym && !right->ms.sym)
248 return _sort__addr_cmp(left->ip, right->ip);
249
250
251
252
253
254 if (!hists__has(left->hists, dso) || hists__has(right->hists, dso)) {
255 ret = sort__dso_cmp(left, right);
256 if (ret != 0)
257 return ret;
258 }
259
260 return _sort__sym_cmp(left->ms.sym, right->ms.sym);
261}
262
263static int64_t
264sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
265{
266 if (!left->ms.sym || !right->ms.sym)
267 return cmp_null(left->ms.sym, right->ms.sym);
268
269 return strcmp(right->ms.sym->name, left->ms.sym->name);
270}
271
272static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
273 u64 ip, char level, char *bf, size_t size,
274 unsigned int width)
275{
276 size_t ret = 0;
277
278 if (verbose > 0) {
279 char o = map ? dso__symtab_origin(map->dso) : '!';
280 ret += repsep_snprintf(bf, size, "%-#*llx %c ",
281 BITS_PER_LONG / 4 + 2, ip, o);
282 }
283
284 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
285 if (sym && map) {
286 if (sym->type == STT_OBJECT) {
287 ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
288 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
289 ip - map->unmap_ip(map, sym->start));
290 } else {
291 ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
292 width - ret,
293 sym->name);
294 if (sym->inlined)
295 ret += repsep_snprintf(bf + ret, size - ret,
296 " (inlined)");
297 }
298 } else {
299 size_t len = BITS_PER_LONG / 4;
300 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
301 len, ip);
302 }
303
304 return ret;
305}
306
307static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,
308 size_t size, unsigned int width)
309{
310 return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip,
311 he->level, bf, size, width);
312}
313
314static int hist_entry__sym_filter(struct hist_entry *he, int type, const void *arg)
315{
316 const char *sym = arg;
317
318 if (type != HIST_FILTER__SYMBOL)
319 return -1;
320
321 return sym && (!he->ms.sym || !strstr(he->ms.sym->name, sym));
322}
323
324struct sort_entry sort_sym = {
325 .se_header = "Symbol",
326 .se_cmp = sort__sym_cmp,
327 .se_sort = sort__sym_sort,
328 .se_snprintf = hist_entry__sym_snprintf,
329 .se_filter = hist_entry__sym_filter,
330 .se_width_idx = HISTC_SYMBOL,
331};
332
333
334
335char *hist_entry__srcline(struct hist_entry *he)
336{
337 return map__srcline(he->ms.map, he->ip, he->ms.sym);
338}
339
340static int64_t
341sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
342{
343 if (!left->srcline)
344 left->srcline = hist_entry__srcline(left);
345 if (!right->srcline)
346 right->srcline = hist_entry__srcline(right);
347
348 return strcmp(right->srcline, left->srcline);
349}
350
351static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
352 size_t size, unsigned int width)
353{
354 if (!he->srcline)
355 he->srcline = hist_entry__srcline(he);
356
357 return repsep_snprintf(bf, size, "%-.*s", width, he->srcline);
358}
359
360struct sort_entry sort_srcline = {
361 .se_header = "Source:Line",
362 .se_cmp = sort__srcline_cmp,
363 .se_snprintf = hist_entry__srcline_snprintf,
364 .se_width_idx = HISTC_SRCLINE,
365};
366
367
368
369static char *addr_map_symbol__srcline(struct addr_map_symbol *ams)
370{
371 return map__srcline(ams->map, ams->al_addr, ams->sym);
372}
373
374static int64_t
375sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right)
376{
377 if (!left->branch_info->srcline_from)
378 left->branch_info->srcline_from = addr_map_symbol__srcline(&left->branch_info->from);
379
380 if (!right->branch_info->srcline_from)
381 right->branch_info->srcline_from = addr_map_symbol__srcline(&right->branch_info->from);
382
383 return strcmp(right->branch_info->srcline_from, left->branch_info->srcline_from);
384}
385
386static int hist_entry__srcline_from_snprintf(struct hist_entry *he, char *bf,
387 size_t size, unsigned int width)
388{
389 return repsep_snprintf(bf, size, "%-*.*s", width, width, he->branch_info->srcline_from);
390}
391
392struct sort_entry sort_srcline_from = {
393 .se_header = "From Source:Line",
394 .se_cmp = sort__srcline_from_cmp,
395 .se_snprintf = hist_entry__srcline_from_snprintf,
396 .se_width_idx = HISTC_SRCLINE_FROM,
397};
398
399
400
401static int64_t
402sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right)
403{
404 if (!left->branch_info->srcline_to)
405 left->branch_info->srcline_to = addr_map_symbol__srcline(&left->branch_info->to);
406
407 if (!right->branch_info->srcline_to)
408 right->branch_info->srcline_to = addr_map_symbol__srcline(&right->branch_info->to);
409
410 return strcmp(right->branch_info->srcline_to, left->branch_info->srcline_to);
411}
412
413static int hist_entry__srcline_to_snprintf(struct hist_entry *he, char *bf,
414 size_t size, unsigned int width)
415{
416 return repsep_snprintf(bf, size, "%-*.*s", width, width, he->branch_info->srcline_to);
417}
418
419struct sort_entry sort_srcline_to = {
420 .se_header = "To Source:Line",
421 .se_cmp = sort__srcline_to_cmp,
422 .se_snprintf = hist_entry__srcline_to_snprintf,
423 .se_width_idx = HISTC_SRCLINE_TO,
424};
425
426static int hist_entry__sym_ipc_snprintf(struct hist_entry *he, char *bf,
427 size_t size, unsigned int width)
428{
429
430 struct symbol *sym = he->ms.sym;
431 struct map *map = he->ms.map;
432 struct perf_evsel *evsel = hists_to_evsel(he->hists);
433 struct annotation *notes;
434 double ipc = 0.0, coverage = 0.0;
435 char tmp[64];
436
437 if (!sym)
438 return repsep_snprintf(bf, size, "%-*s", width, "-");
439
440 if (!sym->annotate2 && symbol__annotate2(sym, map, evsel,
441 &annotation__default_options, NULL) < 0) {
442 return 0;
443 }
444
445 notes = symbol__annotation(sym);
446
447 if (notes->hit_cycles)
448 ipc = notes->hit_insn / ((double)notes->hit_cycles);
449
450 if (notes->total_insn) {
451 coverage = notes->cover_insn * 100.0 /
452 ((double)notes->total_insn);
453 }
454
455 snprintf(tmp, sizeof(tmp), "%-5.2f [%5.1f%%]", ipc, coverage);
456 return repsep_snprintf(bf, size, "%-*s", width, tmp);
457}
458
459struct sort_entry sort_sym_ipc = {
460 .se_header = "IPC [IPC Coverage]",
461 .se_cmp = sort__sym_cmp,
462 .se_snprintf = hist_entry__sym_ipc_snprintf,
463 .se_width_idx = HISTC_SYMBOL_IPC,
464};
465
466static int hist_entry__sym_ipc_null_snprintf(struct hist_entry *he
467 __maybe_unused,
468 char *bf, size_t size,
469 unsigned int width)
470{
471 char tmp[64];
472
473 snprintf(tmp, sizeof(tmp), "%-5s %2s", "-", "-");
474 return repsep_snprintf(bf, size, "%-*s", width, tmp);
475}
476
477struct sort_entry sort_sym_ipc_null = {
478 .se_header = "IPC [IPC Coverage]",
479 .se_cmp = sort__sym_cmp,
480 .se_snprintf = hist_entry__sym_ipc_null_snprintf,
481 .se_width_idx = HISTC_SYMBOL_IPC,
482};
483
484
485
486static char no_srcfile[1];
487
488static char *hist_entry__get_srcfile(struct hist_entry *e)
489{
490 char *sf, *p;
491 struct map *map = e->ms.map;
492
493 if (!map)
494 return no_srcfile;
495
496 sf = __get_srcline(map->dso, map__rip_2objdump(map, e->ip),
497 e->ms.sym, false, true, true, e->ip);
498 if (!strcmp(sf, SRCLINE_UNKNOWN))
499 return no_srcfile;
500 p = strchr(sf, ':');
501 if (p && *sf) {
502 *p = 0;
503 return sf;
504 }
505 free(sf);
506 return no_srcfile;
507}
508
509static int64_t
510sort__srcfile_cmp(struct hist_entry *left, struct hist_entry *right)
511{
512 if (!left->srcfile)
513 left->srcfile = hist_entry__get_srcfile(left);
514 if (!right->srcfile)
515 right->srcfile = hist_entry__get_srcfile(right);
516
517 return strcmp(right->srcfile, left->srcfile);
518}
519
520static int hist_entry__srcfile_snprintf(struct hist_entry *he, char *bf,
521 size_t size, unsigned int width)
522{
523 if (!he->srcfile)
524 he->srcfile = hist_entry__get_srcfile(he);
525
526 return repsep_snprintf(bf, size, "%-.*s", width, he->srcfile);
527}
528
529struct sort_entry sort_srcfile = {
530 .se_header = "Source File",
531 .se_cmp = sort__srcfile_cmp,
532 .se_snprintf = hist_entry__srcfile_snprintf,
533 .se_width_idx = HISTC_SRCFILE,
534};
535
536
537
538static int64_t
539sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
540{
541 struct symbol *sym_l = left->parent;
542 struct symbol *sym_r = right->parent;
543
544 if (!sym_l || !sym_r)
545 return cmp_null(sym_l, sym_r);
546
547 return strcmp(sym_r->name, sym_l->name);
548}
549
550static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
551 size_t size, unsigned int width)
552{
553 return repsep_snprintf(bf, size, "%-*.*s", width, width,
554 he->parent ? he->parent->name : "[other]");
555}
556
557struct sort_entry sort_parent = {
558 .se_header = "Parent symbol",
559 .se_cmp = sort__parent_cmp,
560 .se_snprintf = hist_entry__parent_snprintf,
561 .se_width_idx = HISTC_PARENT,
562};
563
564
565
566static int64_t
567sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
568{
569 return right->cpu - left->cpu;
570}
571
572static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf,
573 size_t size, unsigned int width)
574{
575 return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu);
576}
577
578struct sort_entry sort_cpu = {
579 .se_header = "CPU",
580 .se_cmp = sort__cpu_cmp,
581 .se_snprintf = hist_entry__cpu_snprintf,
582 .se_width_idx = HISTC_CPU,
583};
584
585
586
587static int64_t _sort__cgroup_dev_cmp(u64 left_dev, u64 right_dev)
588{
589 return (int64_t)(right_dev - left_dev);
590}
591
592static int64_t _sort__cgroup_inode_cmp(u64 left_ino, u64 right_ino)
593{
594 return (int64_t)(right_ino - left_ino);
595}
596
597static int64_t
598sort__cgroup_id_cmp(struct hist_entry *left, struct hist_entry *right)
599{
600 int64_t ret;
601
602 ret = _sort__cgroup_dev_cmp(right->cgroup_id.dev, left->cgroup_id.dev);
603 if (ret != 0)
604 return ret;
605
606 return _sort__cgroup_inode_cmp(right->cgroup_id.ino,
607 left->cgroup_id.ino);
608}
609
610static int hist_entry__cgroup_id_snprintf(struct hist_entry *he,
611 char *bf, size_t size,
612 unsigned int width __maybe_unused)
613{
614 return repsep_snprintf(bf, size, "%lu/0x%lx", he->cgroup_id.dev,
615 he->cgroup_id.ino);
616}
617
618struct sort_entry sort_cgroup_id = {
619 .se_header = "cgroup id (dev/inode)",
620 .se_cmp = sort__cgroup_id_cmp,
621 .se_snprintf = hist_entry__cgroup_id_snprintf,
622 .se_width_idx = HISTC_CGROUP_ID,
623};
624
625
626
627static int64_t
628sort__socket_cmp(struct hist_entry *left, struct hist_entry *right)
629{
630 return right->socket - left->socket;
631}
632
633static int hist_entry__socket_snprintf(struct hist_entry *he, char *bf,
634 size_t size, unsigned int width)
635{
636 return repsep_snprintf(bf, size, "%*.*d", width, width-3, he->socket);
637}
638
639static int hist_entry__socket_filter(struct hist_entry *he, int type, const void *arg)
640{
641 int sk = *(const int *)arg;
642
643 if (type != HIST_FILTER__SOCKET)
644 return -1;
645
646 return sk >= 0 && he->socket != sk;
647}
648
649struct sort_entry sort_socket = {
650 .se_header = "Socket",
651 .se_cmp = sort__socket_cmp,
652 .se_snprintf = hist_entry__socket_snprintf,
653 .se_filter = hist_entry__socket_filter,
654 .se_width_idx = HISTC_SOCKET,
655};
656
657
658
659static char *get_trace_output(struct hist_entry *he)
660{
661 struct trace_seq seq;
662 struct perf_evsel *evsel;
663 struct tep_record rec = {
664 .data = he->raw_data,
665 .size = he->raw_size,
666 };
667
668 evsel = hists_to_evsel(he->hists);
669
670 trace_seq_init(&seq);
671 if (symbol_conf.raw_trace) {
672 tep_print_fields(&seq, he->raw_data, he->raw_size,
673 evsel->tp_format);
674 } else {
675 tep_event_info(&seq, evsel->tp_format, &rec);
676 }
677
678
679
680
681 return realloc(seq.buffer, seq.len + 1);
682}
683
684static int64_t
685sort__trace_cmp(struct hist_entry *left, struct hist_entry *right)
686{
687 struct perf_evsel *evsel;
688
689 evsel = hists_to_evsel(left->hists);
690 if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
691 return 0;
692
693 if (left->trace_output == NULL)
694 left->trace_output = get_trace_output(left);
695 if (right->trace_output == NULL)
696 right->trace_output = get_trace_output(right);
697
698 return strcmp(right->trace_output, left->trace_output);
699}
700
701static int hist_entry__trace_snprintf(struct hist_entry *he, char *bf,
702 size_t size, unsigned int width)
703{
704 struct perf_evsel *evsel;
705
706 evsel = hists_to_evsel(he->hists);
707 if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
708 return scnprintf(bf, size, "%-.*s", width, "N/A");
709
710 if (he->trace_output == NULL)
711 he->trace_output = get_trace_output(he);
712 return repsep_snprintf(bf, size, "%-.*s", width, he->trace_output);
713}
714
715struct sort_entry sort_trace = {
716 .se_header = "Trace output",
717 .se_cmp = sort__trace_cmp,
718 .se_snprintf = hist_entry__trace_snprintf,
719 .se_width_idx = HISTC_TRACE,
720};
721
722
723
724static int64_t
725sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
726{
727 if (!left->branch_info || !right->branch_info)
728 return cmp_null(left->branch_info, right->branch_info);
729
730 return _sort__dso_cmp(left->branch_info->from.map,
731 right->branch_info->from.map);
732}
733
734static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,
735 size_t size, unsigned int width)
736{
737 if (he->branch_info)
738 return _hist_entry__dso_snprintf(he->branch_info->from.map,
739 bf, size, width);
740 else
741 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
742}
743
744static int hist_entry__dso_from_filter(struct hist_entry *he, int type,
745 const void *arg)
746{
747 const struct dso *dso = arg;
748
749 if (type != HIST_FILTER__DSO)
750 return -1;
751
752 return dso && (!he->branch_info || !he->branch_info->from.map ||
753 he->branch_info->from.map->dso != dso);
754}
755
756static int64_t
757sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
758{
759 if (!left->branch_info || !right->branch_info)
760 return cmp_null(left->branch_info, right->branch_info);
761
762 return _sort__dso_cmp(left->branch_info->to.map,
763 right->branch_info->to.map);
764}
765
766static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,
767 size_t size, unsigned int width)
768{
769 if (he->branch_info)
770 return _hist_entry__dso_snprintf(he->branch_info->to.map,
771 bf, size, width);
772 else
773 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
774}
775
776static int hist_entry__dso_to_filter(struct hist_entry *he, int type,
777 const void *arg)
778{
779 const struct dso *dso = arg;
780
781 if (type != HIST_FILTER__DSO)
782 return -1;
783
784 return dso && (!he->branch_info || !he->branch_info->to.map ||
785 he->branch_info->to.map->dso != dso);
786}
787
788static int64_t
789sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
790{
791 struct addr_map_symbol *from_l = &left->branch_info->from;
792 struct addr_map_symbol *from_r = &right->branch_info->from;
793
794 if (!left->branch_info || !right->branch_info)
795 return cmp_null(left->branch_info, right->branch_info);
796
797 from_l = &left->branch_info->from;
798 from_r = &right->branch_info->from;
799
800 if (!from_l->sym && !from_r->sym)
801 return _sort__addr_cmp(from_l->addr, from_r->addr);
802
803 return _sort__sym_cmp(from_l->sym, from_r->sym);
804}
805
806static int64_t
807sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
808{
809 struct addr_map_symbol *to_l, *to_r;
810
811 if (!left->branch_info || !right->branch_info)
812 return cmp_null(left->branch_info, right->branch_info);
813
814 to_l = &left->branch_info->to;
815 to_r = &right->branch_info->to;
816
817 if (!to_l->sym && !to_r->sym)
818 return _sort__addr_cmp(to_l->addr, to_r->addr);
819
820 return _sort__sym_cmp(to_l->sym, to_r->sym);
821}
822
823static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,
824 size_t size, unsigned int width)
825{
826 if (he->branch_info) {
827 struct addr_map_symbol *from = &he->branch_info->from;
828
829 return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
830 he->level, bf, size, width);
831 }
832
833 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
834}
835
836static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,
837 size_t size, unsigned int width)
838{
839 if (he->branch_info) {
840 struct addr_map_symbol *to = &he->branch_info->to;
841
842 return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
843 he->level, bf, size, width);
844 }
845
846 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
847}
848
849static int hist_entry__sym_from_filter(struct hist_entry *he, int type,
850 const void *arg)
851{
852 const char *sym = arg;
853
854 if (type != HIST_FILTER__SYMBOL)
855 return -1;
856
857 return sym && !(he->branch_info && he->branch_info->from.sym &&
858 strstr(he->branch_info->from.sym->name, sym));
859}
860
861static int hist_entry__sym_to_filter(struct hist_entry *he, int type,
862 const void *arg)
863{
864 const char *sym = arg;
865
866 if (type != HIST_FILTER__SYMBOL)
867 return -1;
868
869 return sym && !(he->branch_info && he->branch_info->to.sym &&
870 strstr(he->branch_info->to.sym->name, sym));
871}
872
873struct sort_entry sort_dso_from = {
874 .se_header = "Source Shared Object",
875 .se_cmp = sort__dso_from_cmp,
876 .se_snprintf = hist_entry__dso_from_snprintf,
877 .se_filter = hist_entry__dso_from_filter,
878 .se_width_idx = HISTC_DSO_FROM,
879};
880
881struct sort_entry sort_dso_to = {
882 .se_header = "Target Shared Object",
883 .se_cmp = sort__dso_to_cmp,
884 .se_snprintf = hist_entry__dso_to_snprintf,
885 .se_filter = hist_entry__dso_to_filter,
886 .se_width_idx = HISTC_DSO_TO,
887};
888
889struct sort_entry sort_sym_from = {
890 .se_header = "Source Symbol",
891 .se_cmp = sort__sym_from_cmp,
892 .se_snprintf = hist_entry__sym_from_snprintf,
893 .se_filter = hist_entry__sym_from_filter,
894 .se_width_idx = HISTC_SYMBOL_FROM,
895};
896
897struct sort_entry sort_sym_to = {
898 .se_header = "Target Symbol",
899 .se_cmp = sort__sym_to_cmp,
900 .se_snprintf = hist_entry__sym_to_snprintf,
901 .se_filter = hist_entry__sym_to_filter,
902 .se_width_idx = HISTC_SYMBOL_TO,
903};
904
905static int64_t
906sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
907{
908 unsigned char mp, p;
909
910 if (!left->branch_info || !right->branch_info)
911 return cmp_null(left->branch_info, right->branch_info);
912
913 mp = left->branch_info->flags.mispred != right->branch_info->flags.mispred;
914 p = left->branch_info->flags.predicted != right->branch_info->flags.predicted;
915 return mp || p;
916}
917
918static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,
919 size_t size, unsigned int width){
920 static const char *out = "N/A";
921
922 if (he->branch_info) {
923 if (he->branch_info->flags.predicted)
924 out = "N";
925 else if (he->branch_info->flags.mispred)
926 out = "Y";
927 }
928
929 return repsep_snprintf(bf, size, "%-*.*s", width, width, out);
930}
931
932static int64_t
933sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right)
934{
935 if (!left->branch_info || !right->branch_info)
936 return cmp_null(left->branch_info, right->branch_info);
937
938 return left->branch_info->flags.cycles -
939 right->branch_info->flags.cycles;
940}
941
942static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf,
943 size_t size, unsigned int width)
944{
945 if (!he->branch_info)
946 return scnprintf(bf, size, "%-.*s", width, "N/A");
947 if (he->branch_info->flags.cycles == 0)
948 return repsep_snprintf(bf, size, "%-*s", width, "-");
949 return repsep_snprintf(bf, size, "%-*hd", width,
950 he->branch_info->flags.cycles);
951}
952
953struct sort_entry sort_cycles = {
954 .se_header = "Basic Block Cycles",
955 .se_cmp = sort__cycles_cmp,
956 .se_snprintf = hist_entry__cycles_snprintf,
957 .se_width_idx = HISTC_CYCLES,
958};
959
960
961int64_t
962sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
963{
964 uint64_t l = 0, r = 0;
965
966 if (left->mem_info)
967 l = left->mem_info->daddr.addr;
968 if (right->mem_info)
969 r = right->mem_info->daddr.addr;
970
971 return (int64_t)(r - l);
972}
973
974static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,
975 size_t size, unsigned int width)
976{
977 uint64_t addr = 0;
978 struct map *map = NULL;
979 struct symbol *sym = NULL;
980
981 if (he->mem_info) {
982 addr = he->mem_info->daddr.addr;
983 map = he->mem_info->daddr.map;
984 sym = he->mem_info->daddr.sym;
985 }
986 return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
987 width);
988}
989
990int64_t
991sort__iaddr_cmp(struct hist_entry *left, struct hist_entry *right)
992{
993 uint64_t l = 0, r = 0;
994
995 if (left->mem_info)
996 l = left->mem_info->iaddr.addr;
997 if (right->mem_info)
998 r = right->mem_info->iaddr.addr;
999
1000 return (int64_t)(r - l);
1001}
1002
1003static int hist_entry__iaddr_snprintf(struct hist_entry *he, char *bf,
1004 size_t size, unsigned int width)
1005{
1006 uint64_t addr = 0;
1007 struct map *map = NULL;
1008 struct symbol *sym = NULL;
1009
1010 if (he->mem_info) {
1011 addr = he->mem_info->iaddr.addr;
1012 map = he->mem_info->iaddr.map;
1013 sym = he->mem_info->iaddr.sym;
1014 }
1015 return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
1016 width);
1017}
1018
1019static int64_t
1020sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
1021{
1022 struct map *map_l = NULL;
1023 struct map *map_r = NULL;
1024
1025 if (left->mem_info)
1026 map_l = left->mem_info->daddr.map;
1027 if (right->mem_info)
1028 map_r = right->mem_info->daddr.map;
1029
1030 return _sort__dso_cmp(map_l, map_r);
1031}
1032
1033static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,
1034 size_t size, unsigned int width)
1035{
1036 struct map *map = NULL;
1037
1038 if (he->mem_info)
1039 map = he->mem_info->daddr.map;
1040
1041 return _hist_entry__dso_snprintf(map, bf, size, width);
1042}
1043
1044static int64_t
1045sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
1046{
1047 union perf_mem_data_src data_src_l;
1048 union perf_mem_data_src data_src_r;
1049
1050 if (left->mem_info)
1051 data_src_l = left->mem_info->data_src;
1052 else
1053 data_src_l.mem_lock = PERF_MEM_LOCK_NA;
1054
1055 if (right->mem_info)
1056 data_src_r = right->mem_info->data_src;
1057 else
1058 data_src_r.mem_lock = PERF_MEM_LOCK_NA;
1059
1060 return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
1061}
1062
1063static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,
1064 size_t size, unsigned int width)
1065{
1066 char out[10];
1067
1068 perf_mem__lck_scnprintf(out, sizeof(out), he->mem_info);
1069 return repsep_snprintf(bf, size, "%.*s", width, out);
1070}
1071
1072static int64_t
1073sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
1074{
1075 union perf_mem_data_src data_src_l;
1076 union perf_mem_data_src data_src_r;
1077
1078 if (left->mem_info)
1079 data_src_l = left->mem_info->data_src;
1080 else
1081 data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
1082
1083 if (right->mem_info)
1084 data_src_r = right->mem_info->data_src;
1085 else
1086 data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
1087
1088 return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
1089}
1090
1091static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,
1092 size_t size, unsigned int width)
1093{
1094 char out[64];
1095
1096 perf_mem__tlb_scnprintf(out, sizeof(out), he->mem_info);
1097 return repsep_snprintf(bf, size, "%-*s", width, out);
1098}
1099
1100static int64_t
1101sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
1102{
1103 union perf_mem_data_src data_src_l;
1104 union perf_mem_data_src data_src_r;
1105
1106 if (left->mem_info)
1107 data_src_l = left->mem_info->data_src;
1108 else
1109 data_src_l.mem_lvl = PERF_MEM_LVL_NA;
1110
1111 if (right->mem_info)
1112 data_src_r = right->mem_info->data_src;
1113 else
1114 data_src_r.mem_lvl = PERF_MEM_LVL_NA;
1115
1116 return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
1117}
1118
1119static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,
1120 size_t size, unsigned int width)
1121{
1122 char out[64];
1123
1124 perf_mem__lvl_scnprintf(out, sizeof(out), he->mem_info);
1125 return repsep_snprintf(bf, size, "%-*s", width, out);
1126}
1127
1128static int64_t
1129sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
1130{
1131 union perf_mem_data_src data_src_l;
1132 union perf_mem_data_src data_src_r;
1133
1134 if (left->mem_info)
1135 data_src_l = left->mem_info->data_src;
1136 else
1137 data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
1138
1139 if (right->mem_info)
1140 data_src_r = right->mem_info->data_src;
1141 else
1142 data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
1143
1144 return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
1145}
1146
1147static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
1148 size_t size, unsigned int width)
1149{
1150 char out[64];
1151
1152 perf_mem__snp_scnprintf(out, sizeof(out), he->mem_info);
1153 return repsep_snprintf(bf, size, "%-*s", width, out);
1154}
1155
1156int64_t
1157sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
1158{
1159 u64 l, r;
1160 struct map *l_map, *r_map;
1161
1162 if (!left->mem_info) return -1;
1163 if (!right->mem_info) return 1;
1164
1165
1166 if (left->cpumode > right->cpumode) return -1;
1167 if (left->cpumode < right->cpumode) return 1;
1168
1169 l_map = left->mem_info->daddr.map;
1170 r_map = right->mem_info->daddr.map;
1171
1172
1173 if (!l_map && !r_map)
1174 goto addr;
1175
1176 if (!l_map) return -1;
1177 if (!r_map) return 1;
1178
1179 if (l_map->maj > r_map->maj) return -1;
1180 if (l_map->maj < r_map->maj) return 1;
1181
1182 if (l_map->min > r_map->min) return -1;
1183 if (l_map->min < r_map->min) return 1;
1184
1185 if (l_map->ino > r_map->ino) return -1;
1186 if (l_map->ino < r_map->ino) return 1;
1187
1188 if (l_map->ino_generation > r_map->ino_generation) return -1;
1189 if (l_map->ino_generation < r_map->ino_generation) return 1;
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199 if ((left->cpumode != PERF_RECORD_MISC_KERNEL) &&
1200 (!(l_map->flags & MAP_SHARED)) &&
1201 !l_map->maj && !l_map->min && !l_map->ino &&
1202 !l_map->ino_generation) {
1203
1204
1205 if (left->thread->pid_ > right->thread->pid_) return -1;
1206 if (left->thread->pid_ < right->thread->pid_) return 1;
1207 }
1208
1209addr:
1210
1211 l = cl_address(left->mem_info->daddr.al_addr);
1212 r = cl_address(right->mem_info->daddr.al_addr);
1213
1214 if (l > r) return -1;
1215 if (l < r) return 1;
1216
1217 return 0;
1218}
1219
1220static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf,
1221 size_t size, unsigned int width)
1222{
1223
1224 uint64_t addr = 0;
1225 struct map *map = NULL;
1226 struct symbol *sym = NULL;
1227 char level = he->level;
1228
1229 if (he->mem_info) {
1230 addr = cl_address(he->mem_info->daddr.al_addr);
1231 map = he->mem_info->daddr.map;
1232 sym = he->mem_info->daddr.sym;
1233
1234
1235 if ((he->cpumode != PERF_RECORD_MISC_KERNEL) &&
1236 map && !(map->prot & PROT_EXEC) &&
1237 (map->flags & MAP_SHARED) &&
1238 (map->maj || map->min || map->ino ||
1239 map->ino_generation))
1240 level = 's';
1241 else if (!map)
1242 level = 'X';
1243 }
1244 return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size,
1245 width);
1246}
1247
1248struct sort_entry sort_mispredict = {
1249 .se_header = "Branch Mispredicted",
1250 .se_cmp = sort__mispredict_cmp,
1251 .se_snprintf = hist_entry__mispredict_snprintf,
1252 .se_width_idx = HISTC_MISPREDICT,
1253};
1254
1255static u64 he_weight(struct hist_entry *he)
1256{
1257 return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
1258}
1259
1260static int64_t
1261sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
1262{
1263 return he_weight(left) - he_weight(right);
1264}
1265
1266static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,
1267 size_t size, unsigned int width)
1268{
1269 return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he));
1270}
1271
1272struct sort_entry sort_local_weight = {
1273 .se_header = "Local Weight",
1274 .se_cmp = sort__local_weight_cmp,
1275 .se_snprintf = hist_entry__local_weight_snprintf,
1276 .se_width_idx = HISTC_LOCAL_WEIGHT,
1277};
1278
1279static int64_t
1280sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
1281{
1282 return left->stat.weight - right->stat.weight;
1283}
1284
1285static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,
1286 size_t size, unsigned int width)
1287{
1288 return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight);
1289}
1290
1291struct sort_entry sort_global_weight = {
1292 .se_header = "Weight",
1293 .se_cmp = sort__global_weight_cmp,
1294 .se_snprintf = hist_entry__global_weight_snprintf,
1295 .se_width_idx = HISTC_GLOBAL_WEIGHT,
1296};
1297
1298struct sort_entry sort_mem_daddr_sym = {
1299 .se_header = "Data Symbol",
1300 .se_cmp = sort__daddr_cmp,
1301 .se_snprintf = hist_entry__daddr_snprintf,
1302 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
1303};
1304
1305struct sort_entry sort_mem_iaddr_sym = {
1306 .se_header = "Code Symbol",
1307 .se_cmp = sort__iaddr_cmp,
1308 .se_snprintf = hist_entry__iaddr_snprintf,
1309 .se_width_idx = HISTC_MEM_IADDR_SYMBOL,
1310};
1311
1312struct sort_entry sort_mem_daddr_dso = {
1313 .se_header = "Data Object",
1314 .se_cmp = sort__dso_daddr_cmp,
1315 .se_snprintf = hist_entry__dso_daddr_snprintf,
1316 .se_width_idx = HISTC_MEM_DADDR_DSO,
1317};
1318
1319struct sort_entry sort_mem_locked = {
1320 .se_header = "Locked",
1321 .se_cmp = sort__locked_cmp,
1322 .se_snprintf = hist_entry__locked_snprintf,
1323 .se_width_idx = HISTC_MEM_LOCKED,
1324};
1325
1326struct sort_entry sort_mem_tlb = {
1327 .se_header = "TLB access",
1328 .se_cmp = sort__tlb_cmp,
1329 .se_snprintf = hist_entry__tlb_snprintf,
1330 .se_width_idx = HISTC_MEM_TLB,
1331};
1332
1333struct sort_entry sort_mem_lvl = {
1334 .se_header = "Memory access",
1335 .se_cmp = sort__lvl_cmp,
1336 .se_snprintf = hist_entry__lvl_snprintf,
1337 .se_width_idx = HISTC_MEM_LVL,
1338};
1339
1340struct sort_entry sort_mem_snoop = {
1341 .se_header = "Snoop",
1342 .se_cmp = sort__snoop_cmp,
1343 .se_snprintf = hist_entry__snoop_snprintf,
1344 .se_width_idx = HISTC_MEM_SNOOP,
1345};
1346
1347struct sort_entry sort_mem_dcacheline = {
1348 .se_header = "Data Cacheline",
1349 .se_cmp = sort__dcacheline_cmp,
1350 .se_snprintf = hist_entry__dcacheline_snprintf,
1351 .se_width_idx = HISTC_MEM_DCACHELINE,
1352};
1353
1354static int64_t
1355sort__phys_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
1356{
1357 uint64_t l = 0, r = 0;
1358
1359 if (left->mem_info)
1360 l = left->mem_info->daddr.phys_addr;
1361 if (right->mem_info)
1362 r = right->mem_info->daddr.phys_addr;
1363
1364 return (int64_t)(r - l);
1365}
1366
1367static int hist_entry__phys_daddr_snprintf(struct hist_entry *he, char *bf,
1368 size_t size, unsigned int width)
1369{
1370 uint64_t addr = 0;
1371 size_t ret = 0;
1372 size_t len = BITS_PER_LONG / 4;
1373
1374 addr = he->mem_info->daddr.phys_addr;
1375
1376 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", he->level);
1377
1378 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", len, addr);
1379
1380 ret += repsep_snprintf(bf + ret, size - ret, "%-*s", width - ret, "");
1381
1382 if (ret > width)
1383 bf[width] = '\0';
1384
1385 return width;
1386}
1387
1388struct sort_entry sort_mem_phys_daddr = {
1389 .se_header = "Data Physical Address",
1390 .se_cmp = sort__phys_daddr_cmp,
1391 .se_snprintf = hist_entry__phys_daddr_snprintf,
1392 .se_width_idx = HISTC_MEM_PHYS_DADDR,
1393};
1394
1395static int64_t
1396sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
1397{
1398 if (!left->branch_info || !right->branch_info)
1399 return cmp_null(left->branch_info, right->branch_info);
1400
1401 return left->branch_info->flags.abort !=
1402 right->branch_info->flags.abort;
1403}
1404
1405static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf,
1406 size_t size, unsigned int width)
1407{
1408 static const char *out = "N/A";
1409
1410 if (he->branch_info) {
1411 if (he->branch_info->flags.abort)
1412 out = "A";
1413 else
1414 out = ".";
1415 }
1416
1417 return repsep_snprintf(bf, size, "%-*s", width, out);
1418}
1419
1420struct sort_entry sort_abort = {
1421 .se_header = "Transaction abort",
1422 .se_cmp = sort__abort_cmp,
1423 .se_snprintf = hist_entry__abort_snprintf,
1424 .se_width_idx = HISTC_ABORT,
1425};
1426
1427static int64_t
1428sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right)
1429{
1430 if (!left->branch_info || !right->branch_info)
1431 return cmp_null(left->branch_info, right->branch_info);
1432
1433 return left->branch_info->flags.in_tx !=
1434 right->branch_info->flags.in_tx;
1435}
1436
1437static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf,
1438 size_t size, unsigned int width)
1439{
1440 static const char *out = "N/A";
1441
1442 if (he->branch_info) {
1443 if (he->branch_info->flags.in_tx)
1444 out = "T";
1445 else
1446 out = ".";
1447 }
1448
1449 return repsep_snprintf(bf, size, "%-*s", width, out);
1450}
1451
1452struct sort_entry sort_in_tx = {
1453 .se_header = "Branch in transaction",
1454 .se_cmp = sort__in_tx_cmp,
1455 .se_snprintf = hist_entry__in_tx_snprintf,
1456 .se_width_idx = HISTC_IN_TX,
1457};
1458
1459static int64_t
1460sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
1461{
1462 return left->transaction - right->transaction;
1463}
1464
1465static inline char *add_str(char *p, const char *str)
1466{
1467 strcpy(p, str);
1468 return p + strlen(str);
1469}
1470
1471static struct txbit {
1472 unsigned flag;
1473 const char *name;
1474 int skip_for_len;
1475} txbits[] = {
1476 { PERF_TXN_ELISION, "EL ", 0 },
1477 { PERF_TXN_TRANSACTION, "TX ", 1 },
1478 { PERF_TXN_SYNC, "SYNC ", 1 },
1479 { PERF_TXN_ASYNC, "ASYNC ", 0 },
1480 { PERF_TXN_RETRY, "RETRY ", 0 },
1481 { PERF_TXN_CONFLICT, "CON ", 0 },
1482 { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 },
1483 { PERF_TXN_CAPACITY_READ, "CAP-READ ", 0 },
1484 { 0, NULL, 0 }
1485};
1486
1487int hist_entry__transaction_len(void)
1488{
1489 int i;
1490 int len = 0;
1491
1492 for (i = 0; txbits[i].name; i++) {
1493 if (!txbits[i].skip_for_len)
1494 len += strlen(txbits[i].name);
1495 }
1496 len += 4;
1497 return len;
1498}
1499
1500static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf,
1501 size_t size, unsigned int width)
1502{
1503 u64 t = he->transaction;
1504 char buf[128];
1505 char *p = buf;
1506 int i;
1507
1508 buf[0] = 0;
1509 for (i = 0; txbits[i].name; i++)
1510 if (txbits[i].flag & t)
1511 p = add_str(p, txbits[i].name);
1512 if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC)))
1513 p = add_str(p, "NEITHER ");
1514 if (t & PERF_TXN_ABORT_MASK) {
1515 sprintf(p, ":%" PRIx64,
1516 (t & PERF_TXN_ABORT_MASK) >>
1517 PERF_TXN_ABORT_SHIFT);
1518 p += strlen(p);
1519 }
1520
1521 return repsep_snprintf(bf, size, "%-*s", width, buf);
1522}
1523
1524struct sort_entry sort_transaction = {
1525 .se_header = "Transaction ",
1526 .se_cmp = sort__transaction_cmp,
1527 .se_snprintf = hist_entry__transaction_snprintf,
1528 .se_width_idx = HISTC_TRANSACTION,
1529};
1530
1531
1532
1533static int64_t _sort__sym_size_cmp(struct symbol *sym_l, struct symbol *sym_r)
1534{
1535 int64_t size_l = sym_l != NULL ? symbol__size(sym_l) : 0;
1536 int64_t size_r = sym_r != NULL ? symbol__size(sym_r) : 0;
1537
1538 return size_l < size_r ? -1 :
1539 size_l == size_r ? 0 : 1;
1540}
1541
1542static int64_t
1543sort__sym_size_cmp(struct hist_entry *left, struct hist_entry *right)
1544{
1545 return _sort__sym_size_cmp(right->ms.sym, left->ms.sym);
1546}
1547
1548static int _hist_entry__sym_size_snprintf(struct symbol *sym, char *bf,
1549 size_t bf_size, unsigned int width)
1550{
1551 if (sym)
1552 return repsep_snprintf(bf, bf_size, "%*d", width, symbol__size(sym));
1553
1554 return repsep_snprintf(bf, bf_size, "%*s", width, "unknown");
1555}
1556
1557static int hist_entry__sym_size_snprintf(struct hist_entry *he, char *bf,
1558 size_t size, unsigned int width)
1559{
1560 return _hist_entry__sym_size_snprintf(he->ms.sym, bf, size, width);
1561}
1562
1563struct sort_entry sort_sym_size = {
1564 .se_header = "Symbol size",
1565 .se_cmp = sort__sym_size_cmp,
1566 .se_snprintf = hist_entry__sym_size_snprintf,
1567 .se_width_idx = HISTC_SYM_SIZE,
1568};
1569
1570
1571
1572static int64_t _sort__dso_size_cmp(struct map *map_l, struct map *map_r)
1573{
1574 int64_t size_l = map_l != NULL ? map__size(map_l) : 0;
1575 int64_t size_r = map_r != NULL ? map__size(map_r) : 0;
1576
1577 return size_l < size_r ? -1 :
1578 size_l == size_r ? 0 : 1;
1579}
1580
1581static int64_t
1582sort__dso_size_cmp(struct hist_entry *left, struct hist_entry *right)
1583{
1584 return _sort__dso_size_cmp(right->ms.map, left->ms.map);
1585}
1586
1587static int _hist_entry__dso_size_snprintf(struct map *map, char *bf,
1588 size_t bf_size, unsigned int width)
1589{
1590 if (map && map->dso)
1591 return repsep_snprintf(bf, bf_size, "%*d", width,
1592 map__size(map));
1593
1594 return repsep_snprintf(bf, bf_size, "%*s", width, "unknown");
1595}
1596
1597static int hist_entry__dso_size_snprintf(struct hist_entry *he, char *bf,
1598 size_t size, unsigned int width)
1599{
1600 return _hist_entry__dso_size_snprintf(he->ms.map, bf, size, width);
1601}
1602
1603struct sort_entry sort_dso_size = {
1604 .se_header = "DSO size",
1605 .se_cmp = sort__dso_size_cmp,
1606 .se_snprintf = hist_entry__dso_size_snprintf,
1607 .se_width_idx = HISTC_DSO_SIZE,
1608};
1609
1610
1611struct sort_dimension {
1612 const char *name;
1613 struct sort_entry *entry;
1614 int taken;
1615};
1616
1617#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
1618
1619static struct sort_dimension common_sort_dimensions[] = {
1620 DIM(SORT_PID, "pid", sort_thread),
1621 DIM(SORT_COMM, "comm", sort_comm),
1622 DIM(SORT_DSO, "dso", sort_dso),
1623 DIM(SORT_SYM, "symbol", sort_sym),
1624 DIM(SORT_PARENT, "parent", sort_parent),
1625 DIM(SORT_CPU, "cpu", sort_cpu),
1626 DIM(SORT_SOCKET, "socket", sort_socket),
1627 DIM(SORT_SRCLINE, "srcline", sort_srcline),
1628 DIM(SORT_SRCFILE, "srcfile", sort_srcfile),
1629 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
1630 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
1631 DIM(SORT_TRANSACTION, "transaction", sort_transaction),
1632 DIM(SORT_TRACE, "trace", sort_trace),
1633 DIM(SORT_SYM_SIZE, "symbol_size", sort_sym_size),
1634 DIM(SORT_DSO_SIZE, "dso_size", sort_dso_size),
1635 DIM(SORT_CGROUP_ID, "cgroup_id", sort_cgroup_id),
1636 DIM(SORT_SYM_IPC_NULL, "ipc_null", sort_sym_ipc_null),
1637};
1638
1639#undef DIM
1640
1641#define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
1642
1643static struct sort_dimension bstack_sort_dimensions[] = {
1644 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
1645 DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
1646 DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
1647 DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
1648 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
1649 DIM(SORT_IN_TX, "in_tx", sort_in_tx),
1650 DIM(SORT_ABORT, "abort", sort_abort),
1651 DIM(SORT_CYCLES, "cycles", sort_cycles),
1652 DIM(SORT_SRCLINE_FROM, "srcline_from", sort_srcline_from),
1653 DIM(SORT_SRCLINE_TO, "srcline_to", sort_srcline_to),
1654 DIM(SORT_SYM_IPC, "ipc_lbr", sort_sym_ipc),
1655};
1656
1657#undef DIM
1658
1659#define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
1660
1661static struct sort_dimension memory_sort_dimensions[] = {
1662 DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
1663 DIM(SORT_MEM_IADDR_SYMBOL, "symbol_iaddr", sort_mem_iaddr_sym),
1664 DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
1665 DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
1666 DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
1667 DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
1668 DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
1669 DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline),
1670 DIM(SORT_MEM_PHYS_DADDR, "phys_daddr", sort_mem_phys_daddr),
1671};
1672
1673#undef DIM
1674
1675struct hpp_dimension {
1676 const char *name;
1677 struct perf_hpp_fmt *fmt;
1678 int taken;
1679};
1680
1681#define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
1682
1683static struct hpp_dimension hpp_sort_dimensions[] = {
1684 DIM(PERF_HPP__OVERHEAD, "overhead"),
1685 DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"),
1686 DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
1687 DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
1688 DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
1689 DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"),
1690 DIM(PERF_HPP__SAMPLES, "sample"),
1691 DIM(PERF_HPP__PERIOD, "period"),
1692};
1693
1694#undef DIM
1695
1696struct hpp_sort_entry {
1697 struct perf_hpp_fmt hpp;
1698 struct sort_entry *se;
1699};
1700
1701void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists)
1702{
1703 struct hpp_sort_entry *hse;
1704
1705 if (!perf_hpp__is_sort_entry(fmt))
1706 return;
1707
1708 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1709 hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name));
1710}
1711
1712static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1713 struct hists *hists, int line __maybe_unused,
1714 int *span __maybe_unused)
1715{
1716 struct hpp_sort_entry *hse;
1717 size_t len = fmt->user_len;
1718
1719 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1720
1721 if (!len)
1722 len = hists__col_len(hists, hse->se->se_width_idx);
1723
1724 return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name);
1725}
1726
1727static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
1728 struct perf_hpp *hpp __maybe_unused,
1729 struct hists *hists)
1730{
1731 struct hpp_sort_entry *hse;
1732 size_t len = fmt->user_len;
1733
1734 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1735
1736 if (!len)
1737 len = hists__col_len(hists, hse->se->se_width_idx);
1738
1739 return len;
1740}
1741
1742static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1743 struct hist_entry *he)
1744{
1745 struct hpp_sort_entry *hse;
1746 size_t len = fmt->user_len;
1747
1748 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1749
1750 if (!len)
1751 len = hists__col_len(he->hists, hse->se->se_width_idx);
1752
1753 return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
1754}
1755
1756static int64_t __sort__hpp_cmp(struct perf_hpp_fmt *fmt,
1757 struct hist_entry *a, struct hist_entry *b)
1758{
1759 struct hpp_sort_entry *hse;
1760
1761 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1762 return hse->se->se_cmp(a, b);
1763}
1764
1765static int64_t __sort__hpp_collapse(struct perf_hpp_fmt *fmt,
1766 struct hist_entry *a, struct hist_entry *b)
1767{
1768 struct hpp_sort_entry *hse;
1769 int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
1770
1771 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1772 collapse_fn = hse->se->se_collapse ?: hse->se->se_cmp;
1773 return collapse_fn(a, b);
1774}
1775
1776static int64_t __sort__hpp_sort(struct perf_hpp_fmt *fmt,
1777 struct hist_entry *a, struct hist_entry *b)
1778{
1779 struct hpp_sort_entry *hse;
1780 int64_t (*sort_fn)(struct hist_entry *, struct hist_entry *);
1781
1782 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1783 sort_fn = hse->se->se_sort ?: hse->se->se_cmp;
1784 return sort_fn(a, b);
1785}
1786
1787bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
1788{
1789 return format->header == __sort__hpp_header;
1790}
1791
1792#define MK_SORT_ENTRY_CHK(key) \
1793bool perf_hpp__is_ ## key ## _entry(struct perf_hpp_fmt *fmt) \
1794{ \
1795 struct hpp_sort_entry *hse; \
1796 \
1797 if (!perf_hpp__is_sort_entry(fmt)) \
1798 return false; \
1799 \
1800 hse = container_of(fmt, struct hpp_sort_entry, hpp); \
1801 return hse->se == &sort_ ## key ; \
1802}
1803
1804MK_SORT_ENTRY_CHK(trace)
1805MK_SORT_ENTRY_CHK(srcline)
1806MK_SORT_ENTRY_CHK(srcfile)
1807MK_SORT_ENTRY_CHK(thread)
1808MK_SORT_ENTRY_CHK(comm)
1809MK_SORT_ENTRY_CHK(dso)
1810MK_SORT_ENTRY_CHK(sym)
1811
1812
1813static bool __sort__hpp_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1814{
1815 struct hpp_sort_entry *hse_a;
1816 struct hpp_sort_entry *hse_b;
1817
1818 if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
1819 return false;
1820
1821 hse_a = container_of(a, struct hpp_sort_entry, hpp);
1822 hse_b = container_of(b, struct hpp_sort_entry, hpp);
1823
1824 return hse_a->se == hse_b->se;
1825}
1826
1827static void hse_free(struct perf_hpp_fmt *fmt)
1828{
1829 struct hpp_sort_entry *hse;
1830
1831 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1832 free(hse);
1833}
1834
1835static struct hpp_sort_entry *
1836__sort_dimension__alloc_hpp(struct sort_dimension *sd, int level)
1837{
1838 struct hpp_sort_entry *hse;
1839
1840 hse = malloc(sizeof(*hse));
1841 if (hse == NULL) {
1842 pr_err("Memory allocation failed\n");
1843 return NULL;
1844 }
1845
1846 hse->se = sd->entry;
1847 hse->hpp.name = sd->entry->se_header;
1848 hse->hpp.header = __sort__hpp_header;
1849 hse->hpp.width = __sort__hpp_width;
1850 hse->hpp.entry = __sort__hpp_entry;
1851 hse->hpp.color = NULL;
1852
1853 hse->hpp.cmp = __sort__hpp_cmp;
1854 hse->hpp.collapse = __sort__hpp_collapse;
1855 hse->hpp.sort = __sort__hpp_sort;
1856 hse->hpp.equal = __sort__hpp_equal;
1857 hse->hpp.free = hse_free;
1858
1859 INIT_LIST_HEAD(&hse->hpp.list);
1860 INIT_LIST_HEAD(&hse->hpp.sort_list);
1861 hse->hpp.elide = false;
1862 hse->hpp.len = 0;
1863 hse->hpp.user_len = 0;
1864 hse->hpp.level = level;
1865
1866 return hse;
1867}
1868
1869static void hpp_free(struct perf_hpp_fmt *fmt)
1870{
1871 free(fmt);
1872}
1873
1874static struct perf_hpp_fmt *__hpp_dimension__alloc_hpp(struct hpp_dimension *hd,
1875 int level)
1876{
1877 struct perf_hpp_fmt *fmt;
1878
1879 fmt = memdup(hd->fmt, sizeof(*fmt));
1880 if (fmt) {
1881 INIT_LIST_HEAD(&fmt->list);
1882 INIT_LIST_HEAD(&fmt->sort_list);
1883 fmt->free = hpp_free;
1884 fmt->level = level;
1885 }
1886
1887 return fmt;
1888}
1889
1890int hist_entry__filter(struct hist_entry *he, int type, const void *arg)
1891{
1892 struct perf_hpp_fmt *fmt;
1893 struct hpp_sort_entry *hse;
1894 int ret = -1;
1895 int r;
1896
1897 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
1898 if (!perf_hpp__is_sort_entry(fmt))
1899 continue;
1900
1901 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1902 if (hse->se->se_filter == NULL)
1903 continue;
1904
1905
1906
1907
1908
1909 r = hse->se->se_filter(he, type, arg);
1910 if (r >= 0) {
1911 if (ret < 0)
1912 ret = 0;
1913 ret |= r;
1914 }
1915 }
1916
1917 return ret;
1918}
1919
1920static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd,
1921 struct perf_hpp_list *list,
1922 int level)
1923{
1924 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, level);
1925
1926 if (hse == NULL)
1927 return -1;
1928
1929 perf_hpp_list__register_sort_field(list, &hse->hpp);
1930 return 0;
1931}
1932
1933static int __sort_dimension__add_hpp_output(struct sort_dimension *sd,
1934 struct perf_hpp_list *list)
1935{
1936 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, 0);
1937
1938 if (hse == NULL)
1939 return -1;
1940
1941 perf_hpp_list__column_register(list, &hse->hpp);
1942 return 0;
1943}
1944
1945struct hpp_dynamic_entry {
1946 struct perf_hpp_fmt hpp;
1947 struct perf_evsel *evsel;
1948 struct tep_format_field *field;
1949 unsigned dynamic_len;
1950 bool raw_trace;
1951};
1952
1953static int hde_width(struct hpp_dynamic_entry *hde)
1954{
1955 if (!hde->hpp.len) {
1956 int len = hde->dynamic_len;
1957 int namelen = strlen(hde->field->name);
1958 int fieldlen = hde->field->size;
1959
1960 if (namelen > len)
1961 len = namelen;
1962
1963 if (!(hde->field->flags & TEP_FIELD_IS_STRING)) {
1964
1965 fieldlen = hde->field->size * 2 + 2;
1966 }
1967 if (fieldlen > len)
1968 len = fieldlen;
1969
1970 hde->hpp.len = len;
1971 }
1972 return hde->hpp.len;
1973}
1974
1975static void update_dynamic_len(struct hpp_dynamic_entry *hde,
1976 struct hist_entry *he)
1977{
1978 char *str, *pos;
1979 struct tep_format_field *field = hde->field;
1980 size_t namelen;
1981 bool last = false;
1982
1983 if (hde->raw_trace)
1984 return;
1985
1986
1987 if (!he->trace_output)
1988 he->trace_output = get_trace_output(he);
1989
1990 namelen = strlen(field->name);
1991 str = he->trace_output;
1992
1993 while (str) {
1994 pos = strchr(str, ' ');
1995 if (pos == NULL) {
1996 last = true;
1997 pos = str + strlen(str);
1998 }
1999
2000 if (!strncmp(str, field->name, namelen)) {
2001 size_t len;
2002
2003 str += namelen + 1;
2004 len = pos - str;
2005
2006 if (len > hde->dynamic_len)
2007 hde->dynamic_len = len;
2008 break;
2009 }
2010
2011 if (last)
2012 str = NULL;
2013 else
2014 str = pos + 1;
2015 }
2016}
2017
2018static int __sort__hde_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
2019 struct hists *hists __maybe_unused,
2020 int line __maybe_unused,
2021 int *span __maybe_unused)
2022{
2023 struct hpp_dynamic_entry *hde;
2024 size_t len = fmt->user_len;
2025
2026 hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
2027
2028 if (!len)
2029 len = hde_width(hde);
2030
2031 return scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, hde->field->name);
2032}
2033
2034static int __sort__hde_width(struct perf_hpp_fmt *fmt,
2035 struct perf_hpp *hpp __maybe_unused,
2036 struct hists *hists __maybe_unused)
2037{
2038 struct hpp_dynamic_entry *hde;
2039 size_t len = fmt->user_len;
2040
2041 hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
2042
2043 if (!len)
2044 len = hde_width(hde);
2045
2046 return len;
2047}
2048
2049bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *hists)
2050{
2051 struct hpp_dynamic_entry *hde;
2052
2053 hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
2054
2055 return hists_to_evsel(hists) == hde->evsel;
2056}
2057
2058static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
2059 struct hist_entry *he)
2060{
2061 struct hpp_dynamic_entry *hde;
2062 size_t len = fmt->user_len;
2063 char *str, *pos;
2064 struct tep_format_field *field;
2065 size_t namelen;
2066 bool last = false;
2067 int ret;
2068
2069 hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
2070
2071 if (!len)
2072 len = hde_width(hde);
2073
2074 if (hde->raw_trace)
2075 goto raw_field;
2076
2077 if (!he->trace_output)
2078 he->trace_output = get_trace_output(he);
2079
2080 field = hde->field;
2081 namelen = strlen(field->name);
2082 str = he->trace_output;
2083
2084 while (str) {
2085 pos = strchr(str, ' ');
2086 if (pos == NULL) {
2087 last = true;
2088 pos = str + strlen(str);
2089 }
2090
2091 if (!strncmp(str, field->name, namelen)) {
2092 str += namelen + 1;
2093 str = strndup(str, pos - str);
2094
2095 if (str == NULL)
2096 return scnprintf(hpp->buf, hpp->size,
2097 "%*.*s", len, len, "ERROR");
2098 break;
2099 }
2100
2101 if (last)
2102 str = NULL;
2103 else
2104 str = pos + 1;
2105 }
2106
2107 if (str == NULL) {
2108 struct trace_seq seq;
2109raw_field:
2110 trace_seq_init(&seq);
2111 tep_print_field(&seq, he->raw_data, hde->field);
2112 str = seq.buffer;
2113 }
2114
2115 ret = scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, str);
2116 free(str);
2117 return ret;
2118}
2119
2120static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt,
2121 struct hist_entry *a, struct hist_entry *b)
2122{
2123 struct hpp_dynamic_entry *hde;
2124 struct tep_format_field *field;
2125 unsigned offset, size;
2126
2127 hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
2128
2129 if (b == NULL) {
2130 update_dynamic_len(hde, a);
2131 return 0;
2132 }
2133
2134 field = hde->field;
2135 if (field->flags & TEP_FIELD_IS_DYNAMIC) {
2136 unsigned long long dyn;
2137
2138 tep_read_number_field(field, a->raw_data, &dyn);
2139 offset = dyn & 0xffff;
2140 size = (dyn >> 16) & 0xffff;
2141
2142
2143 if (size > hde->dynamic_len)
2144 hde->dynamic_len = size;
2145 } else {
2146 offset = field->offset;
2147 size = field->size;
2148 }
2149
2150 return memcmp(a->raw_data + offset, b->raw_data + offset, size);
2151}
2152
2153bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *fmt)
2154{
2155 return fmt->cmp == __sort__hde_cmp;
2156}
2157
2158static bool __sort__hde_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
2159{
2160 struct hpp_dynamic_entry *hde_a;
2161 struct hpp_dynamic_entry *hde_b;
2162
2163 if (!perf_hpp__is_dynamic_entry(a) || !perf_hpp__is_dynamic_entry(b))
2164 return false;
2165
2166 hde_a = container_of(a, struct hpp_dynamic_entry, hpp);
2167 hde_b = container_of(b, struct hpp_dynamic_entry, hpp);
2168
2169 return hde_a->field == hde_b->field;
2170}
2171
2172static void hde_free(struct perf_hpp_fmt *fmt)
2173{
2174 struct hpp_dynamic_entry *hde;
2175
2176 hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
2177 free(hde);
2178}
2179
2180static struct hpp_dynamic_entry *
2181__alloc_dynamic_entry(struct perf_evsel *evsel, struct tep_format_field *field,
2182 int level)
2183{
2184 struct hpp_dynamic_entry *hde;
2185
2186 hde = malloc(sizeof(*hde));
2187 if (hde == NULL) {
2188 pr_debug("Memory allocation failed\n");
2189 return NULL;
2190 }
2191
2192 hde->evsel = evsel;
2193 hde->field = field;
2194 hde->dynamic_len = 0;
2195
2196 hde->hpp.name = field->name;
2197 hde->hpp.header = __sort__hde_header;
2198 hde->hpp.width = __sort__hde_width;
2199 hde->hpp.entry = __sort__hde_entry;
2200 hde->hpp.color = NULL;
2201
2202 hde->hpp.cmp = __sort__hde_cmp;
2203 hde->hpp.collapse = __sort__hde_cmp;
2204 hde->hpp.sort = __sort__hde_cmp;
2205 hde->hpp.equal = __sort__hde_equal;
2206 hde->hpp.free = hde_free;
2207
2208 INIT_LIST_HEAD(&hde->hpp.list);
2209 INIT_LIST_HEAD(&hde->hpp.sort_list);
2210 hde->hpp.elide = false;
2211 hde->hpp.len = 0;
2212 hde->hpp.user_len = 0;
2213 hde->hpp.level = level;
2214
2215 return hde;
2216}
2217
2218struct perf_hpp_fmt *perf_hpp_fmt__dup(struct perf_hpp_fmt *fmt)
2219{
2220 struct perf_hpp_fmt *new_fmt = NULL;
2221
2222 if (perf_hpp__is_sort_entry(fmt)) {
2223 struct hpp_sort_entry *hse, *new_hse;
2224
2225 hse = container_of(fmt, struct hpp_sort_entry, hpp);
2226 new_hse = memdup(hse, sizeof(*hse));
2227 if (new_hse)
2228 new_fmt = &new_hse->hpp;
2229 } else if (perf_hpp__is_dynamic_entry(fmt)) {
2230 struct hpp_dynamic_entry *hde, *new_hde;
2231
2232 hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
2233 new_hde = memdup(hde, sizeof(*hde));
2234 if (new_hde)
2235 new_fmt = &new_hde->hpp;
2236 } else {
2237 new_fmt = memdup(fmt, sizeof(*fmt));
2238 }
2239
2240 INIT_LIST_HEAD(&new_fmt->list);
2241 INIT_LIST_HEAD(&new_fmt->sort_list);
2242
2243 return new_fmt;
2244}
2245
2246static int parse_field_name(char *str, char **event, char **field, char **opt)
2247{
2248 char *event_name, *field_name, *opt_name;
2249
2250 event_name = str;
2251 field_name = strchr(str, '.');
2252
2253 if (field_name) {
2254 *field_name++ = '\0';
2255 } else {
2256 event_name = NULL;
2257 field_name = str;
2258 }
2259
2260 opt_name = strchr(field_name, '/');
2261 if (opt_name)
2262 *opt_name++ = '\0';
2263
2264 *event = event_name;
2265 *field = field_name;
2266 *opt = opt_name;
2267
2268 return 0;
2269}
2270
2271
2272
2273
2274
2275
2276static struct perf_evsel *find_evsel(struct perf_evlist *evlist, char *event_name)
2277{
2278 struct perf_evsel *evsel = NULL;
2279 struct perf_evsel *pos;
2280 bool full_name;
2281
2282
2283 if (event_name[0] == '%') {
2284 int nr = strtol(event_name+1, NULL, 0);
2285
2286 if (nr > evlist->nr_entries)
2287 return NULL;
2288
2289 evsel = perf_evlist__first(evlist);
2290 while (--nr > 0)
2291 evsel = perf_evsel__next(evsel);
2292
2293 return evsel;
2294 }
2295
2296 full_name = !!strchr(event_name, ':');
2297 evlist__for_each_entry(evlist, pos) {
2298
2299 if (full_name && !strcmp(pos->name, event_name))
2300 return pos;
2301
2302 if (!full_name && strstr(pos->name, event_name)) {
2303 if (evsel) {
2304 pr_debug("'%s' event is ambiguous: it can be %s or %s\n",
2305 event_name, evsel->name, pos->name);
2306 return NULL;
2307 }
2308 evsel = pos;
2309 }
2310 }
2311
2312 return evsel;
2313}
2314
2315static int __dynamic_dimension__add(struct perf_evsel *evsel,
2316 struct tep_format_field *field,
2317 bool raw_trace, int level)
2318{
2319 struct hpp_dynamic_entry *hde;
2320
2321 hde = __alloc_dynamic_entry(evsel, field, level);
2322 if (hde == NULL)
2323 return -ENOMEM;
2324
2325 hde->raw_trace = raw_trace;
2326
2327 perf_hpp__register_sort_field(&hde->hpp);
2328 return 0;
2329}
2330
2331static int add_evsel_fields(struct perf_evsel *evsel, bool raw_trace, int level)
2332{
2333 int ret;
2334 struct tep_format_field *field;
2335
2336 field = evsel->tp_format->format.fields;
2337 while (field) {
2338 ret = __dynamic_dimension__add(evsel, field, raw_trace, level);
2339 if (ret < 0)
2340 return ret;
2341
2342 field = field->next;
2343 }
2344 return 0;
2345}
2346
2347static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace,
2348 int level)
2349{
2350 int ret;
2351 struct perf_evsel *evsel;
2352
2353 evlist__for_each_entry(evlist, evsel) {
2354 if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
2355 continue;
2356
2357 ret = add_evsel_fields(evsel, raw_trace, level);
2358 if (ret < 0)
2359 return ret;
2360 }
2361 return 0;
2362}
2363
2364static int add_all_matching_fields(struct perf_evlist *evlist,
2365 char *field_name, bool raw_trace, int level)
2366{
2367 int ret = -ESRCH;
2368 struct perf_evsel *evsel;
2369 struct tep_format_field *field;
2370
2371 evlist__for_each_entry(evlist, evsel) {
2372 if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
2373 continue;
2374
2375 field = tep_find_any_field(evsel->tp_format, field_name);
2376 if (field == NULL)
2377 continue;
2378
2379 ret = __dynamic_dimension__add(evsel, field, raw_trace, level);
2380 if (ret < 0)
2381 break;
2382 }
2383 return ret;
2384}
2385
2386static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok,
2387 int level)
2388{
2389 char *str, *event_name, *field_name, *opt_name;
2390 struct perf_evsel *evsel;
2391 struct tep_format_field *field;
2392 bool raw_trace = symbol_conf.raw_trace;
2393 int ret = 0;
2394
2395 if (evlist == NULL)
2396 return -ENOENT;
2397
2398 str = strdup(tok);
2399 if (str == NULL)
2400 return -ENOMEM;
2401
2402 if (parse_field_name(str, &event_name, &field_name, &opt_name) < 0) {
2403 ret = -EINVAL;
2404 goto out;
2405 }
2406
2407 if (opt_name) {
2408 if (strcmp(opt_name, "raw")) {
2409 pr_debug("unsupported field option %s\n", opt_name);
2410 ret = -EINVAL;
2411 goto out;
2412 }
2413 raw_trace = true;
2414 }
2415
2416 if (!strcmp(field_name, "trace_fields")) {
2417 ret = add_all_dynamic_fields(evlist, raw_trace, level);
2418 goto out;
2419 }
2420
2421 if (event_name == NULL) {
2422 ret = add_all_matching_fields(evlist, field_name, raw_trace, level);
2423 goto out;
2424 }
2425
2426 evsel = find_evsel(evlist, event_name);
2427 if (evsel == NULL) {
2428 pr_debug("Cannot find event: %s\n", event_name);
2429 ret = -ENOENT;
2430 goto out;
2431 }
2432
2433 if (evsel->attr.type != PERF_TYPE_TRACEPOINT) {
2434 pr_debug("%s is not a tracepoint event\n", event_name);
2435 ret = -EINVAL;
2436 goto out;
2437 }
2438
2439 if (!strcmp(field_name, "*")) {
2440 ret = add_evsel_fields(evsel, raw_trace, level);
2441 } else {
2442 field = tep_find_any_field(evsel->tp_format, field_name);
2443 if (field == NULL) {
2444 pr_debug("Cannot find event field for %s.%s\n",
2445 event_name, field_name);
2446 return -ENOENT;
2447 }
2448
2449 ret = __dynamic_dimension__add(evsel, field, raw_trace, level);
2450 }
2451
2452out:
2453 free(str);
2454 return ret;
2455}
2456
2457static int __sort_dimension__add(struct sort_dimension *sd,
2458 struct perf_hpp_list *list,
2459 int level)
2460{
2461 if (sd->taken)
2462 return 0;
2463
2464 if (__sort_dimension__add_hpp_sort(sd, list, level) < 0)
2465 return -1;
2466
2467 if (sd->entry->se_collapse)
2468 list->need_collapse = 1;
2469
2470 sd->taken = 1;
2471
2472 return 0;
2473}
2474
2475static int __hpp_dimension__add(struct hpp_dimension *hd,
2476 struct perf_hpp_list *list,
2477 int level)
2478{
2479 struct perf_hpp_fmt *fmt;
2480
2481 if (hd->taken)
2482 return 0;
2483
2484 fmt = __hpp_dimension__alloc_hpp(hd, level);
2485 if (!fmt)
2486 return -1;
2487
2488 hd->taken = 1;
2489 perf_hpp_list__register_sort_field(list, fmt);
2490 return 0;
2491}
2492
2493static int __sort_dimension__add_output(struct perf_hpp_list *list,
2494 struct sort_dimension *sd)
2495{
2496 if (sd->taken)
2497 return 0;
2498
2499 if (__sort_dimension__add_hpp_output(sd, list) < 0)
2500 return -1;
2501
2502 sd->taken = 1;
2503 return 0;
2504}
2505
2506static int __hpp_dimension__add_output(struct perf_hpp_list *list,
2507 struct hpp_dimension *hd)
2508{
2509 struct perf_hpp_fmt *fmt;
2510
2511 if (hd->taken)
2512 return 0;
2513
2514 fmt = __hpp_dimension__alloc_hpp(hd, 0);
2515 if (!fmt)
2516 return -1;
2517
2518 hd->taken = 1;
2519 perf_hpp_list__column_register(list, fmt);
2520 return 0;
2521}
2522
2523int hpp_dimension__add_output(unsigned col)
2524{
2525 BUG_ON(col >= PERF_HPP__MAX_INDEX);
2526 return __hpp_dimension__add_output(&perf_hpp_list, &hpp_sort_dimensions[col]);
2527}
2528
2529int sort_dimension__add(struct perf_hpp_list *list, const char *tok,
2530 struct perf_evlist *evlist,
2531 int level)
2532{
2533 unsigned int i;
2534
2535 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
2536 struct sort_dimension *sd = &common_sort_dimensions[i];
2537
2538 if (strncasecmp(tok, sd->name, strlen(tok)))
2539 continue;
2540
2541 if (sd->entry == &sort_parent) {
2542 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
2543 if (ret) {
2544 char err[BUFSIZ];
2545
2546 regerror(ret, &parent_regex, err, sizeof(err));
2547 pr_err("Invalid regex: %s\n%s", parent_pattern, err);
2548 return -EINVAL;
2549 }
2550 list->parent = 1;
2551 } else if (sd->entry == &sort_sym) {
2552 list->sym = 1;
2553
2554
2555
2556
2557
2558
2559 if (sort__mode == SORT_MODE__DIFF)
2560 sd->entry->se_collapse = sort__sym_sort;
2561
2562 } else if (sd->entry == &sort_dso) {
2563 list->dso = 1;
2564 } else if (sd->entry == &sort_socket) {
2565 list->socket = 1;
2566 } else if (sd->entry == &sort_thread) {
2567 list->thread = 1;
2568 } else if (sd->entry == &sort_comm) {
2569 list->comm = 1;
2570 }
2571
2572 return __sort_dimension__add(sd, list, level);
2573 }
2574
2575 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
2576 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
2577
2578 if (strncasecmp(tok, hd->name, strlen(tok)))
2579 continue;
2580
2581 return __hpp_dimension__add(hd, list, level);
2582 }
2583
2584 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
2585 struct sort_dimension *sd = &bstack_sort_dimensions[i];
2586
2587 if (strncasecmp(tok, sd->name, strlen(tok)))
2588 continue;
2589
2590 if (sort__mode != SORT_MODE__BRANCH)
2591 return -EINVAL;
2592
2593 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
2594 list->sym = 1;
2595
2596 __sort_dimension__add(sd, list, level);
2597 return 0;
2598 }
2599
2600 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
2601 struct sort_dimension *sd = &memory_sort_dimensions[i];
2602
2603 if (strncasecmp(tok, sd->name, strlen(tok)))
2604 continue;
2605
2606 if (sort__mode != SORT_MODE__MEMORY)
2607 return -EINVAL;
2608
2609 if (sd->entry == &sort_mem_dcacheline && cacheline_size() == 0)
2610 return -EINVAL;
2611
2612 if (sd->entry == &sort_mem_daddr_sym)
2613 list->sym = 1;
2614
2615 __sort_dimension__add(sd, list, level);
2616 return 0;
2617 }
2618
2619 if (!add_dynamic_entry(evlist, tok, level))
2620 return 0;
2621
2622 return -ESRCH;
2623}
2624
2625static int setup_sort_list(struct perf_hpp_list *list, char *str,
2626 struct perf_evlist *evlist)
2627{
2628 char *tmp, *tok;
2629 int ret = 0;
2630 int level = 0;
2631 int next_level = 1;
2632 bool in_group = false;
2633
2634 do {
2635 tok = str;
2636 tmp = strpbrk(str, "{}, ");
2637 if (tmp) {
2638 if (in_group)
2639 next_level = level;
2640 else
2641 next_level = level + 1;
2642
2643 if (*tmp == '{')
2644 in_group = true;
2645 else if (*tmp == '}')
2646 in_group = false;
2647
2648 *tmp = '\0';
2649 str = tmp + 1;
2650 }
2651
2652 if (*tok) {
2653 ret = sort_dimension__add(list, tok, evlist, level);
2654 if (ret == -EINVAL) {
2655 if (!cacheline_size() && !strncasecmp(tok, "dcacheline", strlen(tok)))
2656 pr_err("The \"dcacheline\" --sort key needs to know the cacheline size and it couldn't be determined on this system");
2657 else
2658 pr_err("Invalid --sort key: `%s'", tok);
2659 break;
2660 } else if (ret == -ESRCH) {
2661 pr_err("Unknown --sort key: `%s'", tok);
2662 break;
2663 }
2664 }
2665
2666 level = next_level;
2667 } while (tmp);
2668
2669 return ret;
2670}
2671
2672static const char *get_default_sort_order(struct perf_evlist *evlist)
2673{
2674 const char *default_sort_orders[] = {
2675 default_sort_order,
2676 default_branch_sort_order,
2677 default_mem_sort_order,
2678 default_top_sort_order,
2679 default_diff_sort_order,
2680 default_tracepoint_sort_order,
2681 };
2682 bool use_trace = true;
2683 struct perf_evsel *evsel;
2684
2685 BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
2686
2687 if (evlist == NULL || perf_evlist__empty(evlist))
2688 goto out_no_evlist;
2689
2690 evlist__for_each_entry(evlist, evsel) {
2691 if (evsel->attr.type != PERF_TYPE_TRACEPOINT) {
2692 use_trace = false;
2693 break;
2694 }
2695 }
2696
2697 if (use_trace) {
2698 sort__mode = SORT_MODE__TRACEPOINT;
2699 if (symbol_conf.raw_trace)
2700 return "trace_fields";
2701 }
2702out_no_evlist:
2703 return default_sort_orders[sort__mode];
2704}
2705
2706static int setup_sort_order(struct perf_evlist *evlist)
2707{
2708 char *new_sort_order;
2709
2710
2711
2712
2713
2714 if (!sort_order || is_strict_order(sort_order))
2715 return 0;
2716
2717 if (sort_order[1] == '\0') {
2718 pr_err("Invalid --sort key: `+'");
2719 return -EINVAL;
2720 }
2721
2722
2723
2724
2725
2726 if (asprintf(&new_sort_order, "%s,%s",
2727 get_default_sort_order(evlist), sort_order + 1) < 0) {
2728 pr_err("Not enough memory to set up --sort");
2729 return -ENOMEM;
2730 }
2731
2732 sort_order = new_sort_order;
2733 return 0;
2734}
2735
2736
2737
2738
2739
2740static char *prefix_if_not_in(const char *pre, char *str)
2741{
2742 char *n;
2743
2744 if (!str || strstr(str, pre))
2745 return str;
2746
2747 if (asprintf(&n, "%s,%s", pre, str) < 0)
2748 return NULL;
2749
2750 free(str);
2751 return n;
2752}
2753
2754static char *setup_overhead(char *keys)
2755{
2756 if (sort__mode == SORT_MODE__DIFF)
2757 return keys;
2758
2759 keys = prefix_if_not_in("overhead", keys);
2760
2761 if (symbol_conf.cumulate_callchain)
2762 keys = prefix_if_not_in("overhead_children", keys);
2763
2764 return keys;
2765}
2766
2767static int __setup_sorting(struct perf_evlist *evlist)
2768{
2769 char *str;
2770 const char *sort_keys;
2771 int ret = 0;
2772
2773 ret = setup_sort_order(evlist);
2774 if (ret)
2775 return ret;
2776
2777 sort_keys = sort_order;
2778 if (sort_keys == NULL) {
2779 if (is_strict_order(field_order)) {
2780
2781
2782
2783
2784 return 0;
2785 }
2786
2787 sort_keys = get_default_sort_order(evlist);
2788 }
2789
2790 str = strdup(sort_keys);
2791 if (str == NULL) {
2792 pr_err("Not enough memory to setup sort keys");
2793 return -ENOMEM;
2794 }
2795
2796
2797
2798
2799 if (!is_strict_order(field_order)) {
2800 str = setup_overhead(str);
2801 if (str == NULL) {
2802 pr_err("Not enough memory to setup overhead keys");
2803 return -ENOMEM;
2804 }
2805 }
2806
2807 ret = setup_sort_list(&perf_hpp_list, str, evlist);
2808
2809 free(str);
2810 return ret;
2811}
2812
2813void perf_hpp__set_elide(int idx, bool elide)
2814{
2815 struct perf_hpp_fmt *fmt;
2816 struct hpp_sort_entry *hse;
2817
2818 perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
2819 if (!perf_hpp__is_sort_entry(fmt))
2820 continue;
2821
2822 hse = container_of(fmt, struct hpp_sort_entry, hpp);
2823 if (hse->se->se_width_idx == idx) {
2824 fmt->elide = elide;
2825 break;
2826 }
2827 }
2828}
2829
2830static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)
2831{
2832 if (list && strlist__nr_entries(list) == 1) {
2833 if (fp != NULL)
2834 fprintf(fp, "# %s: %s\n", list_name,
2835 strlist__entry(list, 0)->s);
2836 return true;
2837 }
2838 return false;
2839}
2840
2841static bool get_elide(int idx, FILE *output)
2842{
2843 switch (idx) {
2844 case HISTC_SYMBOL:
2845 return __get_elide(symbol_conf.sym_list, "symbol", output);
2846 case HISTC_DSO:
2847 return __get_elide(symbol_conf.dso_list, "dso", output);
2848 case HISTC_COMM:
2849 return __get_elide(symbol_conf.comm_list, "comm", output);
2850 default:
2851 break;
2852 }
2853
2854 if (sort__mode != SORT_MODE__BRANCH)
2855 return false;
2856
2857 switch (idx) {
2858 case HISTC_SYMBOL_FROM:
2859 return __get_elide(symbol_conf.sym_from_list, "sym_from", output);
2860 case HISTC_SYMBOL_TO:
2861 return __get_elide(symbol_conf.sym_to_list, "sym_to", output);
2862 case HISTC_DSO_FROM:
2863 return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
2864 case HISTC_DSO_TO:
2865 return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
2866 default:
2867 break;
2868 }
2869
2870 return false;
2871}
2872
2873void sort__setup_elide(FILE *output)
2874{
2875 struct perf_hpp_fmt *fmt;
2876 struct hpp_sort_entry *hse;
2877
2878 perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
2879 if (!perf_hpp__is_sort_entry(fmt))
2880 continue;
2881
2882 hse = container_of(fmt, struct hpp_sort_entry, hpp);
2883 fmt->elide = get_elide(hse->se->se_width_idx, output);
2884 }
2885
2886
2887
2888
2889
2890 perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
2891 if (!perf_hpp__is_sort_entry(fmt))
2892 continue;
2893
2894 if (!fmt->elide)
2895 return;
2896 }
2897
2898 perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
2899 if (!perf_hpp__is_sort_entry(fmt))
2900 continue;
2901
2902 fmt->elide = false;
2903 }
2904}
2905
2906int output_field_add(struct perf_hpp_list *list, char *tok)
2907{
2908 unsigned int i;
2909
2910 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
2911 struct sort_dimension *sd = &common_sort_dimensions[i];
2912
2913 if (strncasecmp(tok, sd->name, strlen(tok)))
2914 continue;
2915
2916 return __sort_dimension__add_output(list, sd);
2917 }
2918
2919 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
2920 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
2921
2922 if (strncasecmp(tok, hd->name, strlen(tok)))
2923 continue;
2924
2925 return __hpp_dimension__add_output(list, hd);
2926 }
2927
2928 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
2929 struct sort_dimension *sd = &bstack_sort_dimensions[i];
2930
2931 if (strncasecmp(tok, sd->name, strlen(tok)))
2932 continue;
2933
2934 return __sort_dimension__add_output(list, sd);
2935 }
2936
2937 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
2938 struct sort_dimension *sd = &memory_sort_dimensions[i];
2939
2940 if (strncasecmp(tok, sd->name, strlen(tok)))
2941 continue;
2942
2943 return __sort_dimension__add_output(list, sd);
2944 }
2945
2946 return -ESRCH;
2947}
2948
2949static int setup_output_list(struct perf_hpp_list *list, char *str)
2950{
2951 char *tmp, *tok;
2952 int ret = 0;
2953
2954 for (tok = strtok_r(str, ", ", &tmp);
2955 tok; tok = strtok_r(NULL, ", ", &tmp)) {
2956 ret = output_field_add(list, tok);
2957 if (ret == -EINVAL) {
2958 ui__error("Invalid --fields key: `%s'", tok);
2959 break;
2960 } else if (ret == -ESRCH) {
2961 ui__error("Unknown --fields key: `%s'", tok);
2962 break;
2963 }
2964 }
2965
2966 return ret;
2967}
2968
2969void reset_dimensions(void)
2970{
2971 unsigned int i;
2972
2973 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++)
2974 common_sort_dimensions[i].taken = 0;
2975
2976 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++)
2977 hpp_sort_dimensions[i].taken = 0;
2978
2979 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++)
2980 bstack_sort_dimensions[i].taken = 0;
2981
2982 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++)
2983 memory_sort_dimensions[i].taken = 0;
2984}
2985
2986bool is_strict_order(const char *order)
2987{
2988 return order && (*order != '+');
2989}
2990
2991static int __setup_output_field(void)
2992{
2993 char *str, *strp;
2994 int ret = -EINVAL;
2995
2996 if (field_order == NULL)
2997 return 0;
2998
2999 strp = str = strdup(field_order);
3000 if (str == NULL) {
3001 pr_err("Not enough memory to setup output fields");
3002 return -ENOMEM;
3003 }
3004
3005 if (!is_strict_order(field_order))
3006 strp++;
3007
3008 if (!strlen(strp)) {
3009 pr_err("Invalid --fields key: `+'");
3010 goto out;
3011 }
3012
3013 ret = setup_output_list(&perf_hpp_list, strp);
3014
3015out:
3016 free(str);
3017 return ret;
3018}
3019
3020int setup_sorting(struct perf_evlist *evlist)
3021{
3022 int err;
3023
3024 err = __setup_sorting(evlist);
3025 if (err < 0)
3026 return err;
3027
3028 if (parent_pattern != default_parent_pattern) {
3029 err = sort_dimension__add(&perf_hpp_list, "parent", evlist, -1);
3030 if (err < 0)
3031 return err;
3032 }
3033
3034 reset_dimensions();
3035
3036
3037
3038
3039 if (sort__mode != SORT_MODE__DIFF)
3040 perf_hpp__init();
3041
3042 err = __setup_output_field();
3043 if (err < 0)
3044 return err;
3045
3046
3047 perf_hpp__setup_output_field(&perf_hpp_list);
3048
3049 perf_hpp__append_sort_keys(&perf_hpp_list);
3050
3051
3052 if (perf_hpp__setup_hists_formats(&perf_hpp_list, evlist) < 0)
3053 return -1;
3054
3055 return 0;
3056}
3057
3058void reset_output_field(void)
3059{
3060 perf_hpp_list.need_collapse = 0;
3061 perf_hpp_list.parent = 0;
3062 perf_hpp_list.sym = 0;
3063 perf_hpp_list.dso = 0;
3064
3065 field_order = NULL;
3066 sort_order = NULL;
3067
3068 reset_dimensions();
3069 perf_hpp__reset_output_field(&perf_hpp_list);
3070}
3071