perf machine: Detect data vs. text mappings
[deliverable/linux.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;
993ac88d 12int sort__branch_mode = -1; /* -1 = means not set */
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 ",
197 BITS_PER_LONG / 4, 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",
205 ip - sym->start);
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),
05484298
AK
874 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
875 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
98a3b32c
SE
876 DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
877 DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
878 DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
879 DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
880 DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
881 DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
872a878f
FW
882};
883
fc5871ed
NK
884#undef DIM
885
886#define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
887
888static struct sort_dimension bstack_sort_dimensions[] = {
889 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
890 DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
891 DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
892 DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
893 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
894};
895
896#undef DIM
897
dd68ada2
JK
898int sort_dimension__add(const char *tok)
899{
900 unsigned int i;
901
fc5871ed
NK
902 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
903 struct sort_dimension *sd = &common_sort_dimensions[i];
dd68ada2 904
dd68ada2
JK
905 if (strncasecmp(tok, sd->name, strlen(tok)))
906 continue;
fc5871ed 907
dd68ada2
JK
908 if (sd->entry == &sort_parent) {
909 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
910 if (ret) {
911 char err[BUFSIZ];
912
913 regerror(ret, &parent_regex, err, sizeof(err));
2aefa4f7
ACM
914 pr_err("Invalid regex: %s\n%s", parent_pattern, err);
915 return -EINVAL;
dd68ada2
JK
916 }
917 sort__has_parent = 1;
98a3b32c
SE
918 } else if (sd->entry == &sort_sym ||
919 sd->entry == &sort_sym_from ||
920 sd->entry == &sort_sym_to ||
921 sd->entry == &sort_mem_daddr_sym) {
1af55640 922 sort__has_sym = 1;
dd68ada2
JK
923 }
924
fd8ea212
FW
925 if (sd->taken)
926 return 0;
927
928 if (sd->entry->se_collapse)
929 sort__need_collapse = 1;
930
6f38cf25
NK
931 if (list_empty(&hist_entry__sort_list))
932 sort__first_dimension = i;
af0a6fa4 933
dd68ada2
JK
934 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
935 sd->taken = 1;
936
937 return 0;
938 }
fc5871ed
NK
939
940 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
941 struct sort_dimension *sd = &bstack_sort_dimensions[i];
942
943 if (strncasecmp(tok, sd->name, strlen(tok)))
944 continue;
945
946 if (sort__branch_mode != 1)
947 return -EINVAL;
948
949 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
950 sort__has_sym = 1;
951
952 if (sd->taken)
953 return 0;
954
955 if (sd->entry->se_collapse)
956 sort__need_collapse = 1;
957
958 if (list_empty(&hist_entry__sort_list))
959 sort__first_dimension = i + __SORT_BRANCH_STACK;
960
961 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
962 sd->taken = 1;
963
964 return 0;
965 }
966
dd68ada2
JK
967 return -ESRCH;
968}
c8829c7a 969
55309985 970int setup_sorting(void)
c8829c7a
ACM
971{
972 char *tmp, *tok, *str = strdup(sort_order);
55309985 973 int ret = 0;
c8829c7a 974
5936f54d
NK
975 if (str == NULL) {
976 error("Not enough memory to setup sort keys");
977 return -ENOMEM;
978 }
979
c8829c7a
ACM
980 for (tok = strtok_r(str, ", ", &tmp);
981 tok; tok = strtok_r(NULL, ", ", &tmp)) {
55309985 982 ret = sort_dimension__add(tok);
fc5871ed
NK
983 if (ret == -EINVAL) {
984 error("Invalid --sort key: `%s'", tok);
55309985 985 break;
fc5871ed 986 } else if (ret == -ESRCH) {
c8829c7a 987 error("Unknown --sort key: `%s'", tok);
55309985 988 break;
c8829c7a
ACM
989 }
990 }
991
992 free(str);
55309985 993 return ret;
c8829c7a 994}
c351c281
ACM
995
996void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
997 const char *list_name, FILE *fp)
998{
999 if (list && strlist__nr_entries(list) == 1) {
1000 if (fp != NULL)
1001 fprintf(fp, "# %s: %s\n", list_name,
1002 strlist__entry(list, 0)->s);
1003 self->elide = true;
1004 }
1005}
This page took 0.198879 seconds and 5 git commands to generate.