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