perf symbols: Fix a memory leak in vmlinux_path__init()
[deliverable/linux.git] / tools / perf / util / sort.c
1 #include <sys/mman.h>
2 #include "sort.h"
3 #include "hist.h"
4 #include "comm.h"
5 #include "symbol.h"
6 #include "evsel.h"
7
8 regex_t parent_regex;
9 const char default_parent_pattern[] = "^sys_|^do_page_fault";
10 const char *parent_pattern = default_parent_pattern;
11 const char default_sort_order[] = "comm,dso,symbol";
12 const char default_branch_sort_order[] = "comm,dso_from,symbol_from,dso_to,symbol_to";
13 const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
14 const char default_top_sort_order[] = "dso,symbol";
15 const char default_diff_sort_order[] = "dso,symbol";
16 const char *sort_order;
17 const char *field_order;
18 regex_t ignore_callees_regex;
19 int have_ignore_callees = 0;
20 int sort__need_collapse = 0;
21 int sort__has_parent = 0;
22 int sort__has_sym = 0;
23 int sort__has_dso = 0;
24 enum sort_mode sort__mode = SORT_MODE__NORMAL;
25
26
27 static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
28 {
29 int n;
30 va_list ap;
31
32 va_start(ap, fmt);
33 n = vsnprintf(bf, size, fmt, ap);
34 if (symbol_conf.field_sep && n > 0) {
35 char *sep = bf;
36
37 while (1) {
38 sep = strchr(sep, *symbol_conf.field_sep);
39 if (sep == NULL)
40 break;
41 *sep = '.';
42 }
43 }
44 va_end(ap);
45
46 if (n >= (int)size)
47 return size - 1;
48 return n;
49 }
50
51 static int64_t cmp_null(const void *l, const void *r)
52 {
53 if (!l && !r)
54 return 0;
55 else if (!l)
56 return -1;
57 else
58 return 1;
59 }
60
61 /* --sort pid */
62
63 static int64_t
64 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
65 {
66 return right->thread->tid - left->thread->tid;
67 }
68
69 static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,
70 size_t size, unsigned int width)
71 {
72 const char *comm = thread__comm_str(he->thread);
73
74 width = max(7U, width) - 6;
75 return repsep_snprintf(bf, size, "%5d:%-*.*s", he->thread->tid,
76 width, width, comm ?: "");
77 }
78
79 struct sort_entry sort_thread = {
80 .se_header = " Pid:Command",
81 .se_cmp = sort__thread_cmp,
82 .se_snprintf = hist_entry__thread_snprintf,
83 .se_width_idx = HISTC_THREAD,
84 };
85
86 /* --sort comm */
87
88 static int64_t
89 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
90 {
91 /* Compare the addr that should be unique among comm */
92 return comm__str(right->comm) - comm__str(left->comm);
93 }
94
95 static int64_t
96 sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
97 {
98 /* Compare the addr that should be unique among comm */
99 return comm__str(right->comm) - comm__str(left->comm);
100 }
101
102 static int64_t
103 sort__comm_sort(struct hist_entry *left, struct hist_entry *right)
104 {
105 return strcmp(comm__str(right->comm), comm__str(left->comm));
106 }
107
108 static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
109 size_t size, unsigned int width)
110 {
111 return repsep_snprintf(bf, size, "%-*.*s", width, width, comm__str(he->comm));
112 }
113
114 struct sort_entry sort_comm = {
115 .se_header = "Command",
116 .se_cmp = sort__comm_cmp,
117 .se_collapse = sort__comm_collapse,
118 .se_sort = sort__comm_sort,
119 .se_snprintf = hist_entry__comm_snprintf,
120 .se_width_idx = HISTC_COMM,
121 };
122
123 /* --sort dso */
124
125 static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
126 {
127 struct dso *dso_l = map_l ? map_l->dso : NULL;
128 struct dso *dso_r = map_r ? map_r->dso : NULL;
129 const char *dso_name_l, *dso_name_r;
130
131 if (!dso_l || !dso_r)
132 return cmp_null(dso_r, dso_l);
133
134 if (verbose) {
135 dso_name_l = dso_l->long_name;
136 dso_name_r = dso_r->long_name;
137 } else {
138 dso_name_l = dso_l->short_name;
139 dso_name_r = dso_r->short_name;
140 }
141
142 return strcmp(dso_name_l, dso_name_r);
143 }
144
145 static int64_t
146 sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
147 {
148 return _sort__dso_cmp(right->ms.map, left->ms.map);
149 }
150
151 static int _hist_entry__dso_snprintf(struct map *map, char *bf,
152 size_t size, unsigned int width)
153 {
154 if (map && map->dso) {
155 const char *dso_name = !verbose ? map->dso->short_name :
156 map->dso->long_name;
157 return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name);
158 }
159
160 return repsep_snprintf(bf, size, "%-*.*s", width, width, "[unknown]");
161 }
162
163 static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,
164 size_t size, unsigned int width)
165 {
166 return _hist_entry__dso_snprintf(he->ms.map, bf, size, width);
167 }
168
169 struct sort_entry sort_dso = {
170 .se_header = "Shared Object",
171 .se_cmp = sort__dso_cmp,
172 .se_snprintf = hist_entry__dso_snprintf,
173 .se_width_idx = HISTC_DSO,
174 };
175
176 /* --sort symbol */
177
178 static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip)
179 {
180 return (int64_t)(right_ip - left_ip);
181 }
182
183 static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
184 {
185 u64 ip_l, ip_r;
186
187 if (!sym_l || !sym_r)
188 return cmp_null(sym_l, sym_r);
189
190 if (sym_l == sym_r)
191 return 0;
192
193 ip_l = sym_l->start;
194 ip_r = sym_r->start;
195
196 return (int64_t)(ip_r - ip_l);
197 }
198
199 static int64_t
200 sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
201 {
202 int64_t ret;
203
204 if (!left->ms.sym && !right->ms.sym)
205 return _sort__addr_cmp(left->ip, right->ip);
206
207 /*
208 * comparing symbol address alone is not enough since it's a
209 * relative address within a dso.
210 */
211 if (!sort__has_dso) {
212 ret = sort__dso_cmp(left, right);
213 if (ret != 0)
214 return ret;
215 }
216
217 return _sort__sym_cmp(left->ms.sym, right->ms.sym);
218 }
219
220 static int64_t
221 sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
222 {
223 if (!left->ms.sym || !right->ms.sym)
224 return cmp_null(left->ms.sym, right->ms.sym);
225
226 return strcmp(right->ms.sym->name, left->ms.sym->name);
227 }
228
229 static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
230 u64 ip, char level, char *bf, size_t size,
231 unsigned int width)
232 {
233 size_t ret = 0;
234
235 if (verbose) {
236 char o = map ? dso__symtab_origin(map->dso) : '!';
237 ret += repsep_snprintf(bf, size, "%-#*llx %c ",
238 BITS_PER_LONG / 4 + 2, ip, o);
239 }
240
241 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
242 if (sym && map) {
243 if (map->type == MAP__VARIABLE) {
244 ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
245 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
246 ip - map->unmap_ip(map, sym->start));
247 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
248 width - ret, "");
249 } else {
250 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
251 width - ret,
252 sym->name);
253 }
254 } else {
255 size_t len = BITS_PER_LONG / 4;
256 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
257 len, ip);
258 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
259 width - ret, "");
260 }
261
262 if (ret > width)
263 bf[width] = '\0';
264
265 return width;
266 }
267
268 static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,
269 size_t size, unsigned int width)
270 {
271 return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip,
272 he->level, bf, size, width);
273 }
274
275 struct sort_entry sort_sym = {
276 .se_header = "Symbol",
277 .se_cmp = sort__sym_cmp,
278 .se_sort = sort__sym_sort,
279 .se_snprintf = hist_entry__sym_snprintf,
280 .se_width_idx = HISTC_SYMBOL,
281 };
282
283 /* --sort srcline */
284
285 static int64_t
286 sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
287 {
288 if (!left->srcline) {
289 if (!left->ms.map)
290 left->srcline = SRCLINE_UNKNOWN;
291 else {
292 struct map *map = left->ms.map;
293 left->srcline = get_srcline(map->dso,
294 map__rip_2objdump(map, left->ip));
295 }
296 }
297 if (!right->srcline) {
298 if (!right->ms.map)
299 right->srcline = SRCLINE_UNKNOWN;
300 else {
301 struct map *map = right->ms.map;
302 right->srcline = get_srcline(map->dso,
303 map__rip_2objdump(map, right->ip));
304 }
305 }
306 return strcmp(right->srcline, left->srcline);
307 }
308
309 static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
310 size_t size, unsigned int width)
311 {
312 return repsep_snprintf(bf, size, "%*.*-s", width, width, he->srcline);
313 }
314
315 struct sort_entry sort_srcline = {
316 .se_header = "Source:Line",
317 .se_cmp = sort__srcline_cmp,
318 .se_snprintf = hist_entry__srcline_snprintf,
319 .se_width_idx = HISTC_SRCLINE,
320 };
321
322 /* --sort parent */
323
324 static int64_t
325 sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
326 {
327 struct symbol *sym_l = left->parent;
328 struct symbol *sym_r = right->parent;
329
330 if (!sym_l || !sym_r)
331 return cmp_null(sym_l, sym_r);
332
333 return strcmp(sym_r->name, sym_l->name);
334 }
335
336 static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
337 size_t size, unsigned int width)
338 {
339 return repsep_snprintf(bf, size, "%-*.*s", width, width,
340 he->parent ? he->parent->name : "[other]");
341 }
342
343 struct sort_entry sort_parent = {
344 .se_header = "Parent symbol",
345 .se_cmp = sort__parent_cmp,
346 .se_snprintf = hist_entry__parent_snprintf,
347 .se_width_idx = HISTC_PARENT,
348 };
349
350 /* --sort cpu */
351
352 static int64_t
353 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
354 {
355 return right->cpu - left->cpu;
356 }
357
358 static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf,
359 size_t size, unsigned int width)
360 {
361 return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu);
362 }
363
364 struct sort_entry sort_cpu = {
365 .se_header = "CPU",
366 .se_cmp = sort__cpu_cmp,
367 .se_snprintf = hist_entry__cpu_snprintf,
368 .se_width_idx = HISTC_CPU,
369 };
370
371 /* sort keys for branch stacks */
372
373 static int64_t
374 sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
375 {
376 return _sort__dso_cmp(left->branch_info->from.map,
377 right->branch_info->from.map);
378 }
379
380 static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,
381 size_t size, unsigned int width)
382 {
383 return _hist_entry__dso_snprintf(he->branch_info->from.map,
384 bf, size, width);
385 }
386
387 static int64_t
388 sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
389 {
390 return _sort__dso_cmp(left->branch_info->to.map,
391 right->branch_info->to.map);
392 }
393
394 static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,
395 size_t size, unsigned int width)
396 {
397 return _hist_entry__dso_snprintf(he->branch_info->to.map,
398 bf, size, width);
399 }
400
401 static int64_t
402 sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
403 {
404 struct addr_map_symbol *from_l = &left->branch_info->from;
405 struct addr_map_symbol *from_r = &right->branch_info->from;
406
407 if (!from_l->sym && !from_r->sym)
408 return _sort__addr_cmp(from_l->addr, from_r->addr);
409
410 return _sort__sym_cmp(from_l->sym, from_r->sym);
411 }
412
413 static int64_t
414 sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
415 {
416 struct addr_map_symbol *to_l = &left->branch_info->to;
417 struct addr_map_symbol *to_r = &right->branch_info->to;
418
419 if (!to_l->sym && !to_r->sym)
420 return _sort__addr_cmp(to_l->addr, to_r->addr);
421
422 return _sort__sym_cmp(to_l->sym, to_r->sym);
423 }
424
425 static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,
426 size_t size, unsigned int width)
427 {
428 struct addr_map_symbol *from = &he->branch_info->from;
429 return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
430 he->level, bf, size, width);
431
432 }
433
434 static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,
435 size_t size, unsigned int width)
436 {
437 struct addr_map_symbol *to = &he->branch_info->to;
438 return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
439 he->level, bf, size, width);
440
441 }
442
443 struct sort_entry sort_dso_from = {
444 .se_header = "Source Shared Object",
445 .se_cmp = sort__dso_from_cmp,
446 .se_snprintf = hist_entry__dso_from_snprintf,
447 .se_width_idx = HISTC_DSO_FROM,
448 };
449
450 struct sort_entry sort_dso_to = {
451 .se_header = "Target Shared Object",
452 .se_cmp = sort__dso_to_cmp,
453 .se_snprintf = hist_entry__dso_to_snprintf,
454 .se_width_idx = HISTC_DSO_TO,
455 };
456
457 struct sort_entry sort_sym_from = {
458 .se_header = "Source Symbol",
459 .se_cmp = sort__sym_from_cmp,
460 .se_snprintf = hist_entry__sym_from_snprintf,
461 .se_width_idx = HISTC_SYMBOL_FROM,
462 };
463
464 struct sort_entry sort_sym_to = {
465 .se_header = "Target Symbol",
466 .se_cmp = sort__sym_to_cmp,
467 .se_snprintf = hist_entry__sym_to_snprintf,
468 .se_width_idx = HISTC_SYMBOL_TO,
469 };
470
471 static int64_t
472 sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
473 {
474 const unsigned char mp = left->branch_info->flags.mispred !=
475 right->branch_info->flags.mispred;
476 const unsigned char p = left->branch_info->flags.predicted !=
477 right->branch_info->flags.predicted;
478
479 return mp || p;
480 }
481
482 static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,
483 size_t size, unsigned int width){
484 static const char *out = "N/A";
485
486 if (he->branch_info->flags.predicted)
487 out = "N";
488 else if (he->branch_info->flags.mispred)
489 out = "Y";
490
491 return repsep_snprintf(bf, size, "%-*.*s", width, width, out);
492 }
493
494 /* --sort daddr_sym */
495 static int64_t
496 sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
497 {
498 uint64_t l = 0, r = 0;
499
500 if (left->mem_info)
501 l = left->mem_info->daddr.addr;
502 if (right->mem_info)
503 r = right->mem_info->daddr.addr;
504
505 return (int64_t)(r - l);
506 }
507
508 static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,
509 size_t size, unsigned int width)
510 {
511 uint64_t addr = 0;
512 struct map *map = NULL;
513 struct symbol *sym = NULL;
514
515 if (he->mem_info) {
516 addr = he->mem_info->daddr.addr;
517 map = he->mem_info->daddr.map;
518 sym = he->mem_info->daddr.sym;
519 }
520 return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
521 width);
522 }
523
524 static int64_t
525 sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
526 {
527 struct map *map_l = NULL;
528 struct map *map_r = NULL;
529
530 if (left->mem_info)
531 map_l = left->mem_info->daddr.map;
532 if (right->mem_info)
533 map_r = right->mem_info->daddr.map;
534
535 return _sort__dso_cmp(map_l, map_r);
536 }
537
538 static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,
539 size_t size, unsigned int width)
540 {
541 struct map *map = NULL;
542
543 if (he->mem_info)
544 map = he->mem_info->daddr.map;
545
546 return _hist_entry__dso_snprintf(map, bf, size, width);
547 }
548
549 static int64_t
550 sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
551 {
552 union perf_mem_data_src data_src_l;
553 union perf_mem_data_src data_src_r;
554
555 if (left->mem_info)
556 data_src_l = left->mem_info->data_src;
557 else
558 data_src_l.mem_lock = PERF_MEM_LOCK_NA;
559
560 if (right->mem_info)
561 data_src_r = right->mem_info->data_src;
562 else
563 data_src_r.mem_lock = PERF_MEM_LOCK_NA;
564
565 return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
566 }
567
568 static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,
569 size_t size, unsigned int width)
570 {
571 const char *out;
572 u64 mask = PERF_MEM_LOCK_NA;
573
574 if (he->mem_info)
575 mask = he->mem_info->data_src.mem_lock;
576
577 if (mask & PERF_MEM_LOCK_NA)
578 out = "N/A";
579 else if (mask & PERF_MEM_LOCK_LOCKED)
580 out = "Yes";
581 else
582 out = "No";
583
584 return repsep_snprintf(bf, size, "%-*s", width, out);
585 }
586
587 static int64_t
588 sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
589 {
590 union perf_mem_data_src data_src_l;
591 union perf_mem_data_src data_src_r;
592
593 if (left->mem_info)
594 data_src_l = left->mem_info->data_src;
595 else
596 data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
597
598 if (right->mem_info)
599 data_src_r = right->mem_info->data_src;
600 else
601 data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
602
603 return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
604 }
605
606 static const char * const tlb_access[] = {
607 "N/A",
608 "HIT",
609 "MISS",
610 "L1",
611 "L2",
612 "Walker",
613 "Fault",
614 };
615 #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
616
617 static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,
618 size_t size, unsigned int width)
619 {
620 char out[64];
621 size_t sz = sizeof(out) - 1; /* -1 for null termination */
622 size_t l = 0, i;
623 u64 m = PERF_MEM_TLB_NA;
624 u64 hit, miss;
625
626 out[0] = '\0';
627
628 if (he->mem_info)
629 m = he->mem_info->data_src.mem_dtlb;
630
631 hit = m & PERF_MEM_TLB_HIT;
632 miss = m & PERF_MEM_TLB_MISS;
633
634 /* already taken care of */
635 m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
636
637 for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) {
638 if (!(m & 0x1))
639 continue;
640 if (l) {
641 strcat(out, " or ");
642 l += 4;
643 }
644 strncat(out, tlb_access[i], sz - l);
645 l += strlen(tlb_access[i]);
646 }
647 if (*out == '\0')
648 strcpy(out, "N/A");
649 if (hit)
650 strncat(out, " hit", sz - l);
651 if (miss)
652 strncat(out, " miss", sz - l);
653
654 return repsep_snprintf(bf, size, "%-*s", width, out);
655 }
656
657 static int64_t
658 sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
659 {
660 union perf_mem_data_src data_src_l;
661 union perf_mem_data_src data_src_r;
662
663 if (left->mem_info)
664 data_src_l = left->mem_info->data_src;
665 else
666 data_src_l.mem_lvl = PERF_MEM_LVL_NA;
667
668 if (right->mem_info)
669 data_src_r = right->mem_info->data_src;
670 else
671 data_src_r.mem_lvl = PERF_MEM_LVL_NA;
672
673 return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
674 }
675
676 static const char * const mem_lvl[] = {
677 "N/A",
678 "HIT",
679 "MISS",
680 "L1",
681 "LFB",
682 "L2",
683 "L3",
684 "Local RAM",
685 "Remote RAM (1 hop)",
686 "Remote RAM (2 hops)",
687 "Remote Cache (1 hop)",
688 "Remote Cache (2 hops)",
689 "I/O",
690 "Uncached",
691 };
692 #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
693
694 static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,
695 size_t size, unsigned int width)
696 {
697 char out[64];
698 size_t sz = sizeof(out) - 1; /* -1 for null termination */
699 size_t i, l = 0;
700 u64 m = PERF_MEM_LVL_NA;
701 u64 hit, miss;
702
703 if (he->mem_info)
704 m = he->mem_info->data_src.mem_lvl;
705
706 out[0] = '\0';
707
708 hit = m & PERF_MEM_LVL_HIT;
709 miss = m & PERF_MEM_LVL_MISS;
710
711 /* already taken care of */
712 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
713
714 for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) {
715 if (!(m & 0x1))
716 continue;
717 if (l) {
718 strcat(out, " or ");
719 l += 4;
720 }
721 strncat(out, mem_lvl[i], sz - l);
722 l += strlen(mem_lvl[i]);
723 }
724 if (*out == '\0')
725 strcpy(out, "N/A");
726 if (hit)
727 strncat(out, " hit", sz - l);
728 if (miss)
729 strncat(out, " miss", sz - l);
730
731 return repsep_snprintf(bf, size, "%-*s", width, out);
732 }
733
734 static int64_t
735 sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
736 {
737 union perf_mem_data_src data_src_l;
738 union perf_mem_data_src data_src_r;
739
740 if (left->mem_info)
741 data_src_l = left->mem_info->data_src;
742 else
743 data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
744
745 if (right->mem_info)
746 data_src_r = right->mem_info->data_src;
747 else
748 data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
749
750 return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
751 }
752
753 static const char * const snoop_access[] = {
754 "N/A",
755 "None",
756 "Miss",
757 "Hit",
758 "HitM",
759 };
760 #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
761
762 static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
763 size_t size, unsigned int width)
764 {
765 char out[64];
766 size_t sz = sizeof(out) - 1; /* -1 for null termination */
767 size_t i, l = 0;
768 u64 m = PERF_MEM_SNOOP_NA;
769
770 out[0] = '\0';
771
772 if (he->mem_info)
773 m = he->mem_info->data_src.mem_snoop;
774
775 for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {
776 if (!(m & 0x1))
777 continue;
778 if (l) {
779 strcat(out, " or ");
780 l += 4;
781 }
782 strncat(out, snoop_access[i], sz - l);
783 l += strlen(snoop_access[i]);
784 }
785
786 if (*out == '\0')
787 strcpy(out, "N/A");
788
789 return repsep_snprintf(bf, size, "%-*s", width, out);
790 }
791
792 static inline u64 cl_address(u64 address)
793 {
794 /* return the cacheline of the address */
795 return (address & ~(cacheline_size - 1));
796 }
797
798 static int64_t
799 sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
800 {
801 u64 l, r;
802 struct map *l_map, *r_map;
803
804 if (!left->mem_info) return -1;
805 if (!right->mem_info) return 1;
806
807 /* group event types together */
808 if (left->cpumode > right->cpumode) return -1;
809 if (left->cpumode < right->cpumode) return 1;
810
811 l_map = left->mem_info->daddr.map;
812 r_map = right->mem_info->daddr.map;
813
814 /* if both are NULL, jump to sort on al_addr instead */
815 if (!l_map && !r_map)
816 goto addr;
817
818 if (!l_map) return -1;
819 if (!r_map) return 1;
820
821 if (l_map->maj > r_map->maj) return -1;
822 if (l_map->maj < r_map->maj) return 1;
823
824 if (l_map->min > r_map->min) return -1;
825 if (l_map->min < r_map->min) return 1;
826
827 if (l_map->ino > r_map->ino) return -1;
828 if (l_map->ino < r_map->ino) return 1;
829
830 if (l_map->ino_generation > r_map->ino_generation) return -1;
831 if (l_map->ino_generation < r_map->ino_generation) return 1;
832
833 /*
834 * Addresses with no major/minor numbers are assumed to be
835 * anonymous in userspace. Sort those on pid then address.
836 *
837 * The kernel and non-zero major/minor mapped areas are
838 * assumed to be unity mapped. Sort those on address.
839 */
840
841 if ((left->cpumode != PERF_RECORD_MISC_KERNEL) &&
842 (!(l_map->flags & MAP_SHARED)) &&
843 !l_map->maj && !l_map->min && !l_map->ino &&
844 !l_map->ino_generation) {
845 /* userspace anonymous */
846
847 if (left->thread->pid_ > right->thread->pid_) return -1;
848 if (left->thread->pid_ < right->thread->pid_) return 1;
849 }
850
851 addr:
852 /* al_addr does all the right addr - start + offset calculations */
853 l = cl_address(left->mem_info->daddr.al_addr);
854 r = cl_address(right->mem_info->daddr.al_addr);
855
856 if (l > r) return -1;
857 if (l < r) return 1;
858
859 return 0;
860 }
861
862 static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf,
863 size_t size, unsigned int width)
864 {
865
866 uint64_t addr = 0;
867 struct map *map = NULL;
868 struct symbol *sym = NULL;
869 char level = he->level;
870
871 if (he->mem_info) {
872 addr = cl_address(he->mem_info->daddr.al_addr);
873 map = he->mem_info->daddr.map;
874 sym = he->mem_info->daddr.sym;
875
876 /* print [s] for shared data mmaps */
877 if ((he->cpumode != PERF_RECORD_MISC_KERNEL) &&
878 map && (map->type == MAP__VARIABLE) &&
879 (map->flags & MAP_SHARED) &&
880 (map->maj || map->min || map->ino ||
881 map->ino_generation))
882 level = 's';
883 else if (!map)
884 level = 'X';
885 }
886 return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size,
887 width);
888 }
889
890 struct sort_entry sort_mispredict = {
891 .se_header = "Branch Mispredicted",
892 .se_cmp = sort__mispredict_cmp,
893 .se_snprintf = hist_entry__mispredict_snprintf,
894 .se_width_idx = HISTC_MISPREDICT,
895 };
896
897 static u64 he_weight(struct hist_entry *he)
898 {
899 return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
900 }
901
902 static int64_t
903 sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
904 {
905 return he_weight(left) - he_weight(right);
906 }
907
908 static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,
909 size_t size, unsigned int width)
910 {
911 return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he));
912 }
913
914 struct sort_entry sort_local_weight = {
915 .se_header = "Local Weight",
916 .se_cmp = sort__local_weight_cmp,
917 .se_snprintf = hist_entry__local_weight_snprintf,
918 .se_width_idx = HISTC_LOCAL_WEIGHT,
919 };
920
921 static int64_t
922 sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
923 {
924 return left->stat.weight - right->stat.weight;
925 }
926
927 static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,
928 size_t size, unsigned int width)
929 {
930 return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight);
931 }
932
933 struct sort_entry sort_global_weight = {
934 .se_header = "Weight",
935 .se_cmp = sort__global_weight_cmp,
936 .se_snprintf = hist_entry__global_weight_snprintf,
937 .se_width_idx = HISTC_GLOBAL_WEIGHT,
938 };
939
940 struct sort_entry sort_mem_daddr_sym = {
941 .se_header = "Data Symbol",
942 .se_cmp = sort__daddr_cmp,
943 .se_snprintf = hist_entry__daddr_snprintf,
944 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
945 };
946
947 struct sort_entry sort_mem_daddr_dso = {
948 .se_header = "Data Object",
949 .se_cmp = sort__dso_daddr_cmp,
950 .se_snprintf = hist_entry__dso_daddr_snprintf,
951 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
952 };
953
954 struct sort_entry sort_mem_locked = {
955 .se_header = "Locked",
956 .se_cmp = sort__locked_cmp,
957 .se_snprintf = hist_entry__locked_snprintf,
958 .se_width_idx = HISTC_MEM_LOCKED,
959 };
960
961 struct sort_entry sort_mem_tlb = {
962 .se_header = "TLB access",
963 .se_cmp = sort__tlb_cmp,
964 .se_snprintf = hist_entry__tlb_snprintf,
965 .se_width_idx = HISTC_MEM_TLB,
966 };
967
968 struct sort_entry sort_mem_lvl = {
969 .se_header = "Memory access",
970 .se_cmp = sort__lvl_cmp,
971 .se_snprintf = hist_entry__lvl_snprintf,
972 .se_width_idx = HISTC_MEM_LVL,
973 };
974
975 struct sort_entry sort_mem_snoop = {
976 .se_header = "Snoop",
977 .se_cmp = sort__snoop_cmp,
978 .se_snprintf = hist_entry__snoop_snprintf,
979 .se_width_idx = HISTC_MEM_SNOOP,
980 };
981
982 struct sort_entry sort_mem_dcacheline = {
983 .se_header = "Data Cacheline",
984 .se_cmp = sort__dcacheline_cmp,
985 .se_snprintf = hist_entry__dcacheline_snprintf,
986 .se_width_idx = HISTC_MEM_DCACHELINE,
987 };
988
989 static int64_t
990 sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
991 {
992 return left->branch_info->flags.abort !=
993 right->branch_info->flags.abort;
994 }
995
996 static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf,
997 size_t size, unsigned int width)
998 {
999 static const char *out = ".";
1000
1001 if (he->branch_info->flags.abort)
1002 out = "A";
1003 return repsep_snprintf(bf, size, "%-*s", width, out);
1004 }
1005
1006 struct sort_entry sort_abort = {
1007 .se_header = "Transaction abort",
1008 .se_cmp = sort__abort_cmp,
1009 .se_snprintf = hist_entry__abort_snprintf,
1010 .se_width_idx = HISTC_ABORT,
1011 };
1012
1013 static int64_t
1014 sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right)
1015 {
1016 return left->branch_info->flags.in_tx !=
1017 right->branch_info->flags.in_tx;
1018 }
1019
1020 static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf,
1021 size_t size, unsigned int width)
1022 {
1023 static const char *out = ".";
1024
1025 if (he->branch_info->flags.in_tx)
1026 out = "T";
1027
1028 return repsep_snprintf(bf, size, "%-*s", width, out);
1029 }
1030
1031 struct sort_entry sort_in_tx = {
1032 .se_header = "Branch in transaction",
1033 .se_cmp = sort__in_tx_cmp,
1034 .se_snprintf = hist_entry__in_tx_snprintf,
1035 .se_width_idx = HISTC_IN_TX,
1036 };
1037
1038 static int64_t
1039 sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
1040 {
1041 return left->transaction - right->transaction;
1042 }
1043
1044 static inline char *add_str(char *p, const char *str)
1045 {
1046 strcpy(p, str);
1047 return p + strlen(str);
1048 }
1049
1050 static struct txbit {
1051 unsigned flag;
1052 const char *name;
1053 int skip_for_len;
1054 } txbits[] = {
1055 { PERF_TXN_ELISION, "EL ", 0 },
1056 { PERF_TXN_TRANSACTION, "TX ", 1 },
1057 { PERF_TXN_SYNC, "SYNC ", 1 },
1058 { PERF_TXN_ASYNC, "ASYNC ", 0 },
1059 { PERF_TXN_RETRY, "RETRY ", 0 },
1060 { PERF_TXN_CONFLICT, "CON ", 0 },
1061 { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 },
1062 { PERF_TXN_CAPACITY_READ, "CAP-READ ", 0 },
1063 { 0, NULL, 0 }
1064 };
1065
1066 int hist_entry__transaction_len(void)
1067 {
1068 int i;
1069 int len = 0;
1070
1071 for (i = 0; txbits[i].name; i++) {
1072 if (!txbits[i].skip_for_len)
1073 len += strlen(txbits[i].name);
1074 }
1075 len += 4; /* :XX<space> */
1076 return len;
1077 }
1078
1079 static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf,
1080 size_t size, unsigned int width)
1081 {
1082 u64 t = he->transaction;
1083 char buf[128];
1084 char *p = buf;
1085 int i;
1086
1087 buf[0] = 0;
1088 for (i = 0; txbits[i].name; i++)
1089 if (txbits[i].flag & t)
1090 p = add_str(p, txbits[i].name);
1091 if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC)))
1092 p = add_str(p, "NEITHER ");
1093 if (t & PERF_TXN_ABORT_MASK) {
1094 sprintf(p, ":%" PRIx64,
1095 (t & PERF_TXN_ABORT_MASK) >>
1096 PERF_TXN_ABORT_SHIFT);
1097 p += strlen(p);
1098 }
1099
1100 return repsep_snprintf(bf, size, "%-*s", width, buf);
1101 }
1102
1103 struct sort_entry sort_transaction = {
1104 .se_header = "Transaction ",
1105 .se_cmp = sort__transaction_cmp,
1106 .se_snprintf = hist_entry__transaction_snprintf,
1107 .se_width_idx = HISTC_TRANSACTION,
1108 };
1109
1110 struct sort_dimension {
1111 const char *name;
1112 struct sort_entry *entry;
1113 int taken;
1114 };
1115
1116 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
1117
1118 static struct sort_dimension common_sort_dimensions[] = {
1119 DIM(SORT_PID, "pid", sort_thread),
1120 DIM(SORT_COMM, "comm", sort_comm),
1121 DIM(SORT_DSO, "dso", sort_dso),
1122 DIM(SORT_SYM, "symbol", sort_sym),
1123 DIM(SORT_PARENT, "parent", sort_parent),
1124 DIM(SORT_CPU, "cpu", sort_cpu),
1125 DIM(SORT_SRCLINE, "srcline", sort_srcline),
1126 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
1127 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
1128 DIM(SORT_TRANSACTION, "transaction", sort_transaction),
1129 };
1130
1131 #undef DIM
1132
1133 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
1134
1135 static struct sort_dimension bstack_sort_dimensions[] = {
1136 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
1137 DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
1138 DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
1139 DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
1140 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
1141 DIM(SORT_IN_TX, "in_tx", sort_in_tx),
1142 DIM(SORT_ABORT, "abort", sort_abort),
1143 };
1144
1145 #undef DIM
1146
1147 #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
1148
1149 static struct sort_dimension memory_sort_dimensions[] = {
1150 DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
1151 DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
1152 DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
1153 DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
1154 DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
1155 DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
1156 DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline),
1157 };
1158
1159 #undef DIM
1160
1161 struct hpp_dimension {
1162 const char *name;
1163 struct perf_hpp_fmt *fmt;
1164 int taken;
1165 };
1166
1167 #define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
1168
1169 static struct hpp_dimension hpp_sort_dimensions[] = {
1170 DIM(PERF_HPP__OVERHEAD, "overhead"),
1171 DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"),
1172 DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
1173 DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
1174 DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
1175 DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"),
1176 DIM(PERF_HPP__SAMPLES, "sample"),
1177 DIM(PERF_HPP__PERIOD, "period"),
1178 };
1179
1180 #undef DIM
1181
1182 struct hpp_sort_entry {
1183 struct perf_hpp_fmt hpp;
1184 struct sort_entry *se;
1185 };
1186
1187 bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1188 {
1189 struct hpp_sort_entry *hse_a;
1190 struct hpp_sort_entry *hse_b;
1191
1192 if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
1193 return false;
1194
1195 hse_a = container_of(a, struct hpp_sort_entry, hpp);
1196 hse_b = container_of(b, struct hpp_sort_entry, hpp);
1197
1198 return hse_a->se == hse_b->se;
1199 }
1200
1201 void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists)
1202 {
1203 struct hpp_sort_entry *hse;
1204
1205 if (!perf_hpp__is_sort_entry(fmt))
1206 return;
1207
1208 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1209 hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name));
1210 }
1211
1212 static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1213 struct perf_evsel *evsel)
1214 {
1215 struct hpp_sort_entry *hse;
1216 size_t len = fmt->user_len;
1217
1218 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1219
1220 if (!len)
1221 len = hists__col_len(&evsel->hists, hse->se->se_width_idx);
1222
1223 return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name);
1224 }
1225
1226 static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
1227 struct perf_hpp *hpp __maybe_unused,
1228 struct perf_evsel *evsel)
1229 {
1230 struct hpp_sort_entry *hse;
1231 size_t len = fmt->user_len;
1232
1233 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1234
1235 if (!len)
1236 len = hists__col_len(&evsel->hists, hse->se->se_width_idx);
1237
1238 return len;
1239 }
1240
1241 static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1242 struct hist_entry *he)
1243 {
1244 struct hpp_sort_entry *hse;
1245 size_t len = fmt->user_len;
1246
1247 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1248
1249 if (!len)
1250 len = hists__col_len(he->hists, hse->se->se_width_idx);
1251
1252 return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
1253 }
1254
1255 static struct hpp_sort_entry *
1256 __sort_dimension__alloc_hpp(struct sort_dimension *sd)
1257 {
1258 struct hpp_sort_entry *hse;
1259
1260 hse = malloc(sizeof(*hse));
1261 if (hse == NULL) {
1262 pr_err("Memory allocation failed\n");
1263 return NULL;
1264 }
1265
1266 hse->se = sd->entry;
1267 hse->hpp.name = sd->entry->se_header;
1268 hse->hpp.header = __sort__hpp_header;
1269 hse->hpp.width = __sort__hpp_width;
1270 hse->hpp.entry = __sort__hpp_entry;
1271 hse->hpp.color = NULL;
1272
1273 hse->hpp.cmp = sd->entry->se_cmp;
1274 hse->hpp.collapse = sd->entry->se_collapse ? : sd->entry->se_cmp;
1275 hse->hpp.sort = sd->entry->se_sort ? : hse->hpp.collapse;
1276
1277 INIT_LIST_HEAD(&hse->hpp.list);
1278 INIT_LIST_HEAD(&hse->hpp.sort_list);
1279 hse->hpp.elide = false;
1280 hse->hpp.len = 0;
1281 hse->hpp.user_len = 0;
1282
1283 return hse;
1284 }
1285
1286 bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
1287 {
1288 return format->header == __sort__hpp_header;
1289 }
1290
1291 static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd)
1292 {
1293 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1294
1295 if (hse == NULL)
1296 return -1;
1297
1298 perf_hpp__register_sort_field(&hse->hpp);
1299 return 0;
1300 }
1301
1302 static int __sort_dimension__add_hpp_output(struct sort_dimension *sd)
1303 {
1304 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1305
1306 if (hse == NULL)
1307 return -1;
1308
1309 perf_hpp__column_register(&hse->hpp);
1310 return 0;
1311 }
1312
1313 static int __sort_dimension__add(struct sort_dimension *sd)
1314 {
1315 if (sd->taken)
1316 return 0;
1317
1318 if (__sort_dimension__add_hpp_sort(sd) < 0)
1319 return -1;
1320
1321 if (sd->entry->se_collapse)
1322 sort__need_collapse = 1;
1323
1324 sd->taken = 1;
1325
1326 return 0;
1327 }
1328
1329 static int __hpp_dimension__add(struct hpp_dimension *hd)
1330 {
1331 if (!hd->taken) {
1332 hd->taken = 1;
1333
1334 perf_hpp__register_sort_field(hd->fmt);
1335 }
1336 return 0;
1337 }
1338
1339 static int __sort_dimension__add_output(struct sort_dimension *sd)
1340 {
1341 if (sd->taken)
1342 return 0;
1343
1344 if (__sort_dimension__add_hpp_output(sd) < 0)
1345 return -1;
1346
1347 sd->taken = 1;
1348 return 0;
1349 }
1350
1351 static int __hpp_dimension__add_output(struct hpp_dimension *hd)
1352 {
1353 if (!hd->taken) {
1354 hd->taken = 1;
1355
1356 perf_hpp__column_register(hd->fmt);
1357 }
1358 return 0;
1359 }
1360
1361 int sort_dimension__add(const char *tok)
1362 {
1363 unsigned int i;
1364
1365 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1366 struct sort_dimension *sd = &common_sort_dimensions[i];
1367
1368 if (strncasecmp(tok, sd->name, strlen(tok)))
1369 continue;
1370
1371 if (sd->entry == &sort_parent) {
1372 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
1373 if (ret) {
1374 char err[BUFSIZ];
1375
1376 regerror(ret, &parent_regex, err, sizeof(err));
1377 pr_err("Invalid regex: %s\n%s", parent_pattern, err);
1378 return -EINVAL;
1379 }
1380 sort__has_parent = 1;
1381 } else if (sd->entry == &sort_sym) {
1382 sort__has_sym = 1;
1383 } else if (sd->entry == &sort_dso) {
1384 sort__has_dso = 1;
1385 }
1386
1387 return __sort_dimension__add(sd);
1388 }
1389
1390 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1391 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1392
1393 if (strncasecmp(tok, hd->name, strlen(tok)))
1394 continue;
1395
1396 return __hpp_dimension__add(hd);
1397 }
1398
1399 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1400 struct sort_dimension *sd = &bstack_sort_dimensions[i];
1401
1402 if (strncasecmp(tok, sd->name, strlen(tok)))
1403 continue;
1404
1405 if (sort__mode != SORT_MODE__BRANCH)
1406 return -EINVAL;
1407
1408 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
1409 sort__has_sym = 1;
1410
1411 __sort_dimension__add(sd);
1412 return 0;
1413 }
1414
1415 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1416 struct sort_dimension *sd = &memory_sort_dimensions[i];
1417
1418 if (strncasecmp(tok, sd->name, strlen(tok)))
1419 continue;
1420
1421 if (sort__mode != SORT_MODE__MEMORY)
1422 return -EINVAL;
1423
1424 if (sd->entry == &sort_mem_daddr_sym)
1425 sort__has_sym = 1;
1426
1427 __sort_dimension__add(sd);
1428 return 0;
1429 }
1430
1431 return -ESRCH;
1432 }
1433
1434 static const char *get_default_sort_order(void)
1435 {
1436 const char *default_sort_orders[] = {
1437 default_sort_order,
1438 default_branch_sort_order,
1439 default_mem_sort_order,
1440 default_top_sort_order,
1441 default_diff_sort_order,
1442 };
1443
1444 BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
1445
1446 return default_sort_orders[sort__mode];
1447 }
1448
1449 static int __setup_sorting(void)
1450 {
1451 char *tmp, *tok, *str;
1452 const char *sort_keys = sort_order;
1453 int ret = 0;
1454
1455 if (sort_keys == NULL) {
1456 if (field_order) {
1457 /*
1458 * If user specified field order but no sort order,
1459 * we'll honor it and not add default sort orders.
1460 */
1461 return 0;
1462 }
1463
1464 sort_keys = get_default_sort_order();
1465 }
1466
1467 str = strdup(sort_keys);
1468 if (str == NULL) {
1469 error("Not enough memory to setup sort keys");
1470 return -ENOMEM;
1471 }
1472
1473 for (tok = strtok_r(str, ", ", &tmp);
1474 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1475 ret = sort_dimension__add(tok);
1476 if (ret == -EINVAL) {
1477 error("Invalid --sort key: `%s'", tok);
1478 break;
1479 } else if (ret == -ESRCH) {
1480 error("Unknown --sort key: `%s'", tok);
1481 break;
1482 }
1483 }
1484
1485 free(str);
1486 return ret;
1487 }
1488
1489 void perf_hpp__set_elide(int idx, bool elide)
1490 {
1491 struct perf_hpp_fmt *fmt;
1492 struct hpp_sort_entry *hse;
1493
1494 perf_hpp__for_each_format(fmt) {
1495 if (!perf_hpp__is_sort_entry(fmt))
1496 continue;
1497
1498 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1499 if (hse->se->se_width_idx == idx) {
1500 fmt->elide = elide;
1501 break;
1502 }
1503 }
1504 }
1505
1506 static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)
1507 {
1508 if (list && strlist__nr_entries(list) == 1) {
1509 if (fp != NULL)
1510 fprintf(fp, "# %s: %s\n", list_name,
1511 strlist__entry(list, 0)->s);
1512 return true;
1513 }
1514 return false;
1515 }
1516
1517 static bool get_elide(int idx, FILE *output)
1518 {
1519 switch (idx) {
1520 case HISTC_SYMBOL:
1521 return __get_elide(symbol_conf.sym_list, "symbol", output);
1522 case HISTC_DSO:
1523 return __get_elide(symbol_conf.dso_list, "dso", output);
1524 case HISTC_COMM:
1525 return __get_elide(symbol_conf.comm_list, "comm", output);
1526 default:
1527 break;
1528 }
1529
1530 if (sort__mode != SORT_MODE__BRANCH)
1531 return false;
1532
1533 switch (idx) {
1534 case HISTC_SYMBOL_FROM:
1535 return __get_elide(symbol_conf.sym_from_list, "sym_from", output);
1536 case HISTC_SYMBOL_TO:
1537 return __get_elide(symbol_conf.sym_to_list, "sym_to", output);
1538 case HISTC_DSO_FROM:
1539 return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
1540 case HISTC_DSO_TO:
1541 return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
1542 default:
1543 break;
1544 }
1545
1546 return false;
1547 }
1548
1549 void sort__setup_elide(FILE *output)
1550 {
1551 struct perf_hpp_fmt *fmt;
1552 struct hpp_sort_entry *hse;
1553
1554 perf_hpp__for_each_format(fmt) {
1555 if (!perf_hpp__is_sort_entry(fmt))
1556 continue;
1557
1558 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1559 fmt->elide = get_elide(hse->se->se_width_idx, output);
1560 }
1561
1562 /*
1563 * It makes no sense to elide all of sort entries.
1564 * Just revert them to show up again.
1565 */
1566 perf_hpp__for_each_format(fmt) {
1567 if (!perf_hpp__is_sort_entry(fmt))
1568 continue;
1569
1570 if (!fmt->elide)
1571 return;
1572 }
1573
1574 perf_hpp__for_each_format(fmt) {
1575 if (!perf_hpp__is_sort_entry(fmt))
1576 continue;
1577
1578 fmt->elide = false;
1579 }
1580 }
1581
1582 static int output_field_add(char *tok)
1583 {
1584 unsigned int i;
1585
1586 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1587 struct sort_dimension *sd = &common_sort_dimensions[i];
1588
1589 if (strncasecmp(tok, sd->name, strlen(tok)))
1590 continue;
1591
1592 return __sort_dimension__add_output(sd);
1593 }
1594
1595 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1596 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1597
1598 if (strncasecmp(tok, hd->name, strlen(tok)))
1599 continue;
1600
1601 return __hpp_dimension__add_output(hd);
1602 }
1603
1604 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1605 struct sort_dimension *sd = &bstack_sort_dimensions[i];
1606
1607 if (strncasecmp(tok, sd->name, strlen(tok)))
1608 continue;
1609
1610 return __sort_dimension__add_output(sd);
1611 }
1612
1613 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1614 struct sort_dimension *sd = &memory_sort_dimensions[i];
1615
1616 if (strncasecmp(tok, sd->name, strlen(tok)))
1617 continue;
1618
1619 return __sort_dimension__add_output(sd);
1620 }
1621
1622 return -ESRCH;
1623 }
1624
1625 static void reset_dimensions(void)
1626 {
1627 unsigned int i;
1628
1629 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++)
1630 common_sort_dimensions[i].taken = 0;
1631
1632 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++)
1633 hpp_sort_dimensions[i].taken = 0;
1634
1635 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++)
1636 bstack_sort_dimensions[i].taken = 0;
1637
1638 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++)
1639 memory_sort_dimensions[i].taken = 0;
1640 }
1641
1642 static int __setup_output_field(void)
1643 {
1644 char *tmp, *tok, *str;
1645 int ret = 0;
1646
1647 if (field_order == NULL)
1648 return 0;
1649
1650 reset_dimensions();
1651
1652 str = strdup(field_order);
1653 if (str == NULL) {
1654 error("Not enough memory to setup output fields");
1655 return -ENOMEM;
1656 }
1657
1658 for (tok = strtok_r(str, ", ", &tmp);
1659 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1660 ret = output_field_add(tok);
1661 if (ret == -EINVAL) {
1662 error("Invalid --fields key: `%s'", tok);
1663 break;
1664 } else if (ret == -ESRCH) {
1665 error("Unknown --fields key: `%s'", tok);
1666 break;
1667 }
1668 }
1669
1670 free(str);
1671 return ret;
1672 }
1673
1674 int setup_sorting(void)
1675 {
1676 int err;
1677
1678 err = __setup_sorting();
1679 if (err < 0)
1680 return err;
1681
1682 if (parent_pattern != default_parent_pattern) {
1683 err = sort_dimension__add("parent");
1684 if (err < 0)
1685 return err;
1686 }
1687
1688 reset_dimensions();
1689
1690 /*
1691 * perf diff doesn't use default hpp output fields.
1692 */
1693 if (sort__mode != SORT_MODE__DIFF)
1694 perf_hpp__init();
1695
1696 err = __setup_output_field();
1697 if (err < 0)
1698 return err;
1699
1700 /* copy sort keys to output fields */
1701 perf_hpp__setup_output_field();
1702 /* and then copy output fields to sort keys */
1703 perf_hpp__append_sort_keys();
1704
1705 return 0;
1706 }
1707
1708 void reset_output_field(void)
1709 {
1710 sort__need_collapse = 0;
1711 sort__has_parent = 0;
1712 sort__has_sym = 0;
1713 sort__has_dso = 0;
1714
1715 field_order = NULL;
1716 sort_order = NULL;
1717
1718 reset_dimensions();
1719 perf_hpp__reset_output_field();
1720 }
This page took 0.092203 seconds and 5 git commands to generate.