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