perf test: Consider PERF_SAMPLE_TRANSACTION in the "sample parsing" test
[linux-block.git] / tools / perf / util / sort.c
CommitLineData
dd68ada2 1#include "sort.h"
8a6c5b26 2#include "hist.h"
08e71542 3#include "symbol.h"
dd68ada2
JK
4
5regex_t parent_regex;
edb7c60e
ACM
6const char default_parent_pattern[] = "^sys_|^do_page_fault";
7const char *parent_pattern = default_parent_pattern;
8const char default_sort_order[] = "comm,dso,symbol";
9const char *sort_order = default_sort_order;
b21484f1
GP
10regex_t ignore_callees_regex;
11int have_ignore_callees = 0;
af0a6fa4
FW
12int sort__need_collapse = 0;
13int sort__has_parent = 0;
1af55640 14int sort__has_sym = 0;
55369fc1 15enum sort_mode sort__mode = SORT_MODE__NORMAL;
a4fb581b
FW
16
17enum sort_type sort__first_dimension;
dd68ada2 18
dd68ada2
JK
19LIST_HEAD(hist_entry__sort_list);
20
a4e3b956 21static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
dd68ada2
JK
22{
23 int n;
24 va_list ap;
25
26 va_start(ap, fmt);
a4e3b956 27 n = vsnprintf(bf, size, fmt, ap);
0ca0c130 28 if (symbol_conf.field_sep && n > 0) {
a4e3b956
ACM
29 char *sep = bf;
30
31 while (1) {
0ca0c130 32 sep = strchr(sep, *symbol_conf.field_sep);
a4e3b956
ACM
33 if (sep == NULL)
34 break;
35 *sep = '.';
dd68ada2 36 }
dd68ada2
JK
37 }
38 va_end(ap);
b832796c
AB
39
40 if (n >= (int)size)
41 return size - 1;
dd68ada2
JK
42 return n;
43}
44
872a878f
FW
45static int64_t cmp_null(void *l, void *r)
46{
47 if (!l && !r)
48 return 0;
49 else if (!l)
50 return -1;
51 else
52 return 1;
53}
54
55/* --sort pid */
56
57static int64_t
58sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
59{
38051234 60 return right->thread->tid - left->thread->tid;
872a878f
FW
61}
62
a4e3b956
ACM
63static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
64 size_t size, unsigned int width)
dd68ada2 65{
fb29a338 66 return repsep_snprintf(bf, size, "%*s:%5d", width - 6,
38051234 67 self->thread->comm ?: "", self->thread->tid);
dd68ada2
JK
68}
69
872a878f
FW
70struct sort_entry sort_thread = {
71 .se_header = "Command: Pid",
72 .se_cmp = sort__thread_cmp,
73 .se_snprintf = hist_entry__thread_snprintf,
74 .se_width_idx = HISTC_THREAD,
75};
76
77/* --sort comm */
78
79static int64_t
80sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
81{
38051234 82 return right->thread->tid - left->thread->tid;
872a878f
FW
83}
84
85static int64_t
86sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
87{
88 char *comm_l = left->thread->comm;
89 char *comm_r = right->thread->comm;
90
91 if (!comm_l || !comm_r)
92 return cmp_null(comm_l, comm_r);
93
94 return strcmp(comm_l, comm_r);
95}
96
a4e3b956
ACM
97static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
98 size_t size, unsigned int width)
dd68ada2 99{
a4e3b956 100 return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
dd68ada2
JK
101}
102
14d1ac74
NK
103struct sort_entry sort_comm = {
104 .se_header = "Command",
105 .se_cmp = sort__comm_cmp,
106 .se_collapse = sort__comm_collapse,
107 .se_snprintf = hist_entry__comm_snprintf,
108 .se_width_idx = HISTC_COMM,
109};
110
111/* --sort dso */
112
b5387528
RAV
113static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
114{
115 struct dso *dso_l = map_l ? map_l->dso : NULL;
116 struct dso *dso_r = map_r ? map_r->dso : NULL;
117 const char *dso_name_l, *dso_name_r;
118
119 if (!dso_l || !dso_r)
120 return cmp_null(dso_l, dso_r);
121
122 if (verbose) {
123 dso_name_l = dso_l->long_name;
124 dso_name_r = dso_r->long_name;
125 } else {
126 dso_name_l = dso_l->short_name;
127 dso_name_r = dso_r->short_name;
128 }
129
130 return strcmp(dso_name_l, dso_name_r);
131}
132
872a878f 133static int64_t
dd68ada2
JK
134sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
135{
b5387528
RAV
136 return _sort__dso_cmp(left->ms.map, right->ms.map);
137}
dd68ada2 138
14d1ac74
NK
139static int _hist_entry__dso_snprintf(struct map *map, char *bf,
140 size_t size, unsigned int width)
141{
142 if (map && map->dso) {
143 const char *dso_name = !verbose ? map->dso->short_name :
144 map->dso->long_name;
145 return repsep_snprintf(bf, size, "%-*s", width, dso_name);
146 }
147
148 return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
149}
150
151static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
152 size_t size, unsigned int width)
153{
154 return _hist_entry__dso_snprintf(self->ms.map, bf, size, width);
155}
156
157struct sort_entry sort_dso = {
158 .se_header = "Shared Object",
159 .se_cmp = sort__dso_cmp,
160 .se_snprintf = hist_entry__dso_snprintf,
161 .se_width_idx = HISTC_DSO,
162};
163
164/* --sort symbol */
dd68ada2 165
51f27d14 166static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
b5387528 167{
51f27d14
NK
168 u64 ip_l, ip_r;
169
b5387528
RAV
170 if (!sym_l || !sym_r)
171 return cmp_null(sym_l, sym_r);
172
173 if (sym_l == sym_r)
174 return 0;
175
53985a7b
SL
176 ip_l = sym_l->start;
177 ip_r = sym_r->start;
b5387528
RAV
178
179 return (int64_t)(ip_r - ip_l);
180}
181
14d1ac74
NK
182static int64_t
183sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
b5387528 184{
09600e0f
NK
185 int64_t ret;
186
14d1ac74
NK
187 if (!left->ms.sym && !right->ms.sym)
188 return right->level - left->level;
dd68ada2 189
09600e0f
NK
190 /*
191 * comparing symbol address alone is not enough since it's a
192 * relative address within a dso.
193 */
194 ret = sort__dso_cmp(left, right);
195 if (ret != 0)
196 return ret;
197
51f27d14 198 return _sort__sym_cmp(left->ms.sym, right->ms.sym);
b5387528
RAV
199}
200
201static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
202 u64 ip, char level, char *bf, size_t size,
43355522 203 unsigned int width)
b5387528
RAV
204{
205 size_t ret = 0;
206
207 if (verbose) {
208 char o = map ? dso__symtab_origin(map->dso) : '!';
209 ret += repsep_snprintf(bf, size, "%-#*llx %c ",
ded19d57 210 BITS_PER_LONG / 4 + 2, ip, o);
439d473b 211 }
dd68ada2 212
b5387528 213 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
98a3b32c
SE
214 if (sym && map) {
215 if (map->type == MAP__VARIABLE) {
216 ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
217 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
62667746 218 ip - map->unmap_ip(map, sym->start));
98a3b32c
SE
219 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
220 width - ret, "");
221 } else {
222 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
223 width - ret,
224 sym->name);
225 }
226 } else {
b5387528
RAV
227 size_t len = BITS_PER_LONG / 4;
228 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
229 len, ip);
230 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
231 width - ret, "");
232 }
233
234 return ret;
dd68ada2
JK
235}
236
b5387528 237static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
43355522 238 size_t size, unsigned int width)
b5387528
RAV
239{
240 return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip,
241 self->level, bf, size, width);
242}
dd68ada2 243
872a878f
FW
244struct sort_entry sort_sym = {
245 .se_header = "Symbol",
246 .se_cmp = sort__sym_cmp,
247 .se_snprintf = hist_entry__sym_snprintf,
248 .se_width_idx = HISTC_SYMBOL,
249};
dd68ada2 250
409a8be6
ACM
251/* --sort srcline */
252
253static int64_t
254sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
255{
4adcc430
NK
256 if (!left->srcline) {
257 if (!left->ms.map)
258 left->srcline = SRCLINE_UNKNOWN;
259 else {
260 struct map *map = left->ms.map;
261 left->srcline = get_srcline(map->dso,
262 map__rip_2objdump(map, left->ip));
263 }
264 }
265 if (!right->srcline) {
266 if (!right->ms.map)
267 right->srcline = SRCLINE_UNKNOWN;
268 else {
269 struct map *map = right->ms.map;
270 right->srcline = get_srcline(map->dso,
271 map__rip_2objdump(map, right->ip));
272 }
273 }
274 return strcmp(left->srcline, right->srcline);
409a8be6
ACM
275}
276
277static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf,
1d037ca1
IT
278 size_t size,
279 unsigned int width __maybe_unused)
409a8be6 280{
4adcc430 281 return repsep_snprintf(bf, size, "%s", self->srcline);
409a8be6
ACM
282}
283
284struct sort_entry sort_srcline = {
285 .se_header = "Source:Line",
286 .se_cmp = sort__srcline_cmp,
287 .se_snprintf = hist_entry__srcline_snprintf,
288 .se_width_idx = HISTC_SRCLINE,
289};
290
dd68ada2
JK
291/* --sort parent */
292
872a878f 293static int64_t
dd68ada2
JK
294sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
295{
296 struct symbol *sym_l = left->parent;
297 struct symbol *sym_r = right->parent;
298
299 if (!sym_l || !sym_r)
300 return cmp_null(sym_l, sym_r);
301
302 return strcmp(sym_l->name, sym_r->name);
303}
304
a4e3b956
ACM
305static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
306 size_t size, unsigned int width)
dd68ada2 307{
a4e3b956 308 return repsep_snprintf(bf, size, "%-*s", width,
dd68ada2
JK
309 self->parent ? self->parent->name : "[other]");
310}
311
872a878f
FW
312struct sort_entry sort_parent = {
313 .se_header = "Parent symbol",
314 .se_cmp = sort__parent_cmp,
315 .se_snprintf = hist_entry__parent_snprintf,
316 .se_width_idx = HISTC_PARENT,
317};
318
f60f3593
AS
319/* --sort cpu */
320
872a878f 321static int64_t
f60f3593
AS
322sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
323{
324 return right->cpu - left->cpu;
325}
326
327static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
328 size_t size, unsigned int width)
329{
dccf1805 330 return repsep_snprintf(bf, size, "%*d", width, self->cpu);
f60f3593
AS
331}
332
872a878f
FW
333struct sort_entry sort_cpu = {
334 .se_header = "CPU",
335 .se_cmp = sort__cpu_cmp,
336 .se_snprintf = hist_entry__cpu_snprintf,
337 .se_width_idx = HISTC_CPU,
338};
339
14d1ac74
NK
340/* sort keys for branch stacks */
341
b5387528
RAV
342static int64_t
343sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
344{
345 return _sort__dso_cmp(left->branch_info->from.map,
346 right->branch_info->from.map);
347}
348
349static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf,
350 size_t size, unsigned int width)
351{
352 return _hist_entry__dso_snprintf(self->branch_info->from.map,
353 bf, size, width);
354}
355
b5387528
RAV
356static int64_t
357sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
358{
359 return _sort__dso_cmp(left->branch_info->to.map,
360 right->branch_info->to.map);
361}
362
363static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf,
364 size_t size, unsigned int width)
365{
366 return _hist_entry__dso_snprintf(self->branch_info->to.map,
367 bf, size, width);
368}
369
370static int64_t
371sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
372{
373 struct addr_map_symbol *from_l = &left->branch_info->from;
374 struct addr_map_symbol *from_r = &right->branch_info->from;
375
376 if (!from_l->sym && !from_r->sym)
377 return right->level - left->level;
378
51f27d14 379 return _sort__sym_cmp(from_l->sym, from_r->sym);
b5387528
RAV
380}
381
382static int64_t
383sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
384{
385 struct addr_map_symbol *to_l = &left->branch_info->to;
386 struct addr_map_symbol *to_r = &right->branch_info->to;
387
388 if (!to_l->sym && !to_r->sym)
389 return right->level - left->level;
390
51f27d14 391 return _sort__sym_cmp(to_l->sym, to_r->sym);
b5387528
RAV
392}
393
394static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
43355522 395 size_t size, unsigned int width)
b5387528
RAV
396{
397 struct addr_map_symbol *from = &self->branch_info->from;
398 return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
399 self->level, bf, size, width);
400
401}
402
403static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
43355522 404 size_t size, unsigned int width)
b5387528
RAV
405{
406 struct addr_map_symbol *to = &self->branch_info->to;
407 return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
408 self->level, bf, size, width);
409
410}
411
14d1ac74
NK
412struct sort_entry sort_dso_from = {
413 .se_header = "Source Shared Object",
414 .se_cmp = sort__dso_from_cmp,
415 .se_snprintf = hist_entry__dso_from_snprintf,
416 .se_width_idx = HISTC_DSO_FROM,
417};
418
b5387528
RAV
419struct sort_entry sort_dso_to = {
420 .se_header = "Target Shared Object",
421 .se_cmp = sort__dso_to_cmp,
422 .se_snprintf = hist_entry__dso_to_snprintf,
423 .se_width_idx = HISTC_DSO_TO,
424};
425
426struct sort_entry sort_sym_from = {
427 .se_header = "Source Symbol",
428 .se_cmp = sort__sym_from_cmp,
429 .se_snprintf = hist_entry__sym_from_snprintf,
430 .se_width_idx = HISTC_SYMBOL_FROM,
431};
432
433struct sort_entry sort_sym_to = {
434 .se_header = "Target Symbol",
435 .se_cmp = sort__sym_to_cmp,
436 .se_snprintf = hist_entry__sym_to_snprintf,
437 .se_width_idx = HISTC_SYMBOL_TO,
438};
439
440static int64_t
441sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
442{
443 const unsigned char mp = left->branch_info->flags.mispred !=
444 right->branch_info->flags.mispred;
445 const unsigned char p = left->branch_info->flags.predicted !=
446 right->branch_info->flags.predicted;
447
448 return mp || p;
449}
450
451static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf,
452 size_t size, unsigned int width){
453 static const char *out = "N/A";
454
455 if (self->branch_info->flags.predicted)
456 out = "N";
457 else if (self->branch_info->flags.mispred)
458 out = "Y";
459
460 return repsep_snprintf(bf, size, "%-*s", width, out);
461}
462
98a3b32c
SE
463/* --sort daddr_sym */
464static int64_t
465sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
466{
467 uint64_t l = 0, r = 0;
468
469 if (left->mem_info)
470 l = left->mem_info->daddr.addr;
471 if (right->mem_info)
472 r = right->mem_info->daddr.addr;
473
474 return (int64_t)(r - l);
475}
476
477static int hist_entry__daddr_snprintf(struct hist_entry *self, char *bf,
478 size_t size, unsigned int width)
479{
480 uint64_t addr = 0;
481 struct map *map = NULL;
482 struct symbol *sym = NULL;
483
484 if (self->mem_info) {
485 addr = self->mem_info->daddr.addr;
486 map = self->mem_info->daddr.map;
487 sym = self->mem_info->daddr.sym;
488 }
489 return _hist_entry__sym_snprintf(map, sym, addr, self->level, bf, size,
490 width);
491}
492
493static int64_t
494sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
495{
496 struct map *map_l = NULL;
497 struct map *map_r = NULL;
498
499 if (left->mem_info)
500 map_l = left->mem_info->daddr.map;
501 if (right->mem_info)
502 map_r = right->mem_info->daddr.map;
503
504 return _sort__dso_cmp(map_l, map_r);
505}
506
507static int hist_entry__dso_daddr_snprintf(struct hist_entry *self, char *bf,
508 size_t size, unsigned int width)
509{
510 struct map *map = NULL;
511
512 if (self->mem_info)
513 map = self->mem_info->daddr.map;
514
515 return _hist_entry__dso_snprintf(map, bf, size, width);
516}
517
518static int64_t
519sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
520{
521 union perf_mem_data_src data_src_l;
522 union perf_mem_data_src data_src_r;
523
524 if (left->mem_info)
525 data_src_l = left->mem_info->data_src;
526 else
527 data_src_l.mem_lock = PERF_MEM_LOCK_NA;
528
529 if (right->mem_info)
530 data_src_r = right->mem_info->data_src;
531 else
532 data_src_r.mem_lock = PERF_MEM_LOCK_NA;
533
534 return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
535}
536
537static int hist_entry__locked_snprintf(struct hist_entry *self, char *bf,
538 size_t size, unsigned int width)
539{
540 const char *out;
541 u64 mask = PERF_MEM_LOCK_NA;
542
543 if (self->mem_info)
544 mask = self->mem_info->data_src.mem_lock;
545
546 if (mask & PERF_MEM_LOCK_NA)
547 out = "N/A";
548 else if (mask & PERF_MEM_LOCK_LOCKED)
549 out = "Yes";
550 else
551 out = "No";
552
553 return repsep_snprintf(bf, size, "%-*s", width, out);
554}
555
556static int64_t
557sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
558{
559 union perf_mem_data_src data_src_l;
560 union perf_mem_data_src data_src_r;
561
562 if (left->mem_info)
563 data_src_l = left->mem_info->data_src;
564 else
565 data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
566
567 if (right->mem_info)
568 data_src_r = right->mem_info->data_src;
569 else
570 data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
571
572 return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
573}
574
575static const char * const tlb_access[] = {
576 "N/A",
577 "HIT",
578 "MISS",
579 "L1",
580 "L2",
581 "Walker",
582 "Fault",
583};
584#define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
585
586static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf,
587 size_t size, unsigned int width)
588{
589 char out[64];
590 size_t sz = sizeof(out) - 1; /* -1 for null termination */
591 size_t l = 0, i;
592 u64 m = PERF_MEM_TLB_NA;
593 u64 hit, miss;
594
595 out[0] = '\0';
596
597 if (self->mem_info)
598 m = self->mem_info->data_src.mem_dtlb;
599
600 hit = m & PERF_MEM_TLB_HIT;
601 miss = m & PERF_MEM_TLB_MISS;
602
603 /* already taken care of */
604 m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
605
606 for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) {
607 if (!(m & 0x1))
608 continue;
609 if (l) {
610 strcat(out, " or ");
611 l += 4;
612 }
613 strncat(out, tlb_access[i], sz - l);
614 l += strlen(tlb_access[i]);
615 }
616 if (*out == '\0')
617 strcpy(out, "N/A");
618 if (hit)
619 strncat(out, " hit", sz - l);
620 if (miss)
621 strncat(out, " miss", sz - l);
622
623 return repsep_snprintf(bf, size, "%-*s", width, out);
624}
625
626static int64_t
627sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
628{
629 union perf_mem_data_src data_src_l;
630 union perf_mem_data_src data_src_r;
631
632 if (left->mem_info)
633 data_src_l = left->mem_info->data_src;
634 else
635 data_src_l.mem_lvl = PERF_MEM_LVL_NA;
636
637 if (right->mem_info)
638 data_src_r = right->mem_info->data_src;
639 else
640 data_src_r.mem_lvl = PERF_MEM_LVL_NA;
641
642 return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
643}
644
645static const char * const mem_lvl[] = {
646 "N/A",
647 "HIT",
648 "MISS",
649 "L1",
650 "LFB",
651 "L2",
652 "L3",
653 "Local RAM",
654 "Remote RAM (1 hop)",
655 "Remote RAM (2 hops)",
656 "Remote Cache (1 hop)",
657 "Remote Cache (2 hops)",
658 "I/O",
659 "Uncached",
660};
661#define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
662
663static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf,
664 size_t size, unsigned int width)
665{
666 char out[64];
667 size_t sz = sizeof(out) - 1; /* -1 for null termination */
668 size_t i, l = 0;
669 u64 m = PERF_MEM_LVL_NA;
670 u64 hit, miss;
671
672 if (self->mem_info)
673 m = self->mem_info->data_src.mem_lvl;
674
675 out[0] = '\0';
676
677 hit = m & PERF_MEM_LVL_HIT;
678 miss = m & PERF_MEM_LVL_MISS;
679
680 /* already taken care of */
681 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
682
683 for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) {
684 if (!(m & 0x1))
685 continue;
686 if (l) {
687 strcat(out, " or ");
688 l += 4;
689 }
690 strncat(out, mem_lvl[i], sz - l);
691 l += strlen(mem_lvl[i]);
692 }
693 if (*out == '\0')
694 strcpy(out, "N/A");
695 if (hit)
696 strncat(out, " hit", sz - l);
697 if (miss)
698 strncat(out, " miss", sz - l);
699
700 return repsep_snprintf(bf, size, "%-*s", width, out);
701}
702
703static int64_t
704sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
705{
706 union perf_mem_data_src data_src_l;
707 union perf_mem_data_src data_src_r;
708
709 if (left->mem_info)
710 data_src_l = left->mem_info->data_src;
711 else
712 data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
713
714 if (right->mem_info)
715 data_src_r = right->mem_info->data_src;
716 else
717 data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
718
719 return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
720}
721
722static const char * const snoop_access[] = {
723 "N/A",
724 "None",
725 "Miss",
726 "Hit",
727 "HitM",
728};
729#define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
730
731static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf,
732 size_t size, unsigned int width)
733{
734 char out[64];
735 size_t sz = sizeof(out) - 1; /* -1 for null termination */
736 size_t i, l = 0;
737 u64 m = PERF_MEM_SNOOP_NA;
738
739 out[0] = '\0';
740
741 if (self->mem_info)
742 m = self->mem_info->data_src.mem_snoop;
743
744 for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {
745 if (!(m & 0x1))
746 continue;
747 if (l) {
748 strcat(out, " or ");
749 l += 4;
750 }
751 strncat(out, snoop_access[i], sz - l);
752 l += strlen(snoop_access[i]);
753 }
754
755 if (*out == '\0')
756 strcpy(out, "N/A");
757
758 return repsep_snprintf(bf, size, "%-*s", width, out);
759}
760
b5387528
RAV
761struct sort_entry sort_mispredict = {
762 .se_header = "Branch Mispredicted",
763 .se_cmp = sort__mispredict_cmp,
764 .se_snprintf = hist_entry__mispredict_snprintf,
765 .se_width_idx = HISTC_MISPREDICT,
766};
767
05484298
AK
768static u64 he_weight(struct hist_entry *he)
769{
770 return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
771}
772
773static int64_t
774sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
775{
776 return he_weight(left) - he_weight(right);
777}
778
779static int hist_entry__local_weight_snprintf(struct hist_entry *self, char *bf,
780 size_t size, unsigned int width)
781{
782 return repsep_snprintf(bf, size, "%-*llu", width, he_weight(self));
783}
784
785struct sort_entry sort_local_weight = {
786 .se_header = "Local Weight",
787 .se_cmp = sort__local_weight_cmp,
788 .se_snprintf = hist_entry__local_weight_snprintf,
789 .se_width_idx = HISTC_LOCAL_WEIGHT,
790};
791
792static int64_t
793sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
794{
795 return left->stat.weight - right->stat.weight;
796}
797
798static int hist_entry__global_weight_snprintf(struct hist_entry *self, char *bf,
799 size_t size, unsigned int width)
800{
801 return repsep_snprintf(bf, size, "%-*llu", width, self->stat.weight);
802}
803
804struct sort_entry sort_global_weight = {
805 .se_header = "Weight",
806 .se_cmp = sort__global_weight_cmp,
807 .se_snprintf = hist_entry__global_weight_snprintf,
808 .se_width_idx = HISTC_GLOBAL_WEIGHT,
809};
810
98a3b32c
SE
811struct sort_entry sort_mem_daddr_sym = {
812 .se_header = "Data Symbol",
813 .se_cmp = sort__daddr_cmp,
814 .se_snprintf = hist_entry__daddr_snprintf,
815 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
816};
817
818struct sort_entry sort_mem_daddr_dso = {
819 .se_header = "Data Object",
820 .se_cmp = sort__dso_daddr_cmp,
821 .se_snprintf = hist_entry__dso_daddr_snprintf,
822 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
823};
824
825struct sort_entry sort_mem_locked = {
826 .se_header = "Locked",
827 .se_cmp = sort__locked_cmp,
828 .se_snprintf = hist_entry__locked_snprintf,
829 .se_width_idx = HISTC_MEM_LOCKED,
830};
831
832struct sort_entry sort_mem_tlb = {
833 .se_header = "TLB access",
834 .se_cmp = sort__tlb_cmp,
835 .se_snprintf = hist_entry__tlb_snprintf,
836 .se_width_idx = HISTC_MEM_TLB,
837};
838
839struct sort_entry sort_mem_lvl = {
840 .se_header = "Memory access",
841 .se_cmp = sort__lvl_cmp,
842 .se_snprintf = hist_entry__lvl_snprintf,
843 .se_width_idx = HISTC_MEM_LVL,
844};
845
846struct sort_entry sort_mem_snoop = {
847 .se_header = "Snoop",
848 .se_cmp = sort__snoop_cmp,
849 .se_snprintf = hist_entry__snoop_snprintf,
850 .se_width_idx = HISTC_MEM_SNOOP,
851};
852
f5d05bce
AK
853static int64_t
854sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
855{
856 return left->branch_info->flags.abort !=
857 right->branch_info->flags.abort;
858}
859
860static int hist_entry__abort_snprintf(struct hist_entry *self, char *bf,
861 size_t size, unsigned int width)
862{
863 static const char *out = ".";
864
865 if (self->branch_info->flags.abort)
866 out = "A";
867 return repsep_snprintf(bf, size, "%-*s", width, out);
868}
869
870struct sort_entry sort_abort = {
871 .se_header = "Transaction abort",
872 .se_cmp = sort__abort_cmp,
873 .se_snprintf = hist_entry__abort_snprintf,
874 .se_width_idx = HISTC_ABORT,
875};
876
877static int64_t
878sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right)
879{
880 return left->branch_info->flags.in_tx !=
881 right->branch_info->flags.in_tx;
882}
883
884static int hist_entry__in_tx_snprintf(struct hist_entry *self, char *bf,
885 size_t size, unsigned int width)
886{
887 static const char *out = ".";
888
889 if (self->branch_info->flags.in_tx)
890 out = "T";
891
892 return repsep_snprintf(bf, size, "%-*s", width, out);
893}
894
895struct sort_entry sort_in_tx = {
896 .se_header = "Branch in transaction",
897 .se_cmp = sort__in_tx_cmp,
898 .se_snprintf = hist_entry__in_tx_snprintf,
899 .se_width_idx = HISTC_IN_TX,
900};
901
475eeab9
AK
902static int64_t
903sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
904{
905 return left->transaction - right->transaction;
906}
907
908static inline char *add_str(char *p, const char *str)
909{
910 strcpy(p, str);
911 return p + strlen(str);
912}
913
914static struct txbit {
915 unsigned flag;
916 const char *name;
917 int skip_for_len;
918} txbits[] = {
919 { PERF_TXN_ELISION, "EL ", 0 },
920 { PERF_TXN_TRANSACTION, "TX ", 1 },
921 { PERF_TXN_SYNC, "SYNC ", 1 },
922 { PERF_TXN_ASYNC, "ASYNC ", 0 },
923 { PERF_TXN_RETRY, "RETRY ", 0 },
924 { PERF_TXN_CONFLICT, "CON ", 0 },
925 { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 },
926 { PERF_TXN_CAPACITY_READ, "CAP-READ ", 0 },
927 { 0, NULL, 0 }
928};
929
930int hist_entry__transaction_len(void)
931{
932 int i;
933 int len = 0;
934
935 for (i = 0; txbits[i].name; i++) {
936 if (!txbits[i].skip_for_len)
937 len += strlen(txbits[i].name);
938 }
939 len += 4; /* :XX<space> */
940 return len;
941}
942
943static int hist_entry__transaction_snprintf(struct hist_entry *self, char *bf,
944 size_t size, unsigned int width)
945{
946 u64 t = self->transaction;
947 char buf[128];
948 char *p = buf;
949 int i;
950
951 buf[0] = 0;
952 for (i = 0; txbits[i].name; i++)
953 if (txbits[i].flag & t)
954 p = add_str(p, txbits[i].name);
955 if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC)))
956 p = add_str(p, "NEITHER ");
957 if (t & PERF_TXN_ABORT_MASK) {
958 sprintf(p, ":%" PRIx64,
959 (t & PERF_TXN_ABORT_MASK) >>
960 PERF_TXN_ABORT_SHIFT);
961 p += strlen(p);
962 }
963
964 return repsep_snprintf(bf, size, "%-*s", width, buf);
965}
966
967struct sort_entry sort_transaction = {
968 .se_header = "Transaction ",
969 .se_cmp = sort__transaction_cmp,
970 .se_snprintf = hist_entry__transaction_snprintf,
971 .se_width_idx = HISTC_TRANSACTION,
972};
973
872a878f
FW
974struct sort_dimension {
975 const char *name;
976 struct sort_entry *entry;
977 int taken;
978};
979
b5387528
RAV
980#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
981
fc5871ed 982static struct sort_dimension common_sort_dimensions[] = {
b5387528
RAV
983 DIM(SORT_PID, "pid", sort_thread),
984 DIM(SORT_COMM, "comm", sort_comm),
985 DIM(SORT_DSO, "dso", sort_dso),
b5387528 986 DIM(SORT_SYM, "symbol", sort_sym),
b5387528
RAV
987 DIM(SORT_PARENT, "parent", sort_parent),
988 DIM(SORT_CPU, "cpu", sort_cpu),
409a8be6 989 DIM(SORT_SRCLINE, "srcline", sort_srcline),
f9ea55d0
AK
990 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
991 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
475eeab9 992 DIM(SORT_TRANSACTION, "transaction", sort_transaction),
872a878f
FW
993};
994
fc5871ed
NK
995#undef DIM
996
997#define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
998
999static struct sort_dimension bstack_sort_dimensions[] = {
1000 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
1001 DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
1002 DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
1003 DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
1004 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
f5d05bce
AK
1005 DIM(SORT_IN_TX, "in_tx", sort_in_tx),
1006 DIM(SORT_ABORT, "abort", sort_abort),
fc5871ed
NK
1007};
1008
1009#undef DIM
1010
afab87b9
NK
1011#define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
1012
1013static struct sort_dimension memory_sort_dimensions[] = {
afab87b9
NK
1014 DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
1015 DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
1016 DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
1017 DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
1018 DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
1019 DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
1020};
1021
1022#undef DIM
1023
2f532d09
NK
1024static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx)
1025{
1026 if (sd->taken)
1027 return;
1028
1029 if (sd->entry->se_collapse)
1030 sort__need_collapse = 1;
1031
1032 if (list_empty(&hist_entry__sort_list))
1033 sort__first_dimension = idx;
1034
1035 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
1036 sd->taken = 1;
1037}
1038
dd68ada2
JK
1039int sort_dimension__add(const char *tok)
1040{
1041 unsigned int i;
1042
fc5871ed
NK
1043 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1044 struct sort_dimension *sd = &common_sort_dimensions[i];
dd68ada2 1045
dd68ada2
JK
1046 if (strncasecmp(tok, sd->name, strlen(tok)))
1047 continue;
fc5871ed 1048
dd68ada2
JK
1049 if (sd->entry == &sort_parent) {
1050 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
1051 if (ret) {
1052 char err[BUFSIZ];
1053
1054 regerror(ret, &parent_regex, err, sizeof(err));
2aefa4f7
ACM
1055 pr_err("Invalid regex: %s\n%s", parent_pattern, err);
1056 return -EINVAL;
dd68ada2
JK
1057 }
1058 sort__has_parent = 1;
930477bd 1059 } else if (sd->entry == &sort_sym) {
1af55640 1060 sort__has_sym = 1;
dd68ada2
JK
1061 }
1062
2f532d09 1063 __sort_dimension__add(sd, i);
dd68ada2
JK
1064 return 0;
1065 }
fc5871ed
NK
1066
1067 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1068 struct sort_dimension *sd = &bstack_sort_dimensions[i];
1069
1070 if (strncasecmp(tok, sd->name, strlen(tok)))
1071 continue;
1072
55369fc1 1073 if (sort__mode != SORT_MODE__BRANCH)
fc5871ed
NK
1074 return -EINVAL;
1075
1076 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
1077 sort__has_sym = 1;
1078
2f532d09 1079 __sort_dimension__add(sd, i + __SORT_BRANCH_STACK);
fc5871ed
NK
1080 return 0;
1081 }
1082
afab87b9
NK
1083 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1084 struct sort_dimension *sd = &memory_sort_dimensions[i];
1085
1086 if (strncasecmp(tok, sd->name, strlen(tok)))
1087 continue;
1088
1089 if (sort__mode != SORT_MODE__MEMORY)
1090 return -EINVAL;
1091
1092 if (sd->entry == &sort_mem_daddr_sym)
1093 sort__has_sym = 1;
1094
1095 __sort_dimension__add(sd, i + __SORT_MEMORY_MODE);
1096 return 0;
1097 }
1098
dd68ada2
JK
1099 return -ESRCH;
1100}
c8829c7a 1101
55309985 1102int setup_sorting(void)
c8829c7a
ACM
1103{
1104 char *tmp, *tok, *str = strdup(sort_order);
55309985 1105 int ret = 0;
c8829c7a 1106
5936f54d
NK
1107 if (str == NULL) {
1108 error("Not enough memory to setup sort keys");
1109 return -ENOMEM;
1110 }
1111
c8829c7a
ACM
1112 for (tok = strtok_r(str, ", ", &tmp);
1113 tok; tok = strtok_r(NULL, ", ", &tmp)) {
55309985 1114 ret = sort_dimension__add(tok);
fc5871ed
NK
1115 if (ret == -EINVAL) {
1116 error("Invalid --sort key: `%s'", tok);
55309985 1117 break;
fc5871ed 1118 } else if (ret == -ESRCH) {
c8829c7a 1119 error("Unknown --sort key: `%s'", tok);
55309985 1120 break;
c8829c7a
ACM
1121 }
1122 }
1123
1124 free(str);
55309985 1125 return ret;
c8829c7a 1126}
c351c281 1127
08e71542
NK
1128static void sort_entry__setup_elide(struct sort_entry *self,
1129 struct strlist *list,
1130 const char *list_name, FILE *fp)
c351c281
ACM
1131{
1132 if (list && strlist__nr_entries(list) == 1) {
1133 if (fp != NULL)
1134 fprintf(fp, "# %s: %s\n", list_name,
1135 strlist__entry(list, 0)->s);
1136 self->elide = true;
1137 }
1138}
08e71542
NK
1139
1140void sort__setup_elide(FILE *output)
1141{
1142 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1143 "dso", output);
1144 sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list,
1145 "comm", output);
1146 sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list,
1147 "symbol", output);
1148
1149 if (sort__mode == SORT_MODE__BRANCH) {
1150 sort_entry__setup_elide(&sort_dso_from,
1151 symbol_conf.dso_from_list,
1152 "dso_from", output);
1153 sort_entry__setup_elide(&sort_dso_to,
1154 symbol_conf.dso_to_list,
1155 "dso_to", output);
1156 sort_entry__setup_elide(&sort_sym_from,
1157 symbol_conf.sym_from_list,
1158 "sym_from", output);
1159 sort_entry__setup_elide(&sort_sym_to,
1160 symbol_conf.sym_to_list,
1161 "sym_to", output);
1162 } else if (sort__mode == SORT_MODE__MEMORY) {
1163 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1164 "symbol_daddr", output);
1165 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1166 "dso_daddr", output);
1167 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1168 "mem", output);
1169 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1170 "local_weight", output);
1171 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1172 "tlb", output);
1173 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1174 "snoop", output);
1175 }
1176
1177}