]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - tools/perf/util/sort.c
Merge branch 'for-rafael' of https://git.kernel.org/pub/scm/linux/kernel/git/mzx...
[mirror_ubuntu-artful-kernel.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,symbol_to,cycles";
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 strcmp(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 strcmp(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 if (!sym_l || !sym_r)
186 return cmp_null(sym_l, sym_r);
187
188 if (sym_l == sym_r)
189 return 0;
190
191 if (sym_l->start != sym_r->start)
192 return (int64_t)(sym_r->start - sym_l->start);
193
194 return (int64_t)(sym_r->end - sym_l->end);
195 }
196
197 static int64_t
198 sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
199 {
200 int64_t ret;
201
202 if (!left->ms.sym && !right->ms.sym)
203 return _sort__addr_cmp(left->ip, right->ip);
204
205 /*
206 * comparing symbol address alone is not enough since it's a
207 * relative address within a dso.
208 */
209 if (!sort__has_dso) {
210 ret = sort__dso_cmp(left, right);
211 if (ret != 0)
212 return ret;
213 }
214
215 return _sort__sym_cmp(left->ms.sym, right->ms.sym);
216 }
217
218 static int64_t
219 sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
220 {
221 if (!left->ms.sym || !right->ms.sym)
222 return cmp_null(left->ms.sym, right->ms.sym);
223
224 return strcmp(right->ms.sym->name, left->ms.sym->name);
225 }
226
227 static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
228 u64 ip, char level, char *bf, size_t size,
229 unsigned int width)
230 {
231 size_t ret = 0;
232
233 if (verbose) {
234 char o = map ? dso__symtab_origin(map->dso) : '!';
235 ret += repsep_snprintf(bf, size, "%-#*llx %c ",
236 BITS_PER_LONG / 4 + 2, ip, o);
237 }
238
239 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
240 if (sym && map) {
241 if (map->type == MAP__VARIABLE) {
242 ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
243 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
244 ip - map->unmap_ip(map, sym->start));
245 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
246 width - ret, "");
247 } else {
248 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
249 width - ret,
250 sym->name);
251 }
252 } else {
253 size_t len = BITS_PER_LONG / 4;
254 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
255 len, ip);
256 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
257 width - ret, "");
258 }
259
260 if (ret > width)
261 bf[width] = '\0';
262
263 return width;
264 }
265
266 static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,
267 size_t size, unsigned int width)
268 {
269 return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip,
270 he->level, bf, size, width);
271 }
272
273 struct sort_entry sort_sym = {
274 .se_header = "Symbol",
275 .se_cmp = sort__sym_cmp,
276 .se_sort = sort__sym_sort,
277 .se_snprintf = hist_entry__sym_snprintf,
278 .se_width_idx = HISTC_SYMBOL,
279 };
280
281 /* --sort srcline */
282
283 static int64_t
284 sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
285 {
286 if (!left->srcline) {
287 if (!left->ms.map)
288 left->srcline = SRCLINE_UNKNOWN;
289 else {
290 struct map *map = left->ms.map;
291 left->srcline = get_srcline(map->dso,
292 map__rip_2objdump(map, left->ip),
293 left->ms.sym, true);
294 }
295 }
296 if (!right->srcline) {
297 if (!right->ms.map)
298 right->srcline = SRCLINE_UNKNOWN;
299 else {
300 struct map *map = right->ms.map;
301 right->srcline = get_srcline(map->dso,
302 map__rip_2objdump(map, right->ip),
303 right->ms.sym, true);
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 srcfile */
323
324 static char no_srcfile[1];
325
326 static char *get_srcfile(struct hist_entry *e)
327 {
328 char *sf, *p;
329 struct map *map = e->ms.map;
330
331 sf = get_srcline(map->dso, map__rip_2objdump(map, e->ip),
332 e->ms.sym, true);
333 if (!strcmp(sf, SRCLINE_UNKNOWN))
334 return no_srcfile;
335 p = strchr(sf, ':');
336 if (p && *sf) {
337 *p = 0;
338 return sf;
339 }
340 free(sf);
341 return no_srcfile;
342 }
343
344 static int64_t
345 sort__srcfile_cmp(struct hist_entry *left, struct hist_entry *right)
346 {
347 if (!left->srcfile) {
348 if (!left->ms.map)
349 left->srcfile = no_srcfile;
350 else
351 left->srcfile = get_srcfile(left);
352 }
353 if (!right->srcfile) {
354 if (!right->ms.map)
355 right->srcfile = no_srcfile;
356 else
357 right->srcfile = get_srcfile(right);
358 }
359 return strcmp(right->srcfile, left->srcfile);
360 }
361
362 static int hist_entry__srcfile_snprintf(struct hist_entry *he, char *bf,
363 size_t size, unsigned int width)
364 {
365 return repsep_snprintf(bf, size, "%-*.*s", width, width, he->srcfile);
366 }
367
368 struct sort_entry sort_srcfile = {
369 .se_header = "Source File",
370 .se_cmp = sort__srcfile_cmp,
371 .se_snprintf = hist_entry__srcfile_snprintf,
372 .se_width_idx = HISTC_SRCFILE,
373 };
374
375 /* --sort parent */
376
377 static int64_t
378 sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
379 {
380 struct symbol *sym_l = left->parent;
381 struct symbol *sym_r = right->parent;
382
383 if (!sym_l || !sym_r)
384 return cmp_null(sym_l, sym_r);
385
386 return strcmp(sym_r->name, sym_l->name);
387 }
388
389 static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
390 size_t size, unsigned int width)
391 {
392 return repsep_snprintf(bf, size, "%-*.*s", width, width,
393 he->parent ? he->parent->name : "[other]");
394 }
395
396 struct sort_entry sort_parent = {
397 .se_header = "Parent symbol",
398 .se_cmp = sort__parent_cmp,
399 .se_snprintf = hist_entry__parent_snprintf,
400 .se_width_idx = HISTC_PARENT,
401 };
402
403 /* --sort cpu */
404
405 static int64_t
406 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
407 {
408 return right->cpu - left->cpu;
409 }
410
411 static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf,
412 size_t size, unsigned int width)
413 {
414 return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu);
415 }
416
417 struct sort_entry sort_cpu = {
418 .se_header = "CPU",
419 .se_cmp = sort__cpu_cmp,
420 .se_snprintf = hist_entry__cpu_snprintf,
421 .se_width_idx = HISTC_CPU,
422 };
423
424 /* sort keys for branch stacks */
425
426 static int64_t
427 sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
428 {
429 if (!left->branch_info || !right->branch_info)
430 return cmp_null(left->branch_info, right->branch_info);
431
432 return _sort__dso_cmp(left->branch_info->from.map,
433 right->branch_info->from.map);
434 }
435
436 static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,
437 size_t size, unsigned int width)
438 {
439 if (he->branch_info)
440 return _hist_entry__dso_snprintf(he->branch_info->from.map,
441 bf, size, width);
442 else
443 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
444 }
445
446 static int64_t
447 sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
448 {
449 if (!left->branch_info || !right->branch_info)
450 return cmp_null(left->branch_info, right->branch_info);
451
452 return _sort__dso_cmp(left->branch_info->to.map,
453 right->branch_info->to.map);
454 }
455
456 static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,
457 size_t size, unsigned int width)
458 {
459 if (he->branch_info)
460 return _hist_entry__dso_snprintf(he->branch_info->to.map,
461 bf, size, width);
462 else
463 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
464 }
465
466 static int64_t
467 sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
468 {
469 struct addr_map_symbol *from_l = &left->branch_info->from;
470 struct addr_map_symbol *from_r = &right->branch_info->from;
471
472 if (!left->branch_info || !right->branch_info)
473 return cmp_null(left->branch_info, right->branch_info);
474
475 from_l = &left->branch_info->from;
476 from_r = &right->branch_info->from;
477
478 if (!from_l->sym && !from_r->sym)
479 return _sort__addr_cmp(from_l->addr, from_r->addr);
480
481 return _sort__sym_cmp(from_l->sym, from_r->sym);
482 }
483
484 static int64_t
485 sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
486 {
487 struct addr_map_symbol *to_l, *to_r;
488
489 if (!left->branch_info || !right->branch_info)
490 return cmp_null(left->branch_info, right->branch_info);
491
492 to_l = &left->branch_info->to;
493 to_r = &right->branch_info->to;
494
495 if (!to_l->sym && !to_r->sym)
496 return _sort__addr_cmp(to_l->addr, to_r->addr);
497
498 return _sort__sym_cmp(to_l->sym, to_r->sym);
499 }
500
501 static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,
502 size_t size, unsigned int width)
503 {
504 if (he->branch_info) {
505 struct addr_map_symbol *from = &he->branch_info->from;
506
507 return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
508 he->level, bf, size, width);
509 }
510
511 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
512 }
513
514 static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,
515 size_t size, unsigned int width)
516 {
517 if (he->branch_info) {
518 struct addr_map_symbol *to = &he->branch_info->to;
519
520 return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
521 he->level, bf, size, width);
522 }
523
524 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
525 }
526
527 struct sort_entry sort_dso_from = {
528 .se_header = "Source Shared Object",
529 .se_cmp = sort__dso_from_cmp,
530 .se_snprintf = hist_entry__dso_from_snprintf,
531 .se_width_idx = HISTC_DSO_FROM,
532 };
533
534 struct sort_entry sort_dso_to = {
535 .se_header = "Target Shared Object",
536 .se_cmp = sort__dso_to_cmp,
537 .se_snprintf = hist_entry__dso_to_snprintf,
538 .se_width_idx = HISTC_DSO_TO,
539 };
540
541 struct sort_entry sort_sym_from = {
542 .se_header = "Source Symbol",
543 .se_cmp = sort__sym_from_cmp,
544 .se_snprintf = hist_entry__sym_from_snprintf,
545 .se_width_idx = HISTC_SYMBOL_FROM,
546 };
547
548 struct sort_entry sort_sym_to = {
549 .se_header = "Target Symbol",
550 .se_cmp = sort__sym_to_cmp,
551 .se_snprintf = hist_entry__sym_to_snprintf,
552 .se_width_idx = HISTC_SYMBOL_TO,
553 };
554
555 static int64_t
556 sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
557 {
558 unsigned char mp, p;
559
560 if (!left->branch_info || !right->branch_info)
561 return cmp_null(left->branch_info, right->branch_info);
562
563 mp = left->branch_info->flags.mispred != right->branch_info->flags.mispred;
564 p = left->branch_info->flags.predicted != right->branch_info->flags.predicted;
565 return mp || p;
566 }
567
568 static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,
569 size_t size, unsigned int width){
570 static const char *out = "N/A";
571
572 if (he->branch_info) {
573 if (he->branch_info->flags.predicted)
574 out = "N";
575 else if (he->branch_info->flags.mispred)
576 out = "Y";
577 }
578
579 return repsep_snprintf(bf, size, "%-*.*s", width, width, out);
580 }
581
582 static int64_t
583 sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right)
584 {
585 return left->branch_info->flags.cycles -
586 right->branch_info->flags.cycles;
587 }
588
589 static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf,
590 size_t size, unsigned int width)
591 {
592 if (he->branch_info->flags.cycles == 0)
593 return repsep_snprintf(bf, size, "%-*s", width, "-");
594 return repsep_snprintf(bf, size, "%-*hd", width,
595 he->branch_info->flags.cycles);
596 }
597
598 struct sort_entry sort_cycles = {
599 .se_header = "Basic Block Cycles",
600 .se_cmp = sort__cycles_cmp,
601 .se_snprintf = hist_entry__cycles_snprintf,
602 .se_width_idx = HISTC_CYCLES,
603 };
604
605 /* --sort daddr_sym */
606 static int64_t
607 sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
608 {
609 uint64_t l = 0, r = 0;
610
611 if (left->mem_info)
612 l = left->mem_info->daddr.addr;
613 if (right->mem_info)
614 r = right->mem_info->daddr.addr;
615
616 return (int64_t)(r - l);
617 }
618
619 static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,
620 size_t size, unsigned int width)
621 {
622 uint64_t addr = 0;
623 struct map *map = NULL;
624 struct symbol *sym = NULL;
625
626 if (he->mem_info) {
627 addr = he->mem_info->daddr.addr;
628 map = he->mem_info->daddr.map;
629 sym = he->mem_info->daddr.sym;
630 }
631 return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
632 width);
633 }
634
635 static int64_t
636 sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
637 {
638 struct map *map_l = NULL;
639 struct map *map_r = NULL;
640
641 if (left->mem_info)
642 map_l = left->mem_info->daddr.map;
643 if (right->mem_info)
644 map_r = right->mem_info->daddr.map;
645
646 return _sort__dso_cmp(map_l, map_r);
647 }
648
649 static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,
650 size_t size, unsigned int width)
651 {
652 struct map *map = NULL;
653
654 if (he->mem_info)
655 map = he->mem_info->daddr.map;
656
657 return _hist_entry__dso_snprintf(map, bf, size, width);
658 }
659
660 static int64_t
661 sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
662 {
663 union perf_mem_data_src data_src_l;
664 union perf_mem_data_src data_src_r;
665
666 if (left->mem_info)
667 data_src_l = left->mem_info->data_src;
668 else
669 data_src_l.mem_lock = PERF_MEM_LOCK_NA;
670
671 if (right->mem_info)
672 data_src_r = right->mem_info->data_src;
673 else
674 data_src_r.mem_lock = PERF_MEM_LOCK_NA;
675
676 return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
677 }
678
679 static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,
680 size_t size, unsigned int width)
681 {
682 const char *out;
683 u64 mask = PERF_MEM_LOCK_NA;
684
685 if (he->mem_info)
686 mask = he->mem_info->data_src.mem_lock;
687
688 if (mask & PERF_MEM_LOCK_NA)
689 out = "N/A";
690 else if (mask & PERF_MEM_LOCK_LOCKED)
691 out = "Yes";
692 else
693 out = "No";
694
695 return repsep_snprintf(bf, size, "%-*s", width, out);
696 }
697
698 static int64_t
699 sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
700 {
701 union perf_mem_data_src data_src_l;
702 union perf_mem_data_src data_src_r;
703
704 if (left->mem_info)
705 data_src_l = left->mem_info->data_src;
706 else
707 data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
708
709 if (right->mem_info)
710 data_src_r = right->mem_info->data_src;
711 else
712 data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
713
714 return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
715 }
716
717 static const char * const tlb_access[] = {
718 "N/A",
719 "HIT",
720 "MISS",
721 "L1",
722 "L2",
723 "Walker",
724 "Fault",
725 };
726 #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
727
728 static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,
729 size_t size, unsigned int width)
730 {
731 char out[64];
732 size_t sz = sizeof(out) - 1; /* -1 for null termination */
733 size_t l = 0, i;
734 u64 m = PERF_MEM_TLB_NA;
735 u64 hit, miss;
736
737 out[0] = '\0';
738
739 if (he->mem_info)
740 m = he->mem_info->data_src.mem_dtlb;
741
742 hit = m & PERF_MEM_TLB_HIT;
743 miss = m & PERF_MEM_TLB_MISS;
744
745 /* already taken care of */
746 m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
747
748 for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) {
749 if (!(m & 0x1))
750 continue;
751 if (l) {
752 strcat(out, " or ");
753 l += 4;
754 }
755 strncat(out, tlb_access[i], sz - l);
756 l += strlen(tlb_access[i]);
757 }
758 if (*out == '\0')
759 strcpy(out, "N/A");
760 if (hit)
761 strncat(out, " hit", sz - l);
762 if (miss)
763 strncat(out, " miss", sz - l);
764
765 return repsep_snprintf(bf, size, "%-*s", width, out);
766 }
767
768 static int64_t
769 sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
770 {
771 union perf_mem_data_src data_src_l;
772 union perf_mem_data_src data_src_r;
773
774 if (left->mem_info)
775 data_src_l = left->mem_info->data_src;
776 else
777 data_src_l.mem_lvl = PERF_MEM_LVL_NA;
778
779 if (right->mem_info)
780 data_src_r = right->mem_info->data_src;
781 else
782 data_src_r.mem_lvl = PERF_MEM_LVL_NA;
783
784 return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
785 }
786
787 static const char * const mem_lvl[] = {
788 "N/A",
789 "HIT",
790 "MISS",
791 "L1",
792 "LFB",
793 "L2",
794 "L3",
795 "Local RAM",
796 "Remote RAM (1 hop)",
797 "Remote RAM (2 hops)",
798 "Remote Cache (1 hop)",
799 "Remote Cache (2 hops)",
800 "I/O",
801 "Uncached",
802 };
803 #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
804
805 static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,
806 size_t size, unsigned int width)
807 {
808 char out[64];
809 size_t sz = sizeof(out) - 1; /* -1 for null termination */
810 size_t i, l = 0;
811 u64 m = PERF_MEM_LVL_NA;
812 u64 hit, miss;
813
814 if (he->mem_info)
815 m = he->mem_info->data_src.mem_lvl;
816
817 out[0] = '\0';
818
819 hit = m & PERF_MEM_LVL_HIT;
820 miss = m & PERF_MEM_LVL_MISS;
821
822 /* already taken care of */
823 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
824
825 for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) {
826 if (!(m & 0x1))
827 continue;
828 if (l) {
829 strcat(out, " or ");
830 l += 4;
831 }
832 strncat(out, mem_lvl[i], sz - l);
833 l += strlen(mem_lvl[i]);
834 }
835 if (*out == '\0')
836 strcpy(out, "N/A");
837 if (hit)
838 strncat(out, " hit", sz - l);
839 if (miss)
840 strncat(out, " miss", sz - l);
841
842 return repsep_snprintf(bf, size, "%-*s", width, out);
843 }
844
845 static int64_t
846 sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
847 {
848 union perf_mem_data_src data_src_l;
849 union perf_mem_data_src data_src_r;
850
851 if (left->mem_info)
852 data_src_l = left->mem_info->data_src;
853 else
854 data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
855
856 if (right->mem_info)
857 data_src_r = right->mem_info->data_src;
858 else
859 data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
860
861 return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
862 }
863
864 static const char * const snoop_access[] = {
865 "N/A",
866 "None",
867 "Miss",
868 "Hit",
869 "HitM",
870 };
871 #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
872
873 static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
874 size_t size, unsigned int width)
875 {
876 char out[64];
877 size_t sz = sizeof(out) - 1; /* -1 for null termination */
878 size_t i, l = 0;
879 u64 m = PERF_MEM_SNOOP_NA;
880
881 out[0] = '\0';
882
883 if (he->mem_info)
884 m = he->mem_info->data_src.mem_snoop;
885
886 for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {
887 if (!(m & 0x1))
888 continue;
889 if (l) {
890 strcat(out, " or ");
891 l += 4;
892 }
893 strncat(out, snoop_access[i], sz - l);
894 l += strlen(snoop_access[i]);
895 }
896
897 if (*out == '\0')
898 strcpy(out, "N/A");
899
900 return repsep_snprintf(bf, size, "%-*s", width, out);
901 }
902
903 static inline u64 cl_address(u64 address)
904 {
905 /* return the cacheline of the address */
906 return (address & ~(cacheline_size - 1));
907 }
908
909 static int64_t
910 sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
911 {
912 u64 l, r;
913 struct map *l_map, *r_map;
914
915 if (!left->mem_info) return -1;
916 if (!right->mem_info) return 1;
917
918 /* group event types together */
919 if (left->cpumode > right->cpumode) return -1;
920 if (left->cpumode < right->cpumode) return 1;
921
922 l_map = left->mem_info->daddr.map;
923 r_map = right->mem_info->daddr.map;
924
925 /* if both are NULL, jump to sort on al_addr instead */
926 if (!l_map && !r_map)
927 goto addr;
928
929 if (!l_map) return -1;
930 if (!r_map) return 1;
931
932 if (l_map->maj > r_map->maj) return -1;
933 if (l_map->maj < r_map->maj) return 1;
934
935 if (l_map->min > r_map->min) return -1;
936 if (l_map->min < r_map->min) return 1;
937
938 if (l_map->ino > r_map->ino) return -1;
939 if (l_map->ino < r_map->ino) return 1;
940
941 if (l_map->ino_generation > r_map->ino_generation) return -1;
942 if (l_map->ino_generation < r_map->ino_generation) return 1;
943
944 /*
945 * Addresses with no major/minor numbers are assumed to be
946 * anonymous in userspace. Sort those on pid then address.
947 *
948 * The kernel and non-zero major/minor mapped areas are
949 * assumed to be unity mapped. Sort those on address.
950 */
951
952 if ((left->cpumode != PERF_RECORD_MISC_KERNEL) &&
953 (!(l_map->flags & MAP_SHARED)) &&
954 !l_map->maj && !l_map->min && !l_map->ino &&
955 !l_map->ino_generation) {
956 /* userspace anonymous */
957
958 if (left->thread->pid_ > right->thread->pid_) return -1;
959 if (left->thread->pid_ < right->thread->pid_) return 1;
960 }
961
962 addr:
963 /* al_addr does all the right addr - start + offset calculations */
964 l = cl_address(left->mem_info->daddr.al_addr);
965 r = cl_address(right->mem_info->daddr.al_addr);
966
967 if (l > r) return -1;
968 if (l < r) return 1;
969
970 return 0;
971 }
972
973 static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf,
974 size_t size, unsigned int width)
975 {
976
977 uint64_t addr = 0;
978 struct map *map = NULL;
979 struct symbol *sym = NULL;
980 char level = he->level;
981
982 if (he->mem_info) {
983 addr = cl_address(he->mem_info->daddr.al_addr);
984 map = he->mem_info->daddr.map;
985 sym = he->mem_info->daddr.sym;
986
987 /* print [s] for shared data mmaps */
988 if ((he->cpumode != PERF_RECORD_MISC_KERNEL) &&
989 map && (map->type == MAP__VARIABLE) &&
990 (map->flags & MAP_SHARED) &&
991 (map->maj || map->min || map->ino ||
992 map->ino_generation))
993 level = 's';
994 else if (!map)
995 level = 'X';
996 }
997 return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size,
998 width);
999 }
1000
1001 struct sort_entry sort_mispredict = {
1002 .se_header = "Branch Mispredicted",
1003 .se_cmp = sort__mispredict_cmp,
1004 .se_snprintf = hist_entry__mispredict_snprintf,
1005 .se_width_idx = HISTC_MISPREDICT,
1006 };
1007
1008 static u64 he_weight(struct hist_entry *he)
1009 {
1010 return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
1011 }
1012
1013 static int64_t
1014 sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
1015 {
1016 return he_weight(left) - he_weight(right);
1017 }
1018
1019 static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,
1020 size_t size, unsigned int width)
1021 {
1022 return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he));
1023 }
1024
1025 struct sort_entry sort_local_weight = {
1026 .se_header = "Local Weight",
1027 .se_cmp = sort__local_weight_cmp,
1028 .se_snprintf = hist_entry__local_weight_snprintf,
1029 .se_width_idx = HISTC_LOCAL_WEIGHT,
1030 };
1031
1032 static int64_t
1033 sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
1034 {
1035 return left->stat.weight - right->stat.weight;
1036 }
1037
1038 static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,
1039 size_t size, unsigned int width)
1040 {
1041 return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight);
1042 }
1043
1044 struct sort_entry sort_global_weight = {
1045 .se_header = "Weight",
1046 .se_cmp = sort__global_weight_cmp,
1047 .se_snprintf = hist_entry__global_weight_snprintf,
1048 .se_width_idx = HISTC_GLOBAL_WEIGHT,
1049 };
1050
1051 struct sort_entry sort_mem_daddr_sym = {
1052 .se_header = "Data Symbol",
1053 .se_cmp = sort__daddr_cmp,
1054 .se_snprintf = hist_entry__daddr_snprintf,
1055 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
1056 };
1057
1058 struct sort_entry sort_mem_daddr_dso = {
1059 .se_header = "Data Object",
1060 .se_cmp = sort__dso_daddr_cmp,
1061 .se_snprintf = hist_entry__dso_daddr_snprintf,
1062 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
1063 };
1064
1065 struct sort_entry sort_mem_locked = {
1066 .se_header = "Locked",
1067 .se_cmp = sort__locked_cmp,
1068 .se_snprintf = hist_entry__locked_snprintf,
1069 .se_width_idx = HISTC_MEM_LOCKED,
1070 };
1071
1072 struct sort_entry sort_mem_tlb = {
1073 .se_header = "TLB access",
1074 .se_cmp = sort__tlb_cmp,
1075 .se_snprintf = hist_entry__tlb_snprintf,
1076 .se_width_idx = HISTC_MEM_TLB,
1077 };
1078
1079 struct sort_entry sort_mem_lvl = {
1080 .se_header = "Memory access",
1081 .se_cmp = sort__lvl_cmp,
1082 .se_snprintf = hist_entry__lvl_snprintf,
1083 .se_width_idx = HISTC_MEM_LVL,
1084 };
1085
1086 struct sort_entry sort_mem_snoop = {
1087 .se_header = "Snoop",
1088 .se_cmp = sort__snoop_cmp,
1089 .se_snprintf = hist_entry__snoop_snprintf,
1090 .se_width_idx = HISTC_MEM_SNOOP,
1091 };
1092
1093 struct sort_entry sort_mem_dcacheline = {
1094 .se_header = "Data Cacheline",
1095 .se_cmp = sort__dcacheline_cmp,
1096 .se_snprintf = hist_entry__dcacheline_snprintf,
1097 .se_width_idx = HISTC_MEM_DCACHELINE,
1098 };
1099
1100 static int64_t
1101 sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
1102 {
1103 if (!left->branch_info || !right->branch_info)
1104 return cmp_null(left->branch_info, right->branch_info);
1105
1106 return left->branch_info->flags.abort !=
1107 right->branch_info->flags.abort;
1108 }
1109
1110 static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf,
1111 size_t size, unsigned int width)
1112 {
1113 static const char *out = "N/A";
1114
1115 if (he->branch_info) {
1116 if (he->branch_info->flags.abort)
1117 out = "A";
1118 else
1119 out = ".";
1120 }
1121
1122 return repsep_snprintf(bf, size, "%-*s", width, out);
1123 }
1124
1125 struct sort_entry sort_abort = {
1126 .se_header = "Transaction abort",
1127 .se_cmp = sort__abort_cmp,
1128 .se_snprintf = hist_entry__abort_snprintf,
1129 .se_width_idx = HISTC_ABORT,
1130 };
1131
1132 static int64_t
1133 sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right)
1134 {
1135 if (!left->branch_info || !right->branch_info)
1136 return cmp_null(left->branch_info, right->branch_info);
1137
1138 return left->branch_info->flags.in_tx !=
1139 right->branch_info->flags.in_tx;
1140 }
1141
1142 static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf,
1143 size_t size, unsigned int width)
1144 {
1145 static const char *out = "N/A";
1146
1147 if (he->branch_info) {
1148 if (he->branch_info->flags.in_tx)
1149 out = "T";
1150 else
1151 out = ".";
1152 }
1153
1154 return repsep_snprintf(bf, size, "%-*s", width, out);
1155 }
1156
1157 struct sort_entry sort_in_tx = {
1158 .se_header = "Branch in transaction",
1159 .se_cmp = sort__in_tx_cmp,
1160 .se_snprintf = hist_entry__in_tx_snprintf,
1161 .se_width_idx = HISTC_IN_TX,
1162 };
1163
1164 static int64_t
1165 sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
1166 {
1167 return left->transaction - right->transaction;
1168 }
1169
1170 static inline char *add_str(char *p, const char *str)
1171 {
1172 strcpy(p, str);
1173 return p + strlen(str);
1174 }
1175
1176 static struct txbit {
1177 unsigned flag;
1178 const char *name;
1179 int skip_for_len;
1180 } txbits[] = {
1181 { PERF_TXN_ELISION, "EL ", 0 },
1182 { PERF_TXN_TRANSACTION, "TX ", 1 },
1183 { PERF_TXN_SYNC, "SYNC ", 1 },
1184 { PERF_TXN_ASYNC, "ASYNC ", 0 },
1185 { PERF_TXN_RETRY, "RETRY ", 0 },
1186 { PERF_TXN_CONFLICT, "CON ", 0 },
1187 { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 },
1188 { PERF_TXN_CAPACITY_READ, "CAP-READ ", 0 },
1189 { 0, NULL, 0 }
1190 };
1191
1192 int hist_entry__transaction_len(void)
1193 {
1194 int i;
1195 int len = 0;
1196
1197 for (i = 0; txbits[i].name; i++) {
1198 if (!txbits[i].skip_for_len)
1199 len += strlen(txbits[i].name);
1200 }
1201 len += 4; /* :XX<space> */
1202 return len;
1203 }
1204
1205 static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf,
1206 size_t size, unsigned int width)
1207 {
1208 u64 t = he->transaction;
1209 char buf[128];
1210 char *p = buf;
1211 int i;
1212
1213 buf[0] = 0;
1214 for (i = 0; txbits[i].name; i++)
1215 if (txbits[i].flag & t)
1216 p = add_str(p, txbits[i].name);
1217 if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC)))
1218 p = add_str(p, "NEITHER ");
1219 if (t & PERF_TXN_ABORT_MASK) {
1220 sprintf(p, ":%" PRIx64,
1221 (t & PERF_TXN_ABORT_MASK) >>
1222 PERF_TXN_ABORT_SHIFT);
1223 p += strlen(p);
1224 }
1225
1226 return repsep_snprintf(bf, size, "%-*s", width, buf);
1227 }
1228
1229 struct sort_entry sort_transaction = {
1230 .se_header = "Transaction ",
1231 .se_cmp = sort__transaction_cmp,
1232 .se_snprintf = hist_entry__transaction_snprintf,
1233 .se_width_idx = HISTC_TRANSACTION,
1234 };
1235
1236 struct sort_dimension {
1237 const char *name;
1238 struct sort_entry *entry;
1239 int taken;
1240 };
1241
1242 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
1243
1244 static struct sort_dimension common_sort_dimensions[] = {
1245 DIM(SORT_PID, "pid", sort_thread),
1246 DIM(SORT_COMM, "comm", sort_comm),
1247 DIM(SORT_DSO, "dso", sort_dso),
1248 DIM(SORT_SYM, "symbol", sort_sym),
1249 DIM(SORT_PARENT, "parent", sort_parent),
1250 DIM(SORT_CPU, "cpu", sort_cpu),
1251 DIM(SORT_SRCLINE, "srcline", sort_srcline),
1252 DIM(SORT_SRCFILE, "srcfile", sort_srcfile),
1253 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
1254 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
1255 DIM(SORT_TRANSACTION, "transaction", sort_transaction),
1256 };
1257
1258 #undef DIM
1259
1260 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
1261
1262 static struct sort_dimension bstack_sort_dimensions[] = {
1263 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
1264 DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
1265 DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
1266 DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
1267 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
1268 DIM(SORT_IN_TX, "in_tx", sort_in_tx),
1269 DIM(SORT_ABORT, "abort", sort_abort),
1270 DIM(SORT_CYCLES, "cycles", sort_cycles),
1271 };
1272
1273 #undef DIM
1274
1275 #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
1276
1277 static struct sort_dimension memory_sort_dimensions[] = {
1278 DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
1279 DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
1280 DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
1281 DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
1282 DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
1283 DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
1284 DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline),
1285 };
1286
1287 #undef DIM
1288
1289 struct hpp_dimension {
1290 const char *name;
1291 struct perf_hpp_fmt *fmt;
1292 int taken;
1293 };
1294
1295 #define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
1296
1297 static struct hpp_dimension hpp_sort_dimensions[] = {
1298 DIM(PERF_HPP__OVERHEAD, "overhead"),
1299 DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"),
1300 DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
1301 DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
1302 DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
1303 DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"),
1304 DIM(PERF_HPP__SAMPLES, "sample"),
1305 DIM(PERF_HPP__PERIOD, "period"),
1306 };
1307
1308 #undef DIM
1309
1310 struct hpp_sort_entry {
1311 struct perf_hpp_fmt hpp;
1312 struct sort_entry *se;
1313 };
1314
1315 bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1316 {
1317 struct hpp_sort_entry *hse_a;
1318 struct hpp_sort_entry *hse_b;
1319
1320 if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
1321 return false;
1322
1323 hse_a = container_of(a, struct hpp_sort_entry, hpp);
1324 hse_b = container_of(b, struct hpp_sort_entry, hpp);
1325
1326 return hse_a->se == hse_b->se;
1327 }
1328
1329 void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists)
1330 {
1331 struct hpp_sort_entry *hse;
1332
1333 if (!perf_hpp__is_sort_entry(fmt))
1334 return;
1335
1336 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1337 hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name));
1338 }
1339
1340 static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1341 struct perf_evsel *evsel)
1342 {
1343 struct hpp_sort_entry *hse;
1344 size_t len = fmt->user_len;
1345
1346 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1347
1348 if (!len)
1349 len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx);
1350
1351 return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name);
1352 }
1353
1354 static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
1355 struct perf_hpp *hpp __maybe_unused,
1356 struct perf_evsel *evsel)
1357 {
1358 struct hpp_sort_entry *hse;
1359 size_t len = fmt->user_len;
1360
1361 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1362
1363 if (!len)
1364 len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx);
1365
1366 return len;
1367 }
1368
1369 static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1370 struct hist_entry *he)
1371 {
1372 struct hpp_sort_entry *hse;
1373 size_t len = fmt->user_len;
1374
1375 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1376
1377 if (!len)
1378 len = hists__col_len(he->hists, hse->se->se_width_idx);
1379
1380 return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
1381 }
1382
1383 static int64_t __sort__hpp_cmp(struct perf_hpp_fmt *fmt,
1384 struct hist_entry *a, struct hist_entry *b)
1385 {
1386 struct hpp_sort_entry *hse;
1387
1388 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1389 return hse->se->se_cmp(a, b);
1390 }
1391
1392 static int64_t __sort__hpp_collapse(struct perf_hpp_fmt *fmt,
1393 struct hist_entry *a, struct hist_entry *b)
1394 {
1395 struct hpp_sort_entry *hse;
1396 int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
1397
1398 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1399 collapse_fn = hse->se->se_collapse ?: hse->se->se_cmp;
1400 return collapse_fn(a, b);
1401 }
1402
1403 static int64_t __sort__hpp_sort(struct perf_hpp_fmt *fmt,
1404 struct hist_entry *a, struct hist_entry *b)
1405 {
1406 struct hpp_sort_entry *hse;
1407 int64_t (*sort_fn)(struct hist_entry *, struct hist_entry *);
1408
1409 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1410 sort_fn = hse->se->se_sort ?: hse->se->se_cmp;
1411 return sort_fn(a, b);
1412 }
1413
1414 static struct hpp_sort_entry *
1415 __sort_dimension__alloc_hpp(struct sort_dimension *sd)
1416 {
1417 struct hpp_sort_entry *hse;
1418
1419 hse = malloc(sizeof(*hse));
1420 if (hse == NULL) {
1421 pr_err("Memory allocation failed\n");
1422 return NULL;
1423 }
1424
1425 hse->se = sd->entry;
1426 hse->hpp.name = sd->entry->se_header;
1427 hse->hpp.header = __sort__hpp_header;
1428 hse->hpp.width = __sort__hpp_width;
1429 hse->hpp.entry = __sort__hpp_entry;
1430 hse->hpp.color = NULL;
1431
1432 hse->hpp.cmp = __sort__hpp_cmp;
1433 hse->hpp.collapse = __sort__hpp_collapse;
1434 hse->hpp.sort = __sort__hpp_sort;
1435
1436 INIT_LIST_HEAD(&hse->hpp.list);
1437 INIT_LIST_HEAD(&hse->hpp.sort_list);
1438 hse->hpp.elide = false;
1439 hse->hpp.len = 0;
1440 hse->hpp.user_len = 0;
1441
1442 return hse;
1443 }
1444
1445 bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
1446 {
1447 return format->header == __sort__hpp_header;
1448 }
1449
1450 static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd)
1451 {
1452 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1453
1454 if (hse == NULL)
1455 return -1;
1456
1457 perf_hpp__register_sort_field(&hse->hpp);
1458 return 0;
1459 }
1460
1461 static int __sort_dimension__add_hpp_output(struct sort_dimension *sd)
1462 {
1463 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1464
1465 if (hse == NULL)
1466 return -1;
1467
1468 perf_hpp__column_register(&hse->hpp);
1469 return 0;
1470 }
1471
1472 static int __sort_dimension__add(struct sort_dimension *sd)
1473 {
1474 if (sd->taken)
1475 return 0;
1476
1477 if (__sort_dimension__add_hpp_sort(sd) < 0)
1478 return -1;
1479
1480 if (sd->entry->se_collapse)
1481 sort__need_collapse = 1;
1482
1483 sd->taken = 1;
1484
1485 return 0;
1486 }
1487
1488 static int __hpp_dimension__add(struct hpp_dimension *hd)
1489 {
1490 if (!hd->taken) {
1491 hd->taken = 1;
1492
1493 perf_hpp__register_sort_field(hd->fmt);
1494 }
1495 return 0;
1496 }
1497
1498 static int __sort_dimension__add_output(struct sort_dimension *sd)
1499 {
1500 if (sd->taken)
1501 return 0;
1502
1503 if (__sort_dimension__add_hpp_output(sd) < 0)
1504 return -1;
1505
1506 sd->taken = 1;
1507 return 0;
1508 }
1509
1510 static int __hpp_dimension__add_output(struct hpp_dimension *hd)
1511 {
1512 if (!hd->taken) {
1513 hd->taken = 1;
1514
1515 perf_hpp__column_register(hd->fmt);
1516 }
1517 return 0;
1518 }
1519
1520 int sort_dimension__add(const char *tok)
1521 {
1522 unsigned int i;
1523
1524 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1525 struct sort_dimension *sd = &common_sort_dimensions[i];
1526
1527 if (strncasecmp(tok, sd->name, strlen(tok)))
1528 continue;
1529
1530 if (sd->entry == &sort_parent) {
1531 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
1532 if (ret) {
1533 char err[BUFSIZ];
1534
1535 regerror(ret, &parent_regex, err, sizeof(err));
1536 pr_err("Invalid regex: %s\n%s", parent_pattern, err);
1537 return -EINVAL;
1538 }
1539 sort__has_parent = 1;
1540 } else if (sd->entry == &sort_sym) {
1541 sort__has_sym = 1;
1542 /*
1543 * perf diff displays the performance difference amongst
1544 * two or more perf.data files. Those files could come
1545 * from different binaries. So we should not compare
1546 * their ips, but the name of symbol.
1547 */
1548 if (sort__mode == SORT_MODE__DIFF)
1549 sd->entry->se_collapse = sort__sym_sort;
1550
1551 } else if (sd->entry == &sort_dso) {
1552 sort__has_dso = 1;
1553 }
1554
1555 return __sort_dimension__add(sd);
1556 }
1557
1558 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1559 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1560
1561 if (strncasecmp(tok, hd->name, strlen(tok)))
1562 continue;
1563
1564 return __hpp_dimension__add(hd);
1565 }
1566
1567 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1568 struct sort_dimension *sd = &bstack_sort_dimensions[i];
1569
1570 if (strncasecmp(tok, sd->name, strlen(tok)))
1571 continue;
1572
1573 if (sort__mode != SORT_MODE__BRANCH)
1574 return -EINVAL;
1575
1576 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
1577 sort__has_sym = 1;
1578
1579 __sort_dimension__add(sd);
1580 return 0;
1581 }
1582
1583 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1584 struct sort_dimension *sd = &memory_sort_dimensions[i];
1585
1586 if (strncasecmp(tok, sd->name, strlen(tok)))
1587 continue;
1588
1589 if (sort__mode != SORT_MODE__MEMORY)
1590 return -EINVAL;
1591
1592 if (sd->entry == &sort_mem_daddr_sym)
1593 sort__has_sym = 1;
1594
1595 __sort_dimension__add(sd);
1596 return 0;
1597 }
1598
1599 return -ESRCH;
1600 }
1601
1602 static const char *get_default_sort_order(void)
1603 {
1604 const char *default_sort_orders[] = {
1605 default_sort_order,
1606 default_branch_sort_order,
1607 default_mem_sort_order,
1608 default_top_sort_order,
1609 default_diff_sort_order,
1610 };
1611
1612 BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
1613
1614 return default_sort_orders[sort__mode];
1615 }
1616
1617 static int setup_sort_order(void)
1618 {
1619 char *new_sort_order;
1620
1621 /*
1622 * Append '+'-prefixed sort order to the default sort
1623 * order string.
1624 */
1625 if (!sort_order || is_strict_order(sort_order))
1626 return 0;
1627
1628 if (sort_order[1] == '\0') {
1629 error("Invalid --sort key: `+'");
1630 return -EINVAL;
1631 }
1632
1633 /*
1634 * We allocate new sort_order string, but we never free it,
1635 * because it's checked over the rest of the code.
1636 */
1637 if (asprintf(&new_sort_order, "%s,%s",
1638 get_default_sort_order(), sort_order + 1) < 0) {
1639 error("Not enough memory to set up --sort");
1640 return -ENOMEM;
1641 }
1642
1643 sort_order = new_sort_order;
1644 return 0;
1645 }
1646
1647 static int __setup_sorting(void)
1648 {
1649 char *tmp, *tok, *str;
1650 const char *sort_keys;
1651 int ret = 0;
1652
1653 ret = setup_sort_order();
1654 if (ret)
1655 return ret;
1656
1657 sort_keys = sort_order;
1658 if (sort_keys == NULL) {
1659 if (is_strict_order(field_order)) {
1660 /*
1661 * If user specified field order but no sort order,
1662 * we'll honor it and not add default sort orders.
1663 */
1664 return 0;
1665 }
1666
1667 sort_keys = get_default_sort_order();
1668 }
1669
1670 str = strdup(sort_keys);
1671 if (str == NULL) {
1672 error("Not enough memory to setup sort keys");
1673 return -ENOMEM;
1674 }
1675
1676 for (tok = strtok_r(str, ", ", &tmp);
1677 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1678 ret = sort_dimension__add(tok);
1679 if (ret == -EINVAL) {
1680 error("Invalid --sort key: `%s'", tok);
1681 break;
1682 } else if (ret == -ESRCH) {
1683 error("Unknown --sort key: `%s'", tok);
1684 break;
1685 }
1686 }
1687
1688 free(str);
1689 return ret;
1690 }
1691
1692 void perf_hpp__set_elide(int idx, bool elide)
1693 {
1694 struct perf_hpp_fmt *fmt;
1695 struct hpp_sort_entry *hse;
1696
1697 perf_hpp__for_each_format(fmt) {
1698 if (!perf_hpp__is_sort_entry(fmt))
1699 continue;
1700
1701 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1702 if (hse->se->se_width_idx == idx) {
1703 fmt->elide = elide;
1704 break;
1705 }
1706 }
1707 }
1708
1709 static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)
1710 {
1711 if (list && strlist__nr_entries(list) == 1) {
1712 if (fp != NULL)
1713 fprintf(fp, "# %s: %s\n", list_name,
1714 strlist__entry(list, 0)->s);
1715 return true;
1716 }
1717 return false;
1718 }
1719
1720 static bool get_elide(int idx, FILE *output)
1721 {
1722 switch (idx) {
1723 case HISTC_SYMBOL:
1724 return __get_elide(symbol_conf.sym_list, "symbol", output);
1725 case HISTC_DSO:
1726 return __get_elide(symbol_conf.dso_list, "dso", output);
1727 case HISTC_COMM:
1728 return __get_elide(symbol_conf.comm_list, "comm", output);
1729 default:
1730 break;
1731 }
1732
1733 if (sort__mode != SORT_MODE__BRANCH)
1734 return false;
1735
1736 switch (idx) {
1737 case HISTC_SYMBOL_FROM:
1738 return __get_elide(symbol_conf.sym_from_list, "sym_from", output);
1739 case HISTC_SYMBOL_TO:
1740 return __get_elide(symbol_conf.sym_to_list, "sym_to", output);
1741 case HISTC_DSO_FROM:
1742 return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
1743 case HISTC_DSO_TO:
1744 return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
1745 default:
1746 break;
1747 }
1748
1749 return false;
1750 }
1751
1752 void sort__setup_elide(FILE *output)
1753 {
1754 struct perf_hpp_fmt *fmt;
1755 struct hpp_sort_entry *hse;
1756
1757 perf_hpp__for_each_format(fmt) {
1758 if (!perf_hpp__is_sort_entry(fmt))
1759 continue;
1760
1761 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1762 fmt->elide = get_elide(hse->se->se_width_idx, output);
1763 }
1764
1765 /*
1766 * It makes no sense to elide all of sort entries.
1767 * Just revert them to show up again.
1768 */
1769 perf_hpp__for_each_format(fmt) {
1770 if (!perf_hpp__is_sort_entry(fmt))
1771 continue;
1772
1773 if (!fmt->elide)
1774 return;
1775 }
1776
1777 perf_hpp__for_each_format(fmt) {
1778 if (!perf_hpp__is_sort_entry(fmt))
1779 continue;
1780
1781 fmt->elide = false;
1782 }
1783 }
1784
1785 static int output_field_add(char *tok)
1786 {
1787 unsigned int i;
1788
1789 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1790 struct sort_dimension *sd = &common_sort_dimensions[i];
1791
1792 if (strncasecmp(tok, sd->name, strlen(tok)))
1793 continue;
1794
1795 return __sort_dimension__add_output(sd);
1796 }
1797
1798 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1799 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1800
1801 if (strncasecmp(tok, hd->name, strlen(tok)))
1802 continue;
1803
1804 return __hpp_dimension__add_output(hd);
1805 }
1806
1807 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1808 struct sort_dimension *sd = &bstack_sort_dimensions[i];
1809
1810 if (strncasecmp(tok, sd->name, strlen(tok)))
1811 continue;
1812
1813 return __sort_dimension__add_output(sd);
1814 }
1815
1816 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1817 struct sort_dimension *sd = &memory_sort_dimensions[i];
1818
1819 if (strncasecmp(tok, sd->name, strlen(tok)))
1820 continue;
1821
1822 return __sort_dimension__add_output(sd);
1823 }
1824
1825 return -ESRCH;
1826 }
1827
1828 static void reset_dimensions(void)
1829 {
1830 unsigned int i;
1831
1832 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++)
1833 common_sort_dimensions[i].taken = 0;
1834
1835 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++)
1836 hpp_sort_dimensions[i].taken = 0;
1837
1838 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++)
1839 bstack_sort_dimensions[i].taken = 0;
1840
1841 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++)
1842 memory_sort_dimensions[i].taken = 0;
1843 }
1844
1845 bool is_strict_order(const char *order)
1846 {
1847 return order && (*order != '+');
1848 }
1849
1850 static int __setup_output_field(void)
1851 {
1852 char *tmp, *tok, *str, *strp;
1853 int ret = -EINVAL;
1854
1855 if (field_order == NULL)
1856 return 0;
1857
1858 reset_dimensions();
1859
1860 strp = str = strdup(field_order);
1861 if (str == NULL) {
1862 error("Not enough memory to setup output fields");
1863 return -ENOMEM;
1864 }
1865
1866 if (!is_strict_order(field_order))
1867 strp++;
1868
1869 if (!strlen(strp)) {
1870 error("Invalid --fields key: `+'");
1871 goto out;
1872 }
1873
1874 for (tok = strtok_r(strp, ", ", &tmp);
1875 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1876 ret = output_field_add(tok);
1877 if (ret == -EINVAL) {
1878 error("Invalid --fields key: `%s'", tok);
1879 break;
1880 } else if (ret == -ESRCH) {
1881 error("Unknown --fields key: `%s'", tok);
1882 break;
1883 }
1884 }
1885
1886 out:
1887 free(str);
1888 return ret;
1889 }
1890
1891 int setup_sorting(void)
1892 {
1893 int err;
1894
1895 err = __setup_sorting();
1896 if (err < 0)
1897 return err;
1898
1899 if (parent_pattern != default_parent_pattern) {
1900 err = sort_dimension__add("parent");
1901 if (err < 0)
1902 return err;
1903 }
1904
1905 reset_dimensions();
1906
1907 /*
1908 * perf diff doesn't use default hpp output fields.
1909 */
1910 if (sort__mode != SORT_MODE__DIFF)
1911 perf_hpp__init();
1912
1913 err = __setup_output_field();
1914 if (err < 0)
1915 return err;
1916
1917 /* copy sort keys to output fields */
1918 perf_hpp__setup_output_field();
1919 /* and then copy output fields to sort keys */
1920 perf_hpp__append_sort_keys();
1921
1922 return 0;
1923 }
1924
1925 void reset_output_field(void)
1926 {
1927 sort__need_collapse = 0;
1928 sort__has_parent = 0;
1929 sort__has_sym = 0;
1930 sort__has_dso = 0;
1931
1932 field_order = NULL;
1933 sort_order = NULL;
1934
1935 reset_dimensions();
1936 perf_hpp__reset_output_field();
1937 }