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