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