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