]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blame - tools/perf/util/sort.c
perf tools: Add support to dynamically get cacheline size
[mirror_ubuntu-focal-kernel.git] / tools / perf / util / sort.c
CommitLineData
dd68ada2 1#include "sort.h"
8a6c5b26 2#include "hist.h"
4dfced35 3#include "comm.h"
08e71542 4#include "symbol.h"
8b536999 5#include "evsel.h"
dd68ada2
JK
6
7regex_t parent_regex;
edb7c60e
ACM
8const char default_parent_pattern[] = "^sys_|^do_page_fault";
9const char *parent_pattern = default_parent_pattern;
10const char default_sort_order[] = "comm,dso,symbol";
512ae1bd
NK
11const char default_branch_sort_order[] = "comm,dso_from,symbol_from,dso_to,symbol_to";
12const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
13const char default_top_sort_order[] = "dso,symbol";
14const char default_diff_sort_order[] = "dso,symbol";
15const char *sort_order;
a7d945bc 16const char *field_order;
b21484f1
GP
17regex_t ignore_callees_regex;
18int have_ignore_callees = 0;
af0a6fa4
FW
19int sort__need_collapse = 0;
20int sort__has_parent = 0;
1af55640 21int sort__has_sym = 0;
68f6d022 22int sort__has_dso = 0;
55369fc1 23enum sort_mode sort__mode = SORT_MODE__NORMAL;
a4fb581b 24
dd68ada2 25
a4e3b956 26static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
dd68ada2
JK
27{
28 int n;
29 va_list ap;
30
31 va_start(ap, fmt);
a4e3b956 32 n = vsnprintf(bf, size, fmt, ap);
0ca0c130 33 if (symbol_conf.field_sep && n > 0) {
a4e3b956
ACM
34 char *sep = bf;
35
36 while (1) {
0ca0c130 37 sep = strchr(sep, *symbol_conf.field_sep);
a4e3b956
ACM
38 if (sep == NULL)
39 break;
40 *sep = '.';
dd68ada2 41 }
dd68ada2
JK
42 }
43 va_end(ap);
b832796c
AB
44
45 if (n >= (int)size)
46 return size - 1;
dd68ada2
JK
47 return n;
48}
49
b9c5143a 50static int64_t cmp_null(const void *l, const void *r)
872a878f
FW
51{
52 if (!l && !r)
53 return 0;
54 else if (!l)
55 return -1;
56 else
57 return 1;
58}
59
60/* --sort pid */
61
62static int64_t
63sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
64{
38051234 65 return right->thread->tid - left->thread->tid;
872a878f
FW
66}
67
c824c433 68static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,
a4e3b956 69 size_t size, unsigned int width)
dd68ada2 70{
b9c5143a 71 const char *comm = thread__comm_str(he->thread);
fb29a338 72 return repsep_snprintf(bf, size, "%*s:%5d", width - 6,
b9c5143a 73 comm ?: "", he->thread->tid);
dd68ada2
JK
74}
75
872a878f
FW
76struct sort_entry sort_thread = {
77 .se_header = "Command: Pid",
78 .se_cmp = sort__thread_cmp,
79 .se_snprintf = hist_entry__thread_snprintf,
80 .se_width_idx = HISTC_THREAD,
81};
82
83/* --sort comm */
84
85static int64_t
86sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
87{
fedd63d3 88 /* Compare the addr that should be unique among comm */
4dfced35 89 return comm__str(right->comm) - comm__str(left->comm);
872a878f
FW
90}
91
92static int64_t
93sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
94{
4dfced35
NK
95 /* Compare the addr that should be unique among comm */
96 return comm__str(right->comm) - comm__str(left->comm);
872a878f
FW
97}
98
202e7a6d
NK
99static int64_t
100sort__comm_sort(struct hist_entry *left, struct hist_entry *right)
101{
102 return strcmp(comm__str(right->comm), comm__str(left->comm));
103}
104
c824c433 105static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
a4e3b956 106 size_t size, unsigned int width)
dd68ada2 107{
4dfced35 108 return repsep_snprintf(bf, size, "%*s", width, comm__str(he->comm));
dd68ada2
JK
109}
110
14d1ac74
NK
111struct sort_entry sort_comm = {
112 .se_header = "Command",
113 .se_cmp = sort__comm_cmp,
114 .se_collapse = sort__comm_collapse,
202e7a6d 115 .se_sort = sort__comm_sort,
14d1ac74
NK
116 .se_snprintf = hist_entry__comm_snprintf,
117 .se_width_idx = HISTC_COMM,
118};
119
120/* --sort dso */
121
b5387528
RAV
122static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
123{
124 struct dso *dso_l = map_l ? map_l->dso : NULL;
125 struct dso *dso_r = map_r ? map_r->dso : NULL;
126 const char *dso_name_l, *dso_name_r;
127
128 if (!dso_l || !dso_r)
202e7a6d 129 return cmp_null(dso_r, dso_l);
b5387528
RAV
130
131 if (verbose) {
132 dso_name_l = dso_l->long_name;
133 dso_name_r = dso_r->long_name;
134 } else {
135 dso_name_l = dso_l->short_name;
136 dso_name_r = dso_r->short_name;
137 }
138
139 return strcmp(dso_name_l, dso_name_r);
140}
141
872a878f 142static int64_t
dd68ada2
JK
143sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
144{
202e7a6d 145 return _sort__dso_cmp(right->ms.map, left->ms.map);
b5387528 146}
dd68ada2 147
14d1ac74
NK
148static int _hist_entry__dso_snprintf(struct map *map, char *bf,
149 size_t size, unsigned int width)
150{
151 if (map && map->dso) {
152 const char *dso_name = !verbose ? map->dso->short_name :
153 map->dso->long_name;
154 return repsep_snprintf(bf, size, "%-*s", width, dso_name);
155 }
156
157 return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
158}
159
c824c433 160static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,
14d1ac74
NK
161 size_t size, unsigned int width)
162{
c824c433 163 return _hist_entry__dso_snprintf(he->ms.map, bf, size, width);
14d1ac74
NK
164}
165
166struct sort_entry sort_dso = {
167 .se_header = "Shared Object",
168 .se_cmp = sort__dso_cmp,
169 .se_snprintf = hist_entry__dso_snprintf,
170 .se_width_idx = HISTC_DSO,
171};
172
173/* --sort symbol */
dd68ada2 174
2037be53
NK
175static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip)
176{
177 return (int64_t)(right_ip - left_ip);
178}
179
51f27d14 180static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
b5387528 181{
51f27d14
NK
182 u64 ip_l, ip_r;
183
b5387528
RAV
184 if (!sym_l || !sym_r)
185 return cmp_null(sym_l, sym_r);
186
187 if (sym_l == sym_r)
188 return 0;
189
53985a7b
SL
190 ip_l = sym_l->start;
191 ip_r = sym_r->start;
b5387528
RAV
192
193 return (int64_t)(ip_r - ip_l);
194}
195
14d1ac74
NK
196static int64_t
197sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
b5387528 198{
09600e0f
NK
199 int64_t ret;
200
14d1ac74 201 if (!left->ms.sym && !right->ms.sym)
2037be53 202 return _sort__addr_cmp(left->ip, right->ip);
dd68ada2 203
09600e0f
NK
204 /*
205 * comparing symbol address alone is not enough since it's a
206 * relative address within a dso.
207 */
68f6d022
NK
208 if (!sort__has_dso) {
209 ret = sort__dso_cmp(left, right);
210 if (ret != 0)
211 return ret;
212 }
09600e0f 213
51f27d14 214 return _sort__sym_cmp(left->ms.sym, right->ms.sym);
b5387528
RAV
215}
216
202e7a6d
NK
217static int64_t
218sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
219{
220 if (!left->ms.sym || !right->ms.sym)
221 return cmp_null(left->ms.sym, right->ms.sym);
222
223 return strcmp(right->ms.sym->name, left->ms.sym->name);
224}
225
b5387528
RAV
226static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
227 u64 ip, char level, char *bf, size_t size,
43355522 228 unsigned int width)
b5387528
RAV
229{
230 size_t ret = 0;
231
232 if (verbose) {
233 char o = map ? dso__symtab_origin(map->dso) : '!';
234 ret += repsep_snprintf(bf, size, "%-#*llx %c ",
ded19d57 235 BITS_PER_LONG / 4 + 2, ip, o);
439d473b 236 }
dd68ada2 237
b5387528 238 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
98a3b32c
SE
239 if (sym && map) {
240 if (map->type == MAP__VARIABLE) {
241 ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
242 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
62667746 243 ip - map->unmap_ip(map, sym->start));
98a3b32c
SE
244 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
245 width - ret, "");
246 } else {
247 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
248 width - ret,
249 sym->name);
250 }
251 } else {
b5387528
RAV
252 size_t len = BITS_PER_LONG / 4;
253 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
254 len, ip);
255 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
256 width - ret, "");
257 }
258
259 return ret;
dd68ada2
JK
260}
261
c824c433 262static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,
43355522 263 size_t size, unsigned int width)
b5387528 264{
c824c433
ACM
265 return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip,
266 he->level, bf, size, width);
b5387528 267}
dd68ada2 268
872a878f
FW
269struct sort_entry sort_sym = {
270 .se_header = "Symbol",
271 .se_cmp = sort__sym_cmp,
202e7a6d 272 .se_sort = sort__sym_sort,
872a878f
FW
273 .se_snprintf = hist_entry__sym_snprintf,
274 .se_width_idx = HISTC_SYMBOL,
275};
dd68ada2 276
409a8be6
ACM
277/* --sort srcline */
278
279static int64_t
280sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
281{
4adcc430
NK
282 if (!left->srcline) {
283 if (!left->ms.map)
284 left->srcline = SRCLINE_UNKNOWN;
285 else {
286 struct map *map = left->ms.map;
287 left->srcline = get_srcline(map->dso,
288 map__rip_2objdump(map, left->ip));
289 }
290 }
291 if (!right->srcline) {
292 if (!right->ms.map)
293 right->srcline = SRCLINE_UNKNOWN;
294 else {
295 struct map *map = right->ms.map;
296 right->srcline = get_srcline(map->dso,
297 map__rip_2objdump(map, right->ip));
298 }
299 }
202e7a6d 300 return strcmp(right->srcline, left->srcline);
409a8be6
ACM
301}
302
c824c433 303static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
1d037ca1
IT
304 size_t size,
305 unsigned int width __maybe_unused)
409a8be6 306{
c824c433 307 return repsep_snprintf(bf, size, "%s", he->srcline);
409a8be6
ACM
308}
309
310struct sort_entry sort_srcline = {
311 .se_header = "Source:Line",
312 .se_cmp = sort__srcline_cmp,
313 .se_snprintf = hist_entry__srcline_snprintf,
314 .se_width_idx = HISTC_SRCLINE,
315};
316
dd68ada2
JK
317/* --sort parent */
318
872a878f 319static int64_t
dd68ada2
JK
320sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
321{
322 struct symbol *sym_l = left->parent;
323 struct symbol *sym_r = right->parent;
324
325 if (!sym_l || !sym_r)
326 return cmp_null(sym_l, sym_r);
327
202e7a6d 328 return strcmp(sym_r->name, sym_l->name);
dd68ada2
JK
329}
330
c824c433 331static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
a4e3b956 332 size_t size, unsigned int width)
dd68ada2 333{
a4e3b956 334 return repsep_snprintf(bf, size, "%-*s", width,
c824c433 335 he->parent ? he->parent->name : "[other]");
dd68ada2
JK
336}
337
872a878f
FW
338struct sort_entry sort_parent = {
339 .se_header = "Parent symbol",
340 .se_cmp = sort__parent_cmp,
341 .se_snprintf = hist_entry__parent_snprintf,
342 .se_width_idx = HISTC_PARENT,
343};
344
f60f3593
AS
345/* --sort cpu */
346
872a878f 347static int64_t
f60f3593
AS
348sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
349{
350 return right->cpu - left->cpu;
351}
352
c824c433
ACM
353static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf,
354 size_t size, unsigned int width)
f60f3593 355{
c824c433 356 return repsep_snprintf(bf, size, "%*d", width, he->cpu);
f60f3593
AS
357}
358
872a878f
FW
359struct sort_entry sort_cpu = {
360 .se_header = "CPU",
361 .se_cmp = sort__cpu_cmp,
362 .se_snprintf = hist_entry__cpu_snprintf,
363 .se_width_idx = HISTC_CPU,
364};
365
14d1ac74
NK
366/* sort keys for branch stacks */
367
b5387528
RAV
368static int64_t
369sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
370{
371 return _sort__dso_cmp(left->branch_info->from.map,
372 right->branch_info->from.map);
373}
374
c824c433 375static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,
b5387528
RAV
376 size_t size, unsigned int width)
377{
c824c433 378 return _hist_entry__dso_snprintf(he->branch_info->from.map,
b5387528
RAV
379 bf, size, width);
380}
381
b5387528
RAV
382static int64_t
383sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
384{
385 return _sort__dso_cmp(left->branch_info->to.map,
386 right->branch_info->to.map);
387}
388
c824c433 389static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,
b5387528
RAV
390 size_t size, unsigned int width)
391{
c824c433 392 return _hist_entry__dso_snprintf(he->branch_info->to.map,
b5387528
RAV
393 bf, size, width);
394}
395
396static int64_t
397sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
398{
399 struct addr_map_symbol *from_l = &left->branch_info->from;
400 struct addr_map_symbol *from_r = &right->branch_info->from;
401
402 if (!from_l->sym && !from_r->sym)
2037be53 403 return _sort__addr_cmp(from_l->addr, from_r->addr);
b5387528 404
51f27d14 405 return _sort__sym_cmp(from_l->sym, from_r->sym);
b5387528
RAV
406}
407
408static int64_t
409sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
410{
411 struct addr_map_symbol *to_l = &left->branch_info->to;
412 struct addr_map_symbol *to_r = &right->branch_info->to;
413
414 if (!to_l->sym && !to_r->sym)
2037be53 415 return _sort__addr_cmp(to_l->addr, to_r->addr);
b5387528 416
51f27d14 417 return _sort__sym_cmp(to_l->sym, to_r->sym);
b5387528
RAV
418}
419
c824c433 420static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,
43355522 421 size_t size, unsigned int width)
b5387528 422{
c824c433 423 struct addr_map_symbol *from = &he->branch_info->from;
b5387528 424 return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
c824c433 425 he->level, bf, size, width);
b5387528
RAV
426
427}
428
c824c433 429static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,
43355522 430 size_t size, unsigned int width)
b5387528 431{
c824c433 432 struct addr_map_symbol *to = &he->branch_info->to;
b5387528 433 return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
c824c433 434 he->level, bf, size, width);
b5387528
RAV
435
436}
437
14d1ac74
NK
438struct sort_entry sort_dso_from = {
439 .se_header = "Source Shared Object",
440 .se_cmp = sort__dso_from_cmp,
441 .se_snprintf = hist_entry__dso_from_snprintf,
442 .se_width_idx = HISTC_DSO_FROM,
443};
444
b5387528
RAV
445struct sort_entry sort_dso_to = {
446 .se_header = "Target Shared Object",
447 .se_cmp = sort__dso_to_cmp,
448 .se_snprintf = hist_entry__dso_to_snprintf,
449 .se_width_idx = HISTC_DSO_TO,
450};
451
452struct sort_entry sort_sym_from = {
453 .se_header = "Source Symbol",
454 .se_cmp = sort__sym_from_cmp,
455 .se_snprintf = hist_entry__sym_from_snprintf,
456 .se_width_idx = HISTC_SYMBOL_FROM,
457};
458
459struct sort_entry sort_sym_to = {
460 .se_header = "Target Symbol",
461 .se_cmp = sort__sym_to_cmp,
462 .se_snprintf = hist_entry__sym_to_snprintf,
463 .se_width_idx = HISTC_SYMBOL_TO,
464};
465
466static int64_t
467sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
468{
469 const unsigned char mp = left->branch_info->flags.mispred !=
470 right->branch_info->flags.mispred;
471 const unsigned char p = left->branch_info->flags.predicted !=
472 right->branch_info->flags.predicted;
473
474 return mp || p;
475}
476
c824c433 477static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,
b5387528
RAV
478 size_t size, unsigned int width){
479 static const char *out = "N/A";
480
c824c433 481 if (he->branch_info->flags.predicted)
b5387528 482 out = "N";
c824c433 483 else if (he->branch_info->flags.mispred)
b5387528
RAV
484 out = "Y";
485
486 return repsep_snprintf(bf, size, "%-*s", width, out);
487}
488
98a3b32c
SE
489/* --sort daddr_sym */
490static int64_t
491sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
492{
493 uint64_t l = 0, r = 0;
494
495 if (left->mem_info)
496 l = left->mem_info->daddr.addr;
497 if (right->mem_info)
498 r = right->mem_info->daddr.addr;
499
500 return (int64_t)(r - l);
501}
502
c824c433 503static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,
98a3b32c
SE
504 size_t size, unsigned int width)
505{
506 uint64_t addr = 0;
507 struct map *map = NULL;
508 struct symbol *sym = NULL;
509
c824c433
ACM
510 if (he->mem_info) {
511 addr = he->mem_info->daddr.addr;
512 map = he->mem_info->daddr.map;
513 sym = he->mem_info->daddr.sym;
98a3b32c 514 }
c824c433 515 return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
98a3b32c
SE
516 width);
517}
518
519static int64_t
520sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
521{
522 struct map *map_l = NULL;
523 struct map *map_r = NULL;
524
525 if (left->mem_info)
526 map_l = left->mem_info->daddr.map;
527 if (right->mem_info)
528 map_r = right->mem_info->daddr.map;
529
530 return _sort__dso_cmp(map_l, map_r);
531}
532
c824c433 533static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,
98a3b32c
SE
534 size_t size, unsigned int width)
535{
536 struct map *map = NULL;
537
c824c433
ACM
538 if (he->mem_info)
539 map = he->mem_info->daddr.map;
98a3b32c
SE
540
541 return _hist_entry__dso_snprintf(map, bf, size, width);
542}
543
544static int64_t
545sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
546{
547 union perf_mem_data_src data_src_l;
548 union perf_mem_data_src data_src_r;
549
550 if (left->mem_info)
551 data_src_l = left->mem_info->data_src;
552 else
553 data_src_l.mem_lock = PERF_MEM_LOCK_NA;
554
555 if (right->mem_info)
556 data_src_r = right->mem_info->data_src;
557 else
558 data_src_r.mem_lock = PERF_MEM_LOCK_NA;
559
560 return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
561}
562
c824c433 563static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,
98a3b32c
SE
564 size_t size, unsigned int width)
565{
566 const char *out;
567 u64 mask = PERF_MEM_LOCK_NA;
568
c824c433
ACM
569 if (he->mem_info)
570 mask = he->mem_info->data_src.mem_lock;
98a3b32c
SE
571
572 if (mask & PERF_MEM_LOCK_NA)
573 out = "N/A";
574 else if (mask & PERF_MEM_LOCK_LOCKED)
575 out = "Yes";
576 else
577 out = "No";
578
579 return repsep_snprintf(bf, size, "%-*s", width, out);
580}
581
582static int64_t
583sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
584{
585 union perf_mem_data_src data_src_l;
586 union perf_mem_data_src data_src_r;
587
588 if (left->mem_info)
589 data_src_l = left->mem_info->data_src;
590 else
591 data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
592
593 if (right->mem_info)
594 data_src_r = right->mem_info->data_src;
595 else
596 data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
597
598 return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
599}
600
601static const char * const tlb_access[] = {
602 "N/A",
603 "HIT",
604 "MISS",
605 "L1",
606 "L2",
607 "Walker",
608 "Fault",
609};
610#define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
611
c824c433 612static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,
98a3b32c
SE
613 size_t size, unsigned int width)
614{
615 char out[64];
616 size_t sz = sizeof(out) - 1; /* -1 for null termination */
617 size_t l = 0, i;
618 u64 m = PERF_MEM_TLB_NA;
619 u64 hit, miss;
620
621 out[0] = '\0';
622
c824c433
ACM
623 if (he->mem_info)
624 m = he->mem_info->data_src.mem_dtlb;
98a3b32c
SE
625
626 hit = m & PERF_MEM_TLB_HIT;
627 miss = m & PERF_MEM_TLB_MISS;
628
629 /* already taken care of */
630 m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
631
632 for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) {
633 if (!(m & 0x1))
634 continue;
635 if (l) {
636 strcat(out, " or ");
637 l += 4;
638 }
639 strncat(out, tlb_access[i], sz - l);
640 l += strlen(tlb_access[i]);
641 }
642 if (*out == '\0')
643 strcpy(out, "N/A");
644 if (hit)
645 strncat(out, " hit", sz - l);
646 if (miss)
647 strncat(out, " miss", sz - l);
648
649 return repsep_snprintf(bf, size, "%-*s", width, out);
650}
651
652static int64_t
653sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
654{
655 union perf_mem_data_src data_src_l;
656 union perf_mem_data_src data_src_r;
657
658 if (left->mem_info)
659 data_src_l = left->mem_info->data_src;
660 else
661 data_src_l.mem_lvl = PERF_MEM_LVL_NA;
662
663 if (right->mem_info)
664 data_src_r = right->mem_info->data_src;
665 else
666 data_src_r.mem_lvl = PERF_MEM_LVL_NA;
667
668 return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
669}
670
671static const char * const mem_lvl[] = {
672 "N/A",
673 "HIT",
674 "MISS",
675 "L1",
676 "LFB",
677 "L2",
678 "L3",
679 "Local RAM",
680 "Remote RAM (1 hop)",
681 "Remote RAM (2 hops)",
682 "Remote Cache (1 hop)",
683 "Remote Cache (2 hops)",
684 "I/O",
685 "Uncached",
686};
687#define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
688
c824c433 689static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,
98a3b32c
SE
690 size_t size, unsigned int width)
691{
692 char out[64];
693 size_t sz = sizeof(out) - 1; /* -1 for null termination */
694 size_t i, l = 0;
695 u64 m = PERF_MEM_LVL_NA;
696 u64 hit, miss;
697
c824c433
ACM
698 if (he->mem_info)
699 m = he->mem_info->data_src.mem_lvl;
98a3b32c
SE
700
701 out[0] = '\0';
702
703 hit = m & PERF_MEM_LVL_HIT;
704 miss = m & PERF_MEM_LVL_MISS;
705
706 /* already taken care of */
707 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
708
709 for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) {
710 if (!(m & 0x1))
711 continue;
712 if (l) {
713 strcat(out, " or ");
714 l += 4;
715 }
716 strncat(out, mem_lvl[i], sz - l);
717 l += strlen(mem_lvl[i]);
718 }
719 if (*out == '\0')
720 strcpy(out, "N/A");
721 if (hit)
722 strncat(out, " hit", sz - l);
723 if (miss)
724 strncat(out, " miss", sz - l);
725
726 return repsep_snprintf(bf, size, "%-*s", width, out);
727}
728
729static int64_t
730sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
731{
732 union perf_mem_data_src data_src_l;
733 union perf_mem_data_src data_src_r;
734
735 if (left->mem_info)
736 data_src_l = left->mem_info->data_src;
737 else
738 data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
739
740 if (right->mem_info)
741 data_src_r = right->mem_info->data_src;
742 else
743 data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
744
745 return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
746}
747
748static const char * const snoop_access[] = {
749 "N/A",
750 "None",
751 "Miss",
752 "Hit",
753 "HitM",
754};
755#define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
756
c824c433 757static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
98a3b32c
SE
758 size_t size, unsigned int width)
759{
760 char out[64];
761 size_t sz = sizeof(out) - 1; /* -1 for null termination */
762 size_t i, l = 0;
763 u64 m = PERF_MEM_SNOOP_NA;
764
765 out[0] = '\0';
766
c824c433
ACM
767 if (he->mem_info)
768 m = he->mem_info->data_src.mem_snoop;
98a3b32c
SE
769
770 for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {
771 if (!(m & 0x1))
772 continue;
773 if (l) {
774 strcat(out, " or ");
775 l += 4;
776 }
777 strncat(out, snoop_access[i], sz - l);
778 l += strlen(snoop_access[i]);
779 }
780
781 if (*out == '\0')
782 strcpy(out, "N/A");
783
784 return repsep_snprintf(bf, size, "%-*s", width, out);
785}
786
b5387528
RAV
787struct sort_entry sort_mispredict = {
788 .se_header = "Branch Mispredicted",
789 .se_cmp = sort__mispredict_cmp,
790 .se_snprintf = hist_entry__mispredict_snprintf,
791 .se_width_idx = HISTC_MISPREDICT,
792};
793
05484298
AK
794static u64 he_weight(struct hist_entry *he)
795{
796 return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
797}
798
799static int64_t
800sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
801{
802 return he_weight(left) - he_weight(right);
803}
804
c824c433 805static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,
05484298
AK
806 size_t size, unsigned int width)
807{
c824c433 808 return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he));
05484298
AK
809}
810
811struct sort_entry sort_local_weight = {
812 .se_header = "Local Weight",
813 .se_cmp = sort__local_weight_cmp,
814 .se_snprintf = hist_entry__local_weight_snprintf,
815 .se_width_idx = HISTC_LOCAL_WEIGHT,
816};
817
818static int64_t
819sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
820{
821 return left->stat.weight - right->stat.weight;
822}
823
c824c433 824static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,
05484298
AK
825 size_t size, unsigned int width)
826{
c824c433 827 return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight);
05484298
AK
828}
829
830struct sort_entry sort_global_weight = {
831 .se_header = "Weight",
832 .se_cmp = sort__global_weight_cmp,
833 .se_snprintf = hist_entry__global_weight_snprintf,
834 .se_width_idx = HISTC_GLOBAL_WEIGHT,
835};
836
98a3b32c
SE
837struct sort_entry sort_mem_daddr_sym = {
838 .se_header = "Data Symbol",
839 .se_cmp = sort__daddr_cmp,
840 .se_snprintf = hist_entry__daddr_snprintf,
841 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
842};
843
844struct sort_entry sort_mem_daddr_dso = {
845 .se_header = "Data Object",
846 .se_cmp = sort__dso_daddr_cmp,
847 .se_snprintf = hist_entry__dso_daddr_snprintf,
848 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
849};
850
851struct sort_entry sort_mem_locked = {
852 .se_header = "Locked",
853 .se_cmp = sort__locked_cmp,
854 .se_snprintf = hist_entry__locked_snprintf,
855 .se_width_idx = HISTC_MEM_LOCKED,
856};
857
858struct sort_entry sort_mem_tlb = {
859 .se_header = "TLB access",
860 .se_cmp = sort__tlb_cmp,
861 .se_snprintf = hist_entry__tlb_snprintf,
862 .se_width_idx = HISTC_MEM_TLB,
863};
864
865struct sort_entry sort_mem_lvl = {
866 .se_header = "Memory access",
867 .se_cmp = sort__lvl_cmp,
868 .se_snprintf = hist_entry__lvl_snprintf,
869 .se_width_idx = HISTC_MEM_LVL,
870};
871
872struct sort_entry sort_mem_snoop = {
873 .se_header = "Snoop",
874 .se_cmp = sort__snoop_cmp,
875 .se_snprintf = hist_entry__snoop_snprintf,
876 .se_width_idx = HISTC_MEM_SNOOP,
877};
878
f5d05bce
AK
879static int64_t
880sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
881{
882 return left->branch_info->flags.abort !=
883 right->branch_info->flags.abort;
884}
885
c824c433 886static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf,
f5d05bce
AK
887 size_t size, unsigned int width)
888{
889 static const char *out = ".";
890
c824c433 891 if (he->branch_info->flags.abort)
f5d05bce
AK
892 out = "A";
893 return repsep_snprintf(bf, size, "%-*s", width, out);
894}
895
896struct sort_entry sort_abort = {
897 .se_header = "Transaction abort",
898 .se_cmp = sort__abort_cmp,
899 .se_snprintf = hist_entry__abort_snprintf,
900 .se_width_idx = HISTC_ABORT,
901};
902
903static int64_t
904sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right)
905{
906 return left->branch_info->flags.in_tx !=
907 right->branch_info->flags.in_tx;
908}
909
c824c433 910static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf,
f5d05bce
AK
911 size_t size, unsigned int width)
912{
913 static const char *out = ".";
914
c824c433 915 if (he->branch_info->flags.in_tx)
f5d05bce
AK
916 out = "T";
917
918 return repsep_snprintf(bf, size, "%-*s", width, out);
919}
920
921struct sort_entry sort_in_tx = {
922 .se_header = "Branch in transaction",
923 .se_cmp = sort__in_tx_cmp,
924 .se_snprintf = hist_entry__in_tx_snprintf,
925 .se_width_idx = HISTC_IN_TX,
926};
927
475eeab9
AK
928static int64_t
929sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
930{
931 return left->transaction - right->transaction;
932}
933
934static inline char *add_str(char *p, const char *str)
935{
936 strcpy(p, str);
937 return p + strlen(str);
938}
939
940static struct txbit {
941 unsigned flag;
942 const char *name;
943 int skip_for_len;
944} txbits[] = {
945 { PERF_TXN_ELISION, "EL ", 0 },
946 { PERF_TXN_TRANSACTION, "TX ", 1 },
947 { PERF_TXN_SYNC, "SYNC ", 1 },
948 { PERF_TXN_ASYNC, "ASYNC ", 0 },
949 { PERF_TXN_RETRY, "RETRY ", 0 },
950 { PERF_TXN_CONFLICT, "CON ", 0 },
951 { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 },
952 { PERF_TXN_CAPACITY_READ, "CAP-READ ", 0 },
953 { 0, NULL, 0 }
954};
955
956int hist_entry__transaction_len(void)
957{
958 int i;
959 int len = 0;
960
961 for (i = 0; txbits[i].name; i++) {
962 if (!txbits[i].skip_for_len)
963 len += strlen(txbits[i].name);
964 }
965 len += 4; /* :XX<space> */
966 return len;
967}
968
c824c433 969static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf,
475eeab9
AK
970 size_t size, unsigned int width)
971{
c824c433 972 u64 t = he->transaction;
475eeab9
AK
973 char buf[128];
974 char *p = buf;
975 int i;
976
977 buf[0] = 0;
978 for (i = 0; txbits[i].name; i++)
979 if (txbits[i].flag & t)
980 p = add_str(p, txbits[i].name);
981 if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC)))
982 p = add_str(p, "NEITHER ");
983 if (t & PERF_TXN_ABORT_MASK) {
984 sprintf(p, ":%" PRIx64,
985 (t & PERF_TXN_ABORT_MASK) >>
986 PERF_TXN_ABORT_SHIFT);
987 p += strlen(p);
988 }
989
990 return repsep_snprintf(bf, size, "%-*s", width, buf);
991}
992
993struct sort_entry sort_transaction = {
994 .se_header = "Transaction ",
995 .se_cmp = sort__transaction_cmp,
996 .se_snprintf = hist_entry__transaction_snprintf,
997 .se_width_idx = HISTC_TRANSACTION,
998};
999
872a878f
FW
1000struct sort_dimension {
1001 const char *name;
1002 struct sort_entry *entry;
1003 int taken;
1004};
1005
b5387528
RAV
1006#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
1007
fc5871ed 1008static struct sort_dimension common_sort_dimensions[] = {
b5387528
RAV
1009 DIM(SORT_PID, "pid", sort_thread),
1010 DIM(SORT_COMM, "comm", sort_comm),
1011 DIM(SORT_DSO, "dso", sort_dso),
b5387528 1012 DIM(SORT_SYM, "symbol", sort_sym),
b5387528
RAV
1013 DIM(SORT_PARENT, "parent", sort_parent),
1014 DIM(SORT_CPU, "cpu", sort_cpu),
409a8be6 1015 DIM(SORT_SRCLINE, "srcline", sort_srcline),
f9ea55d0
AK
1016 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
1017 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
475eeab9 1018 DIM(SORT_TRANSACTION, "transaction", sort_transaction),
872a878f
FW
1019};
1020
fc5871ed
NK
1021#undef DIM
1022
1023#define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
1024
1025static struct sort_dimension bstack_sort_dimensions[] = {
1026 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
1027 DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
1028 DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
1029 DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
1030 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
f5d05bce
AK
1031 DIM(SORT_IN_TX, "in_tx", sort_in_tx),
1032 DIM(SORT_ABORT, "abort", sort_abort),
fc5871ed
NK
1033};
1034
1035#undef DIM
1036
afab87b9
NK
1037#define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
1038
1039static struct sort_dimension memory_sort_dimensions[] = {
afab87b9
NK
1040 DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
1041 DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
1042 DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
1043 DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
1044 DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
1045 DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
1046};
1047
1048#undef DIM
1049
a2ce067e
NK
1050struct hpp_dimension {
1051 const char *name;
1052 struct perf_hpp_fmt *fmt;
1053 int taken;
1054};
1055
1056#define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
1057
1058static struct hpp_dimension hpp_sort_dimensions[] = {
1059 DIM(PERF_HPP__OVERHEAD, "overhead"),
1060 DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"),
1061 DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
1062 DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
1063 DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
594dcbf3 1064 DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"),
a2ce067e
NK
1065 DIM(PERF_HPP__SAMPLES, "sample"),
1066 DIM(PERF_HPP__PERIOD, "period"),
1067};
1068
1069#undef DIM
1070
8b536999
NK
1071struct hpp_sort_entry {
1072 struct perf_hpp_fmt hpp;
1073 struct sort_entry *se;
1074};
1075
a7d945bc
NK
1076bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1077{
1078 struct hpp_sort_entry *hse_a;
1079 struct hpp_sort_entry *hse_b;
1080
1081 if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
1082 return false;
1083
1084 hse_a = container_of(a, struct hpp_sort_entry, hpp);
1085 hse_b = container_of(b, struct hpp_sort_entry, hpp);
1086
1087 return hse_a->se == hse_b->se;
1088}
1089
678a500d
NK
1090void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists)
1091{
1092 struct hpp_sort_entry *hse;
1093
1094 if (!perf_hpp__is_sort_entry(fmt))
1095 return;
1096
1097 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1098 hists__new_col_len(hists, hse->se->se_width_idx,
1099 strlen(hse->se->se_header));
1100}
1101
8b536999
NK
1102static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1103 struct perf_evsel *evsel)
1104{
1105 struct hpp_sort_entry *hse;
1106 size_t len;
1107
1108 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1109 len = hists__col_len(&evsel->hists, hse->se->se_width_idx);
1110
1111 return scnprintf(hpp->buf, hpp->size, "%*s", len, hse->se->se_header);
1112}
1113
1114static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
1115 struct perf_hpp *hpp __maybe_unused,
1116 struct perf_evsel *evsel)
1117{
1118 struct hpp_sort_entry *hse;
1119
1120 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1121
1122 return hists__col_len(&evsel->hists, hse->se->se_width_idx);
1123}
1124
1125static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1126 struct hist_entry *he)
1127{
1128 struct hpp_sort_entry *hse;
1129 size_t len;
1130
1131 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1132 len = hists__col_len(he->hists, hse->se->se_width_idx);
1133
1134 return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
1135}
1136
a7d945bc
NK
1137static struct hpp_sort_entry *
1138__sort_dimension__alloc_hpp(struct sort_dimension *sd)
8b536999
NK
1139{
1140 struct hpp_sort_entry *hse;
1141
1142 hse = malloc(sizeof(*hse));
1143 if (hse == NULL) {
1144 pr_err("Memory allocation failed\n");
a7d945bc 1145 return NULL;
8b536999
NK
1146 }
1147
1148 hse->se = sd->entry;
1149 hse->hpp.header = __sort__hpp_header;
1150 hse->hpp.width = __sort__hpp_width;
1151 hse->hpp.entry = __sort__hpp_entry;
1152 hse->hpp.color = NULL;
1153
1154 hse->hpp.cmp = sd->entry->se_cmp;
1155 hse->hpp.collapse = sd->entry->se_collapse ? : sd->entry->se_cmp;
202e7a6d 1156 hse->hpp.sort = sd->entry->se_sort ? : hse->hpp.collapse;
8b536999
NK
1157
1158 INIT_LIST_HEAD(&hse->hpp.list);
1159 INIT_LIST_HEAD(&hse->hpp.sort_list);
f2998422 1160 hse->hpp.elide = false;
8b536999 1161
a7d945bc
NK
1162 return hse;
1163}
1164
1165bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
1166{
1167 return format->header == __sort__hpp_header;
1168}
1169
1170static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd)
1171{
1172 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1173
1174 if (hse == NULL)
1175 return -1;
1176
8b536999
NK
1177 perf_hpp__register_sort_field(&hse->hpp);
1178 return 0;
1179}
1180
a7d945bc
NK
1181static int __sort_dimension__add_hpp_output(struct sort_dimension *sd)
1182{
1183 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1184
1185 if (hse == NULL)
1186 return -1;
1187
1188 perf_hpp__column_register(&hse->hpp);
1189 return 0;
1190}
1191
cfaa154b 1192static int __sort_dimension__add(struct sort_dimension *sd)
2f532d09
NK
1193{
1194 if (sd->taken)
8b536999
NK
1195 return 0;
1196
a7d945bc 1197 if (__sort_dimension__add_hpp_sort(sd) < 0)
8b536999 1198 return -1;
2f532d09
NK
1199
1200 if (sd->entry->se_collapse)
1201 sort__need_collapse = 1;
1202
2f532d09 1203 sd->taken = 1;
8b536999
NK
1204
1205 return 0;
2f532d09
NK
1206}
1207
a2ce067e
NK
1208static int __hpp_dimension__add(struct hpp_dimension *hd)
1209{
1210 if (!hd->taken) {
1211 hd->taken = 1;
1212
1213 perf_hpp__register_sort_field(hd->fmt);
1214 }
1215 return 0;
1216}
1217
a7d945bc
NK
1218static int __sort_dimension__add_output(struct sort_dimension *sd)
1219{
1220 if (sd->taken)
1221 return 0;
1222
1223 if (__sort_dimension__add_hpp_output(sd) < 0)
1224 return -1;
1225
1226 sd->taken = 1;
1227 return 0;
1228}
1229
1230static int __hpp_dimension__add_output(struct hpp_dimension *hd)
1231{
1232 if (!hd->taken) {
1233 hd->taken = 1;
1234
1235 perf_hpp__column_register(hd->fmt);
1236 }
1237 return 0;
1238}
1239
dd68ada2
JK
1240int sort_dimension__add(const char *tok)
1241{
1242 unsigned int i;
1243
fc5871ed
NK
1244 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1245 struct sort_dimension *sd = &common_sort_dimensions[i];
dd68ada2 1246
dd68ada2
JK
1247 if (strncasecmp(tok, sd->name, strlen(tok)))
1248 continue;
fc5871ed 1249
dd68ada2
JK
1250 if (sd->entry == &sort_parent) {
1251 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
1252 if (ret) {
1253 char err[BUFSIZ];
1254
1255 regerror(ret, &parent_regex, err, sizeof(err));
2aefa4f7
ACM
1256 pr_err("Invalid regex: %s\n%s", parent_pattern, err);
1257 return -EINVAL;
dd68ada2
JK
1258 }
1259 sort__has_parent = 1;
930477bd 1260 } else if (sd->entry == &sort_sym) {
1af55640 1261 sort__has_sym = 1;
68f6d022
NK
1262 } else if (sd->entry == &sort_dso) {
1263 sort__has_dso = 1;
dd68ada2
JK
1264 }
1265
cfaa154b 1266 return __sort_dimension__add(sd);
dd68ada2 1267 }
fc5871ed 1268
a2ce067e
NK
1269 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1270 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1271
1272 if (strncasecmp(tok, hd->name, strlen(tok)))
1273 continue;
1274
1275 return __hpp_dimension__add(hd);
1276 }
1277
fc5871ed
NK
1278 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1279 struct sort_dimension *sd = &bstack_sort_dimensions[i];
1280
1281 if (strncasecmp(tok, sd->name, strlen(tok)))
1282 continue;
1283
55369fc1 1284 if (sort__mode != SORT_MODE__BRANCH)
fc5871ed
NK
1285 return -EINVAL;
1286
1287 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
1288 sort__has_sym = 1;
1289
cfaa154b 1290 __sort_dimension__add(sd);
fc5871ed
NK
1291 return 0;
1292 }
1293
afab87b9
NK
1294 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1295 struct sort_dimension *sd = &memory_sort_dimensions[i];
1296
1297 if (strncasecmp(tok, sd->name, strlen(tok)))
1298 continue;
1299
1300 if (sort__mode != SORT_MODE__MEMORY)
1301 return -EINVAL;
1302
1303 if (sd->entry == &sort_mem_daddr_sym)
1304 sort__has_sym = 1;
1305
cfaa154b 1306 __sort_dimension__add(sd);
afab87b9
NK
1307 return 0;
1308 }
1309
dd68ada2
JK
1310 return -ESRCH;
1311}
c8829c7a 1312
512ae1bd
NK
1313static const char *get_default_sort_order(void)
1314{
1315 const char *default_sort_orders[] = {
1316 default_sort_order,
1317 default_branch_sort_order,
1318 default_mem_sort_order,
1319 default_top_sort_order,
1320 default_diff_sort_order,
1321 };
1322
1323 BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
1324
1325 return default_sort_orders[sort__mode];
1326}
1327
a7d945bc 1328static int __setup_sorting(void)
c8829c7a 1329{
512ae1bd
NK
1330 char *tmp, *tok, *str;
1331 const char *sort_keys = sort_order;
55309985 1332 int ret = 0;
c8829c7a 1333
a7d945bc
NK
1334 if (sort_keys == NULL) {
1335 if (field_order) {
1336 /*
1337 * If user specified field order but no sort order,
1338 * we'll honor it and not add default sort orders.
1339 */
1340 return 0;
1341 }
1342
512ae1bd 1343 sort_keys = get_default_sort_order();
a7d945bc 1344 }
512ae1bd
NK
1345
1346 str = strdup(sort_keys);
5936f54d
NK
1347 if (str == NULL) {
1348 error("Not enough memory to setup sort keys");
1349 return -ENOMEM;
1350 }
1351
c8829c7a
ACM
1352 for (tok = strtok_r(str, ", ", &tmp);
1353 tok; tok = strtok_r(NULL, ", ", &tmp)) {
55309985 1354 ret = sort_dimension__add(tok);
fc5871ed
NK
1355 if (ret == -EINVAL) {
1356 error("Invalid --sort key: `%s'", tok);
55309985 1357 break;
fc5871ed 1358 } else if (ret == -ESRCH) {
c8829c7a 1359 error("Unknown --sort key: `%s'", tok);
55309985 1360 break;
c8829c7a
ACM
1361 }
1362 }
1363
1364 free(str);
55309985 1365 return ret;
c8829c7a 1366}
c351c281 1367
f2998422 1368void perf_hpp__set_elide(int idx, bool elide)
e67d49a7 1369{
f2998422
JO
1370 struct perf_hpp_fmt *fmt;
1371 struct hpp_sort_entry *hse;
e67d49a7 1372
f2998422
JO
1373 perf_hpp__for_each_format(fmt) {
1374 if (!perf_hpp__is_sort_entry(fmt))
1375 continue;
1376
1377 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1378 if (hse->se->se_width_idx == idx) {
1379 fmt->elide = elide;
1380 break;
1381 }
e67d49a7 1382 }
e67d49a7
NK
1383}
1384
f2998422 1385static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)
c351c281
ACM
1386{
1387 if (list && strlist__nr_entries(list) == 1) {
1388 if (fp != NULL)
1389 fprintf(fp, "# %s: %s\n", list_name,
1390 strlist__entry(list, 0)->s);
f2998422
JO
1391 return true;
1392 }
1393 return false;
1394}
1395
1396static bool get_elide(int idx, FILE *output)
1397{
1398 switch (idx) {
1399 case HISTC_SYMBOL:
1400 return __get_elide(symbol_conf.sym_list, "symbol", output);
1401 case HISTC_DSO:
1402 return __get_elide(symbol_conf.dso_list, "dso", output);
1403 case HISTC_COMM:
1404 return __get_elide(symbol_conf.comm_list, "comm", output);
1405 default:
1406 break;
c351c281 1407 }
f2998422
JO
1408
1409 if (sort__mode != SORT_MODE__BRANCH)
1410 return false;
1411
1412 switch (idx) {
1413 case HISTC_SYMBOL_FROM:
1414 return __get_elide(symbol_conf.sym_from_list, "sym_from", output);
1415 case HISTC_SYMBOL_TO:
1416 return __get_elide(symbol_conf.sym_to_list, "sym_to", output);
1417 case HISTC_DSO_FROM:
1418 return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
1419 case HISTC_DSO_TO:
1420 return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
1421 default:
1422 break;
1423 }
1424
1425 return false;
c351c281 1426}
08e71542
NK
1427
1428void sort__setup_elide(FILE *output)
1429{
cfaa154b
NK
1430 struct perf_hpp_fmt *fmt;
1431 struct hpp_sort_entry *hse;
7524f63b 1432
f2998422
JO
1433 perf_hpp__for_each_format(fmt) {
1434 if (!perf_hpp__is_sort_entry(fmt))
1435 continue;
1436
1437 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1438 fmt->elide = get_elide(hse->se->se_width_idx, output);
08e71542
NK
1439 }
1440
7524f63b
NK
1441 /*
1442 * It makes no sense to elide all of sort entries.
1443 * Just revert them to show up again.
1444 */
cfaa154b
NK
1445 perf_hpp__for_each_format(fmt) {
1446 if (!perf_hpp__is_sort_entry(fmt))
1447 continue;
1448
f2998422 1449 if (!fmt->elide)
7524f63b
NK
1450 return;
1451 }
1452
cfaa154b
NK
1453 perf_hpp__for_each_format(fmt) {
1454 if (!perf_hpp__is_sort_entry(fmt))
1455 continue;
1456
f2998422 1457 fmt->elide = false;
cfaa154b 1458 }
08e71542 1459}
a7d945bc
NK
1460
1461static int output_field_add(char *tok)
1462{
1463 unsigned int i;
1464
1465 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1466 struct sort_dimension *sd = &common_sort_dimensions[i];
1467
1468 if (strncasecmp(tok, sd->name, strlen(tok)))
1469 continue;
1470
1471 return __sort_dimension__add_output(sd);
1472 }
1473
1474 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1475 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1476
1477 if (strncasecmp(tok, hd->name, strlen(tok)))
1478 continue;
1479
1480 return __hpp_dimension__add_output(hd);
1481 }
1482
1483 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1484 struct sort_dimension *sd = &bstack_sort_dimensions[i];
1485
1486 if (strncasecmp(tok, sd->name, strlen(tok)))
1487 continue;
1488
1489 return __sort_dimension__add_output(sd);
1490 }
1491
1492 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1493 struct sort_dimension *sd = &memory_sort_dimensions[i];
1494
1495 if (strncasecmp(tok, sd->name, strlen(tok)))
1496 continue;
1497
1498 return __sort_dimension__add_output(sd);
1499 }
1500
1501 return -ESRCH;
1502}
1503
1504static void reset_dimensions(void)
1505{
1506 unsigned int i;
1507
1508 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++)
1509 common_sort_dimensions[i].taken = 0;
1510
1511 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++)
1512 hpp_sort_dimensions[i].taken = 0;
1513
1514 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++)
1515 bstack_sort_dimensions[i].taken = 0;
1516
1517 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++)
1518 memory_sort_dimensions[i].taken = 0;
1519}
1520
1521static int __setup_output_field(void)
1522{
1523 char *tmp, *tok, *str;
1524 int ret = 0;
1525
1526 if (field_order == NULL)
1527 return 0;
1528
1529 reset_dimensions();
1530
1531 str = strdup(field_order);
1532 if (str == NULL) {
1533 error("Not enough memory to setup output fields");
1534 return -ENOMEM;
1535 }
1536
1537 for (tok = strtok_r(str, ", ", &tmp);
1538 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1539 ret = output_field_add(tok);
1540 if (ret == -EINVAL) {
1541 error("Invalid --fields key: `%s'", tok);
1542 break;
1543 } else if (ret == -ESRCH) {
1544 error("Unknown --fields key: `%s'", tok);
1545 break;
1546 }
1547 }
1548
1549 free(str);
1550 return ret;
1551}
1552
1553int setup_sorting(void)
1554{
1555 int err;
1556
1557 err = __setup_sorting();
1558 if (err < 0)
1559 return err;
1560
1561 if (parent_pattern != default_parent_pattern) {
1562 err = sort_dimension__add("parent");
1563 if (err < 0)
1564 return err;
1565 }
1566
1567 reset_dimensions();
1568
1569 /*
1570 * perf diff doesn't use default hpp output fields.
1571 */
1572 if (sort__mode != SORT_MODE__DIFF)
1573 perf_hpp__init();
1574
1575 err = __setup_output_field();
1576 if (err < 0)
1577 return err;
1578
1579 /* copy sort keys to output fields */
1580 perf_hpp__setup_output_field();
1581 /* and then copy output fields to sort keys */
1582 perf_hpp__append_sort_keys();
1583
1584 return 0;
1585}
1c89fe9b
NK
1586
1587void reset_output_field(void)
1588{
1589 sort__need_collapse = 0;
1590 sort__has_parent = 0;
1591 sort__has_sym = 0;
1592 sort__has_dso = 0;
1593
d69b2962
NK
1594 field_order = NULL;
1595 sort_order = NULL;
1596
1c89fe9b
NK
1597 reset_dimensions();
1598 perf_hpp__reset_output_field();
1599}