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