perf tools: Compare hists comm by addresses
[deliverable/linux.git] / tools / perf / util / sort.c
1 #include "sort.h"
2 #include "hist.h"
3 #include "symbol.h"
4
5 regex_t parent_regex;
6 const char default_parent_pattern[] = "^sys_|^do_page_fault";
7 const char *parent_pattern = default_parent_pattern;
8 const char default_sort_order[] = "comm,dso,symbol";
9 const char *sort_order = default_sort_order;
10 regex_t ignore_callees_regex;
11 int have_ignore_callees = 0;
12 int sort__need_collapse = 0;
13 int sort__has_parent = 0;
14 int sort__has_sym = 0;
15 enum sort_mode sort__mode = SORT_MODE__NORMAL;
16
17 enum sort_type sort__first_dimension;
18
19 LIST_HEAD(hist_entry__sort_list);
20
21 static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
22 {
23 int n;
24 va_list ap;
25
26 va_start(ap, fmt);
27 n = vsnprintf(bf, size, fmt, ap);
28 if (symbol_conf.field_sep && n > 0) {
29 char *sep = bf;
30
31 while (1) {
32 sep = strchr(sep, *symbol_conf.field_sep);
33 if (sep == NULL)
34 break;
35 *sep = '.';
36 }
37 }
38 va_end(ap);
39
40 if (n >= (int)size)
41 return size - 1;
42 return n;
43 }
44
45 static int64_t cmp_null(const void *l, const 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
57 static int64_t
58 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
59 {
60 return right->thread->tid - left->thread->tid;
61 }
62
63 static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,
64 size_t size, unsigned int width)
65 {
66 const char *comm = thread__comm_str(he->thread);
67 return repsep_snprintf(bf, size, "%*s:%5d", width - 6,
68 comm ?: "", he->thread->tid);
69 }
70
71 struct 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
80 static int64_t
81 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
82 {
83 /* Compare the addr that should be unique among comm */
84 return thread__comm_str(right->thread) - thread__comm_str(left->thread);
85 }
86
87 static int64_t
88 sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
89 {
90 const char *comm_l = thread__comm_str(left->thread);
91 const char *comm_r = thread__comm_str(right->thread);
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
99 static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
100 size_t size, unsigned int width)
101 {
102 return repsep_snprintf(bf, size, "%*s", width, thread__comm_str(he->thread));
103 }
104
105 struct 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
115 static 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
135 static int64_t
136 sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
137 {
138 return _sort__dso_cmp(left->ms.map, right->ms.map);
139 }
140
141 static 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
153 static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,
154 size_t size, unsigned int width)
155 {
156 return _hist_entry__dso_snprintf(he->ms.map, bf, size, width);
157 }
158
159 struct 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 */
167
168 static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
169 {
170 u64 ip_l, ip_r;
171
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
178 ip_l = sym_l->start;
179 ip_r = sym_r->start;
180
181 return (int64_t)(ip_r - ip_l);
182 }
183
184 static int64_t
185 sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
186 {
187 int64_t ret;
188
189 if (!left->ms.sym && !right->ms.sym)
190 return right->level - left->level;
191
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
200 return _sort__sym_cmp(left->ms.sym, right->ms.sym);
201 }
202
203 static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
204 u64 ip, char level, char *bf, size_t size,
205 unsigned int width)
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 ",
212 BITS_PER_LONG / 4 + 2, ip, o);
213 }
214
215 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
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",
220 ip - map->unmap_ip(map, sym->start));
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 {
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;
237 }
238
239 static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,
240 size_t size, unsigned int width)
241 {
242 return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip,
243 he->level, bf, size, width);
244 }
245
246 struct 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 };
252
253 /* --sort srcline */
254
255 static int64_t
256 sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
257 {
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);
277 }
278
279 static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
280 size_t size,
281 unsigned int width __maybe_unused)
282 {
283 return repsep_snprintf(bf, size, "%s", he->srcline);
284 }
285
286 struct 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
293 /* --sort parent */
294
295 static int64_t
296 sort__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
307 static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
308 size_t size, unsigned int width)
309 {
310 return repsep_snprintf(bf, size, "%-*s", width,
311 he->parent ? he->parent->name : "[other]");
312 }
313
314 struct 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
321 /* --sort cpu */
322
323 static int64_t
324 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
325 {
326 return right->cpu - left->cpu;
327 }
328
329 static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf,
330 size_t size, unsigned int width)
331 {
332 return repsep_snprintf(bf, size, "%*d", width, he->cpu);
333 }
334
335 struct 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
342 /* sort keys for branch stacks */
343
344 static int64_t
345 sort__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
351 static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,
352 size_t size, unsigned int width)
353 {
354 return _hist_entry__dso_snprintf(he->branch_info->from.map,
355 bf, size, width);
356 }
357
358 static int64_t
359 sort__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
365 static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,
366 size_t size, unsigned int width)
367 {
368 return _hist_entry__dso_snprintf(he->branch_info->to.map,
369 bf, size, width);
370 }
371
372 static int64_t
373 sort__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
381 return _sort__sym_cmp(from_l->sym, from_r->sym);
382 }
383
384 static int64_t
385 sort__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
393 return _sort__sym_cmp(to_l->sym, to_r->sym);
394 }
395
396 static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,
397 size_t size, unsigned int width)
398 {
399 struct addr_map_symbol *from = &he->branch_info->from;
400 return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
401 he->level, bf, size, width);
402
403 }
404
405 static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,
406 size_t size, unsigned int width)
407 {
408 struct addr_map_symbol *to = &he->branch_info->to;
409 return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
410 he->level, bf, size, width);
411
412 }
413
414 struct 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
421 struct 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
428 struct 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
435 struct 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
442 static int64_t
443 sort__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
453 static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,
454 size_t size, unsigned int width){
455 static const char *out = "N/A";
456
457 if (he->branch_info->flags.predicted)
458 out = "N";
459 else if (he->branch_info->flags.mispred)
460 out = "Y";
461
462 return repsep_snprintf(bf, size, "%-*s", width, out);
463 }
464
465 /* --sort daddr_sym */
466 static int64_t
467 sort__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
479 static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,
480 size_t size, unsigned int width)
481 {
482 uint64_t addr = 0;
483 struct map *map = NULL;
484 struct symbol *sym = NULL;
485
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;
490 }
491 return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
492 width);
493 }
494
495 static int64_t
496 sort__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
509 static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,
510 size_t size, unsigned int width)
511 {
512 struct map *map = NULL;
513
514 if (he->mem_info)
515 map = he->mem_info->daddr.map;
516
517 return _hist_entry__dso_snprintf(map, bf, size, width);
518 }
519
520 static int64_t
521 sort__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
539 static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,
540 size_t size, unsigned int width)
541 {
542 const char *out;
543 u64 mask = PERF_MEM_LOCK_NA;
544
545 if (he->mem_info)
546 mask = he->mem_info->data_src.mem_lock;
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
558 static int64_t
559 sort__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
577 static 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
588 static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,
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
599 if (he->mem_info)
600 m = he->mem_info->data_src.mem_dtlb;
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
628 static int64_t
629 sort__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
647 static 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
665 static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,
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
674 if (he->mem_info)
675 m = he->mem_info->data_src.mem_lvl;
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
705 static int64_t
706 sort__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
724 static 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
733 static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
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
743 if (he->mem_info)
744 m = he->mem_info->data_src.mem_snoop;
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
763 struct 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
770 static u64 he_weight(struct hist_entry *he)
771 {
772 return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
773 }
774
775 static int64_t
776 sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
777 {
778 return he_weight(left) - he_weight(right);
779 }
780
781 static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,
782 size_t size, unsigned int width)
783 {
784 return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he));
785 }
786
787 struct 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
794 static int64_t
795 sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
796 {
797 return left->stat.weight - right->stat.weight;
798 }
799
800 static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,
801 size_t size, unsigned int width)
802 {
803 return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight);
804 }
805
806 struct 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
813 struct 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
820 struct 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
827 struct 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
834 struct 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
841 struct 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
848 struct 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
855 static int64_t
856 sort__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
862 static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf,
863 size_t size, unsigned int width)
864 {
865 static const char *out = ".";
866
867 if (he->branch_info->flags.abort)
868 out = "A";
869 return repsep_snprintf(bf, size, "%-*s", width, out);
870 }
871
872 struct 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
879 static int64_t
880 sort__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
886 static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf,
887 size_t size, unsigned int width)
888 {
889 static const char *out = ".";
890
891 if (he->branch_info->flags.in_tx)
892 out = "T";
893
894 return repsep_snprintf(bf, size, "%-*s", width, out);
895 }
896
897 struct 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
904 static int64_t
905 sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
906 {
907 return left->transaction - right->transaction;
908 }
909
910 static inline char *add_str(char *p, const char *str)
911 {
912 strcpy(p, str);
913 return p + strlen(str);
914 }
915
916 static 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
932 int 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
945 static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf,
946 size_t size, unsigned int width)
947 {
948 u64 t = he->transaction;
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
969 struct 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
976 struct sort_dimension {
977 const char *name;
978 struct sort_entry *entry;
979 int taken;
980 };
981
982 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
983
984 static struct sort_dimension common_sort_dimensions[] = {
985 DIM(SORT_PID, "pid", sort_thread),
986 DIM(SORT_COMM, "comm", sort_comm),
987 DIM(SORT_DSO, "dso", sort_dso),
988 DIM(SORT_SYM, "symbol", sort_sym),
989 DIM(SORT_PARENT, "parent", sort_parent),
990 DIM(SORT_CPU, "cpu", sort_cpu),
991 DIM(SORT_SRCLINE, "srcline", sort_srcline),
992 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
993 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
994 DIM(SORT_TRANSACTION, "transaction", sort_transaction),
995 };
996
997 #undef DIM
998
999 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
1000
1001 static 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),
1007 DIM(SORT_IN_TX, "in_tx", sort_in_tx),
1008 DIM(SORT_ABORT, "abort", sort_abort),
1009 };
1010
1011 #undef DIM
1012
1013 #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
1014
1015 static struct sort_dimension memory_sort_dimensions[] = {
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
1026 static 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
1041 int sort_dimension__add(const char *tok)
1042 {
1043 unsigned int i;
1044
1045 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1046 struct sort_dimension *sd = &common_sort_dimensions[i];
1047
1048 if (strncasecmp(tok, sd->name, strlen(tok)))
1049 continue;
1050
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));
1057 pr_err("Invalid regex: %s\n%s", parent_pattern, err);
1058 return -EINVAL;
1059 }
1060 sort__has_parent = 1;
1061 } else if (sd->entry == &sort_sym) {
1062 sort__has_sym = 1;
1063 }
1064
1065 __sort_dimension__add(sd, i);
1066 return 0;
1067 }
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
1075 if (sort__mode != SORT_MODE__BRANCH)
1076 return -EINVAL;
1077
1078 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
1079 sort__has_sym = 1;
1080
1081 __sort_dimension__add(sd, i + __SORT_BRANCH_STACK);
1082 return 0;
1083 }
1084
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
1101 return -ESRCH;
1102 }
1103
1104 int setup_sorting(void)
1105 {
1106 char *tmp, *tok, *str = strdup(sort_order);
1107 int ret = 0;
1108
1109 if (str == NULL) {
1110 error("Not enough memory to setup sort keys");
1111 return -ENOMEM;
1112 }
1113
1114 for (tok = strtok_r(str, ", ", &tmp);
1115 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1116 ret = sort_dimension__add(tok);
1117 if (ret == -EINVAL) {
1118 error("Invalid --sort key: `%s'", tok);
1119 break;
1120 } else if (ret == -ESRCH) {
1121 error("Unknown --sort key: `%s'", tok);
1122 break;
1123 }
1124 }
1125
1126 free(str);
1127 return ret;
1128 }
1129
1130 static void sort_entry__setup_elide(struct sort_entry *se,
1131 struct strlist *list,
1132 const char *list_name, FILE *fp)
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);
1138 se->elide = true;
1139 }
1140 }
1141
1142 void 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 }
This page took 0.111857 seconds and 5 git commands to generate.