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
repsep_snprintf(char * bf,size_t size,const char * fmt,...)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
cmp_null(const void * l,const void * r)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
sort__thread_cmp(struct hist_entry * left,struct hist_entry * right)65 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
66 {
67 return right->thread->tid - left->thread->tid;
68 }
69
hist_entry__thread_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)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
sort__comm_cmp(struct hist_entry * left,struct hist_entry * right)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
sort__comm_collapse(struct hist_entry * left,struct hist_entry * right)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
sort__comm_sort(struct hist_entry * left,struct hist_entry * right)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
hist_entry__comm_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)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
_sort__dso_cmp(struct map * map_l,struct map * map_r)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
sort__dso_cmp(struct hist_entry * left,struct hist_entry * right)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
_hist_entry__dso_snprintf(struct map * map,char * bf,size_t size,unsigned int width)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
hist_entry__dso_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)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
_sort__addr_cmp(u64 left_ip,u64 right_ip)179 static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip)
180 {
181 return (int64_t)(right_ip - left_ip);
182 }
183
_sort__sym_cmp(struct symbol * sym_l,struct symbol * sym_r)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
sort__sym_cmp(struct hist_entry * left,struct hist_entry * right)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
sort__sym_sort(struct hist_entry * left,struct hist_entry * right)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
_hist_entry__sym_snprintf(struct map * map,struct symbol * sym,u64 ip,char level,char * bf,size_t size,unsigned int width)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
hist_entry__sym_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)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
sort__srcline_cmp(struct hist_entry * left,struct hist_entry * right)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
hist_entry__srcline_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)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
get_srcfile(struct hist_entry * e)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
sort__srcfile_cmp(struct hist_entry * left,struct hist_entry * right)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
hist_entry__srcfile_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)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
sort__parent_cmp(struct hist_entry * left,struct hist_entry * right)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
hist_entry__parent_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)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
sort__cpu_cmp(struct hist_entry * left,struct hist_entry * right)407 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
408 {
409 return right->cpu - left->cpu;
410 }
411
hist_entry__cpu_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)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
sort__socket_cmp(struct hist_entry * left,struct hist_entry * right)428 sort__socket_cmp(struct hist_entry *left, struct hist_entry *right)
429 {
430 return right->socket - left->socket;
431 }
432
hist_entry__socket_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)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
sort__dso_from_cmp(struct hist_entry * left,struct hist_entry * right)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
hist_entry__dso_from_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)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
sort__dso_to_cmp(struct hist_entry * left,struct hist_entry * right)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
hist_entry__dso_to_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)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
sort__sym_from_cmp(struct hist_entry * left,struct hist_entry * right)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
sort__sym_to_cmp(struct hist_entry * left,struct hist_entry * right)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
hist_entry__sym_from_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)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
hist_entry__sym_to_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)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
sort__mispredict_cmp(struct hist_entry * left,struct hist_entry * right)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
hist_entry__mispredict_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)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
sort__cycles_cmp(struct hist_entry * left,struct hist_entry * right)605 sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right)
606 {
607 if (!left->branch_info || !right->branch_info)
608 return cmp_null(left->branch_info, right->branch_info);
609
610 return left->branch_info->flags.cycles -
611 right->branch_info->flags.cycles;
612 }
613
hist_entry__cycles_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)614 static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf,
615 size_t size, unsigned int width)
616 {
617 if (!he->branch_info)
618 return scnprintf(bf, size, "%-.*s", width, "N/A");
619 if (he->branch_info->flags.cycles == 0)
620 return repsep_snprintf(bf, size, "%-*s", width, "-");
621 return repsep_snprintf(bf, size, "%-*hd", width,
622 he->branch_info->flags.cycles);
623 }
624
625 struct sort_entry sort_cycles = {
626 .se_header = "Basic Block Cycles",
627 .se_cmp = sort__cycles_cmp,
628 .se_snprintf = hist_entry__cycles_snprintf,
629 .se_width_idx = HISTC_CYCLES,
630 };
631
632 /* --sort daddr_sym */
633 static int64_t
sort__daddr_cmp(struct hist_entry * left,struct hist_entry * right)634 sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
635 {
636 uint64_t l = 0, r = 0;
637
638 if (left->mem_info)
639 l = left->mem_info->daddr.addr;
640 if (right->mem_info)
641 r = right->mem_info->daddr.addr;
642
643 return (int64_t)(r - l);
644 }
645
hist_entry__daddr_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)646 static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,
647 size_t size, unsigned int width)
648 {
649 uint64_t addr = 0;
650 struct map *map = NULL;
651 struct symbol *sym = NULL;
652
653 if (he->mem_info) {
654 addr = he->mem_info->daddr.addr;
655 map = he->mem_info->daddr.map;
656 sym = he->mem_info->daddr.sym;
657 }
658 return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
659 width);
660 }
661
662 static int64_t
sort__iaddr_cmp(struct hist_entry * left,struct hist_entry * right)663 sort__iaddr_cmp(struct hist_entry *left, struct hist_entry *right)
664 {
665 uint64_t l = 0, r = 0;
666
667 if (left->mem_info)
668 l = left->mem_info->iaddr.addr;
669 if (right->mem_info)
670 r = right->mem_info->iaddr.addr;
671
672 return (int64_t)(r - l);
673 }
674
hist_entry__iaddr_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)675 static int hist_entry__iaddr_snprintf(struct hist_entry *he, char *bf,
676 size_t size, unsigned int width)
677 {
678 uint64_t addr = 0;
679 struct map *map = NULL;
680 struct symbol *sym = NULL;
681
682 if (he->mem_info) {
683 addr = he->mem_info->iaddr.addr;
684 map = he->mem_info->iaddr.map;
685 sym = he->mem_info->iaddr.sym;
686 }
687 return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
688 width);
689 }
690
691 static int64_t
sort__dso_daddr_cmp(struct hist_entry * left,struct hist_entry * right)692 sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
693 {
694 struct map *map_l = NULL;
695 struct map *map_r = NULL;
696
697 if (left->mem_info)
698 map_l = left->mem_info->daddr.map;
699 if (right->mem_info)
700 map_r = right->mem_info->daddr.map;
701
702 return _sort__dso_cmp(map_l, map_r);
703 }
704
hist_entry__dso_daddr_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)705 static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,
706 size_t size, unsigned int width)
707 {
708 struct map *map = NULL;
709
710 if (he->mem_info)
711 map = he->mem_info->daddr.map;
712
713 return _hist_entry__dso_snprintf(map, bf, size, width);
714 }
715
716 static int64_t
sort__locked_cmp(struct hist_entry * left,struct hist_entry * right)717 sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
718 {
719 union perf_mem_data_src data_src_l;
720 union perf_mem_data_src data_src_r;
721
722 if (left->mem_info)
723 data_src_l = left->mem_info->data_src;
724 else
725 data_src_l.mem_lock = PERF_MEM_LOCK_NA;
726
727 if (right->mem_info)
728 data_src_r = right->mem_info->data_src;
729 else
730 data_src_r.mem_lock = PERF_MEM_LOCK_NA;
731
732 return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
733 }
734
hist_entry__locked_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)735 static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,
736 size_t size, unsigned int width)
737 {
738 const char *out;
739 u64 mask = PERF_MEM_LOCK_NA;
740
741 if (he->mem_info)
742 mask = he->mem_info->data_src.mem_lock;
743
744 if (mask & PERF_MEM_LOCK_NA)
745 out = "N/A";
746 else if (mask & PERF_MEM_LOCK_LOCKED)
747 out = "Yes";
748 else
749 out = "No";
750
751 return repsep_snprintf(bf, size, "%-*s", width, out);
752 }
753
754 static int64_t
sort__tlb_cmp(struct hist_entry * left,struct hist_entry * right)755 sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
756 {
757 union perf_mem_data_src data_src_l;
758 union perf_mem_data_src data_src_r;
759
760 if (left->mem_info)
761 data_src_l = left->mem_info->data_src;
762 else
763 data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
764
765 if (right->mem_info)
766 data_src_r = right->mem_info->data_src;
767 else
768 data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
769
770 return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
771 }
772
773 static const char * const tlb_access[] = {
774 "N/A",
775 "HIT",
776 "MISS",
777 "L1",
778 "L2",
779 "Walker",
780 "Fault",
781 };
782 #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
783
hist_entry__tlb_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)784 static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,
785 size_t size, unsigned int width)
786 {
787 char out[64];
788 size_t sz = sizeof(out) - 1; /* -1 for null termination */
789 size_t l = 0, i;
790 u64 m = PERF_MEM_TLB_NA;
791 u64 hit, miss;
792
793 out[0] = '\0';
794
795 if (he->mem_info)
796 m = he->mem_info->data_src.mem_dtlb;
797
798 hit = m & PERF_MEM_TLB_HIT;
799 miss = m & PERF_MEM_TLB_MISS;
800
801 /* already taken care of */
802 m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
803
804 for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) {
805 if (!(m & 0x1))
806 continue;
807 if (l) {
808 strcat(out, " or ");
809 l += 4;
810 }
811 strncat(out, tlb_access[i], sz - l);
812 l += strlen(tlb_access[i]);
813 }
814 if (*out == '\0')
815 strcpy(out, "N/A");
816 if (hit)
817 strncat(out, " hit", sz - l);
818 if (miss)
819 strncat(out, " miss", sz - l);
820
821 return repsep_snprintf(bf, size, "%-*s", width, out);
822 }
823
824 static int64_t
sort__lvl_cmp(struct hist_entry * left,struct hist_entry * right)825 sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
826 {
827 union perf_mem_data_src data_src_l;
828 union perf_mem_data_src data_src_r;
829
830 if (left->mem_info)
831 data_src_l = left->mem_info->data_src;
832 else
833 data_src_l.mem_lvl = PERF_MEM_LVL_NA;
834
835 if (right->mem_info)
836 data_src_r = right->mem_info->data_src;
837 else
838 data_src_r.mem_lvl = PERF_MEM_LVL_NA;
839
840 return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
841 }
842
843 static const char * const mem_lvl[] = {
844 "N/A",
845 "HIT",
846 "MISS",
847 "L1",
848 "LFB",
849 "L2",
850 "L3",
851 "Local RAM",
852 "Remote RAM (1 hop)",
853 "Remote RAM (2 hops)",
854 "Remote Cache (1 hop)",
855 "Remote Cache (2 hops)",
856 "I/O",
857 "Uncached",
858 };
859 #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
860
hist_entry__lvl_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)861 static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,
862 size_t size, unsigned int width)
863 {
864 char out[64];
865 size_t sz = sizeof(out) - 1; /* -1 for null termination */
866 size_t i, l = 0;
867 u64 m = PERF_MEM_LVL_NA;
868 u64 hit, miss;
869
870 if (he->mem_info)
871 m = he->mem_info->data_src.mem_lvl;
872
873 out[0] = '\0';
874
875 hit = m & PERF_MEM_LVL_HIT;
876 miss = m & PERF_MEM_LVL_MISS;
877
878 /* already taken care of */
879 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
880
881 for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) {
882 if (!(m & 0x1))
883 continue;
884 if (l) {
885 strcat(out, " or ");
886 l += 4;
887 }
888 strncat(out, mem_lvl[i], sz - l);
889 l += strlen(mem_lvl[i]);
890 }
891 if (*out == '\0')
892 strcpy(out, "N/A");
893 if (hit)
894 strncat(out, " hit", sz - l);
895 if (miss)
896 strncat(out, " miss", sz - l);
897
898 return repsep_snprintf(bf, size, "%-*s", width, out);
899 }
900
901 static int64_t
sort__snoop_cmp(struct hist_entry * left,struct hist_entry * right)902 sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
903 {
904 union perf_mem_data_src data_src_l;
905 union perf_mem_data_src data_src_r;
906
907 if (left->mem_info)
908 data_src_l = left->mem_info->data_src;
909 else
910 data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
911
912 if (right->mem_info)
913 data_src_r = right->mem_info->data_src;
914 else
915 data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
916
917 return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
918 }
919
920 static const char * const snoop_access[] = {
921 "N/A",
922 "None",
923 "Miss",
924 "Hit",
925 "HitM",
926 };
927 #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
928
hist_entry__snoop_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)929 static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
930 size_t size, unsigned int width)
931 {
932 char out[64];
933 size_t sz = sizeof(out) - 1; /* -1 for null termination */
934 size_t i, l = 0;
935 u64 m = PERF_MEM_SNOOP_NA;
936
937 out[0] = '\0';
938
939 if (he->mem_info)
940 m = he->mem_info->data_src.mem_snoop;
941
942 for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {
943 if (!(m & 0x1))
944 continue;
945 if (l) {
946 strcat(out, " or ");
947 l += 4;
948 }
949 strncat(out, snoop_access[i], sz - l);
950 l += strlen(snoop_access[i]);
951 }
952
953 if (*out == '\0')
954 strcpy(out, "N/A");
955
956 return repsep_snprintf(bf, size, "%-*s", width, out);
957 }
958
cl_address(u64 address)959 static inline u64 cl_address(u64 address)
960 {
961 /* return the cacheline of the address */
962 return (address & ~(cacheline_size - 1));
963 }
964
965 static int64_t
sort__dcacheline_cmp(struct hist_entry * left,struct hist_entry * right)966 sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
967 {
968 u64 l, r;
969 struct map *l_map, *r_map;
970
971 if (!left->mem_info) return -1;
972 if (!right->mem_info) return 1;
973
974 /* group event types together */
975 if (left->cpumode > right->cpumode) return -1;
976 if (left->cpumode < right->cpumode) return 1;
977
978 l_map = left->mem_info->daddr.map;
979 r_map = right->mem_info->daddr.map;
980
981 /* if both are NULL, jump to sort on al_addr instead */
982 if (!l_map && !r_map)
983 goto addr;
984
985 if (!l_map) return -1;
986 if (!r_map) return 1;
987
988 if (l_map->maj > r_map->maj) return -1;
989 if (l_map->maj < r_map->maj) return 1;
990
991 if (l_map->min > r_map->min) return -1;
992 if (l_map->min < r_map->min) return 1;
993
994 if (l_map->ino > r_map->ino) return -1;
995 if (l_map->ino < r_map->ino) return 1;
996
997 if (l_map->ino_generation > r_map->ino_generation) return -1;
998 if (l_map->ino_generation < r_map->ino_generation) return 1;
999
1000 /*
1001 * Addresses with no major/minor numbers are assumed to be
1002 * anonymous in userspace. Sort those on pid then address.
1003 *
1004 * The kernel and non-zero major/minor mapped areas are
1005 * assumed to be unity mapped. Sort those on address.
1006 */
1007
1008 if ((left->cpumode != PERF_RECORD_MISC_KERNEL) &&
1009 (!(l_map->flags & MAP_SHARED)) &&
1010 !l_map->maj && !l_map->min && !l_map->ino &&
1011 !l_map->ino_generation) {
1012 /* userspace anonymous */
1013
1014 if (left->thread->pid_ > right->thread->pid_) return -1;
1015 if (left->thread->pid_ < right->thread->pid_) return 1;
1016 }
1017
1018 addr:
1019 /* al_addr does all the right addr - start + offset calculations */
1020 l = cl_address(left->mem_info->daddr.al_addr);
1021 r = cl_address(right->mem_info->daddr.al_addr);
1022
1023 if (l > r) return -1;
1024 if (l < r) return 1;
1025
1026 return 0;
1027 }
1028
hist_entry__dcacheline_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)1029 static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf,
1030 size_t size, unsigned int width)
1031 {
1032
1033 uint64_t addr = 0;
1034 struct map *map = NULL;
1035 struct symbol *sym = NULL;
1036 char level = he->level;
1037
1038 if (he->mem_info) {
1039 addr = cl_address(he->mem_info->daddr.al_addr);
1040 map = he->mem_info->daddr.map;
1041 sym = he->mem_info->daddr.sym;
1042
1043 /* print [s] for shared data mmaps */
1044 if ((he->cpumode != PERF_RECORD_MISC_KERNEL) &&
1045 map && (map->type == MAP__VARIABLE) &&
1046 (map->flags & MAP_SHARED) &&
1047 (map->maj || map->min || map->ino ||
1048 map->ino_generation))
1049 level = 's';
1050 else if (!map)
1051 level = 'X';
1052 }
1053 return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size,
1054 width);
1055 }
1056
1057 struct sort_entry sort_mispredict = {
1058 .se_header = "Branch Mispredicted",
1059 .se_cmp = sort__mispredict_cmp,
1060 .se_snprintf = hist_entry__mispredict_snprintf,
1061 .se_width_idx = HISTC_MISPREDICT,
1062 };
1063
he_weight(struct hist_entry * he)1064 static u64 he_weight(struct hist_entry *he)
1065 {
1066 return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
1067 }
1068
1069 static int64_t
sort__local_weight_cmp(struct hist_entry * left,struct hist_entry * right)1070 sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
1071 {
1072 return he_weight(left) - he_weight(right);
1073 }
1074
hist_entry__local_weight_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)1075 static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,
1076 size_t size, unsigned int width)
1077 {
1078 return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he));
1079 }
1080
1081 struct sort_entry sort_local_weight = {
1082 .se_header = "Local Weight",
1083 .se_cmp = sort__local_weight_cmp,
1084 .se_snprintf = hist_entry__local_weight_snprintf,
1085 .se_width_idx = HISTC_LOCAL_WEIGHT,
1086 };
1087
1088 static int64_t
sort__global_weight_cmp(struct hist_entry * left,struct hist_entry * right)1089 sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
1090 {
1091 return left->stat.weight - right->stat.weight;
1092 }
1093
hist_entry__global_weight_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)1094 static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,
1095 size_t size, unsigned int width)
1096 {
1097 return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight);
1098 }
1099
1100 struct sort_entry sort_global_weight = {
1101 .se_header = "Weight",
1102 .se_cmp = sort__global_weight_cmp,
1103 .se_snprintf = hist_entry__global_weight_snprintf,
1104 .se_width_idx = HISTC_GLOBAL_WEIGHT,
1105 };
1106
1107 struct sort_entry sort_mem_daddr_sym = {
1108 .se_header = "Data Symbol",
1109 .se_cmp = sort__daddr_cmp,
1110 .se_snprintf = hist_entry__daddr_snprintf,
1111 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
1112 };
1113
1114 struct sort_entry sort_mem_iaddr_sym = {
1115 .se_header = "Code Symbol",
1116 .se_cmp = sort__iaddr_cmp,
1117 .se_snprintf = hist_entry__iaddr_snprintf,
1118 .se_width_idx = HISTC_MEM_IADDR_SYMBOL,
1119 };
1120
1121 struct sort_entry sort_mem_daddr_dso = {
1122 .se_header = "Data Object",
1123 .se_cmp = sort__dso_daddr_cmp,
1124 .se_snprintf = hist_entry__dso_daddr_snprintf,
1125 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
1126 };
1127
1128 struct sort_entry sort_mem_locked = {
1129 .se_header = "Locked",
1130 .se_cmp = sort__locked_cmp,
1131 .se_snprintf = hist_entry__locked_snprintf,
1132 .se_width_idx = HISTC_MEM_LOCKED,
1133 };
1134
1135 struct sort_entry sort_mem_tlb = {
1136 .se_header = "TLB access",
1137 .se_cmp = sort__tlb_cmp,
1138 .se_snprintf = hist_entry__tlb_snprintf,
1139 .se_width_idx = HISTC_MEM_TLB,
1140 };
1141
1142 struct sort_entry sort_mem_lvl = {
1143 .se_header = "Memory access",
1144 .se_cmp = sort__lvl_cmp,
1145 .se_snprintf = hist_entry__lvl_snprintf,
1146 .se_width_idx = HISTC_MEM_LVL,
1147 };
1148
1149 struct sort_entry sort_mem_snoop = {
1150 .se_header = "Snoop",
1151 .se_cmp = sort__snoop_cmp,
1152 .se_snprintf = hist_entry__snoop_snprintf,
1153 .se_width_idx = HISTC_MEM_SNOOP,
1154 };
1155
1156 struct sort_entry sort_mem_dcacheline = {
1157 .se_header = "Data Cacheline",
1158 .se_cmp = sort__dcacheline_cmp,
1159 .se_snprintf = hist_entry__dcacheline_snprintf,
1160 .se_width_idx = HISTC_MEM_DCACHELINE,
1161 };
1162
1163 static int64_t
sort__abort_cmp(struct hist_entry * left,struct hist_entry * right)1164 sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
1165 {
1166 if (!left->branch_info || !right->branch_info)
1167 return cmp_null(left->branch_info, right->branch_info);
1168
1169 return left->branch_info->flags.abort !=
1170 right->branch_info->flags.abort;
1171 }
1172
hist_entry__abort_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)1173 static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf,
1174 size_t size, unsigned int width)
1175 {
1176 static const char *out = "N/A";
1177
1178 if (he->branch_info) {
1179 if (he->branch_info->flags.abort)
1180 out = "A";
1181 else
1182 out = ".";
1183 }
1184
1185 return repsep_snprintf(bf, size, "%-*s", width, out);
1186 }
1187
1188 struct sort_entry sort_abort = {
1189 .se_header = "Transaction abort",
1190 .se_cmp = sort__abort_cmp,
1191 .se_snprintf = hist_entry__abort_snprintf,
1192 .se_width_idx = HISTC_ABORT,
1193 };
1194
1195 static int64_t
sort__in_tx_cmp(struct hist_entry * left,struct hist_entry * right)1196 sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right)
1197 {
1198 if (!left->branch_info || !right->branch_info)
1199 return cmp_null(left->branch_info, right->branch_info);
1200
1201 return left->branch_info->flags.in_tx !=
1202 right->branch_info->flags.in_tx;
1203 }
1204
hist_entry__in_tx_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)1205 static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf,
1206 size_t size, unsigned int width)
1207 {
1208 static const char *out = "N/A";
1209
1210 if (he->branch_info) {
1211 if (he->branch_info->flags.in_tx)
1212 out = "T";
1213 else
1214 out = ".";
1215 }
1216
1217 return repsep_snprintf(bf, size, "%-*s", width, out);
1218 }
1219
1220 struct sort_entry sort_in_tx = {
1221 .se_header = "Branch in transaction",
1222 .se_cmp = sort__in_tx_cmp,
1223 .se_snprintf = hist_entry__in_tx_snprintf,
1224 .se_width_idx = HISTC_IN_TX,
1225 };
1226
1227 static int64_t
sort__transaction_cmp(struct hist_entry * left,struct hist_entry * right)1228 sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
1229 {
1230 return left->transaction - right->transaction;
1231 }
1232
add_str(char * p,const char * str)1233 static inline char *add_str(char *p, const char *str)
1234 {
1235 strcpy(p, str);
1236 return p + strlen(str);
1237 }
1238
1239 static struct txbit {
1240 unsigned flag;
1241 const char *name;
1242 int skip_for_len;
1243 } txbits[] = {
1244 { PERF_TXN_ELISION, "EL ", 0 },
1245 { PERF_TXN_TRANSACTION, "TX ", 1 },
1246 { PERF_TXN_SYNC, "SYNC ", 1 },
1247 { PERF_TXN_ASYNC, "ASYNC ", 0 },
1248 { PERF_TXN_RETRY, "RETRY ", 0 },
1249 { PERF_TXN_CONFLICT, "CON ", 0 },
1250 { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 },
1251 { PERF_TXN_CAPACITY_READ, "CAP-READ ", 0 },
1252 { 0, NULL, 0 }
1253 };
1254
hist_entry__transaction_len(void)1255 int hist_entry__transaction_len(void)
1256 {
1257 int i;
1258 int len = 0;
1259
1260 for (i = 0; txbits[i].name; i++) {
1261 if (!txbits[i].skip_for_len)
1262 len += strlen(txbits[i].name);
1263 }
1264 len += 4; /* :XX<space> */
1265 return len;
1266 }
1267
hist_entry__transaction_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)1268 static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf,
1269 size_t size, unsigned int width)
1270 {
1271 u64 t = he->transaction;
1272 char buf[128];
1273 char *p = buf;
1274 int i;
1275
1276 buf[0] = 0;
1277 for (i = 0; txbits[i].name; i++)
1278 if (txbits[i].flag & t)
1279 p = add_str(p, txbits[i].name);
1280 if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC)))
1281 p = add_str(p, "NEITHER ");
1282 if (t & PERF_TXN_ABORT_MASK) {
1283 sprintf(p, ":%" PRIx64,
1284 (t & PERF_TXN_ABORT_MASK) >>
1285 PERF_TXN_ABORT_SHIFT);
1286 p += strlen(p);
1287 }
1288
1289 return repsep_snprintf(bf, size, "%-*s", width, buf);
1290 }
1291
1292 struct sort_entry sort_transaction = {
1293 .se_header = "Transaction ",
1294 .se_cmp = sort__transaction_cmp,
1295 .se_snprintf = hist_entry__transaction_snprintf,
1296 .se_width_idx = HISTC_TRANSACTION,
1297 };
1298
1299 struct sort_dimension {
1300 const char *name;
1301 struct sort_entry *entry;
1302 int taken;
1303 };
1304
1305 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
1306
1307 static struct sort_dimension common_sort_dimensions[] = {
1308 DIM(SORT_PID, "pid", sort_thread),
1309 DIM(SORT_COMM, "comm", sort_comm),
1310 DIM(SORT_DSO, "dso", sort_dso),
1311 DIM(SORT_SYM, "symbol", sort_sym),
1312 DIM(SORT_PARENT, "parent", sort_parent),
1313 DIM(SORT_CPU, "cpu", sort_cpu),
1314 DIM(SORT_SOCKET, "socket", sort_socket),
1315 DIM(SORT_SRCLINE, "srcline", sort_srcline),
1316 DIM(SORT_SRCFILE, "srcfile", sort_srcfile),
1317 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
1318 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
1319 DIM(SORT_TRANSACTION, "transaction", sort_transaction),
1320 };
1321
1322 #undef DIM
1323
1324 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
1325
1326 static struct sort_dimension bstack_sort_dimensions[] = {
1327 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
1328 DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
1329 DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
1330 DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
1331 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
1332 DIM(SORT_IN_TX, "in_tx", sort_in_tx),
1333 DIM(SORT_ABORT, "abort", sort_abort),
1334 DIM(SORT_CYCLES, "cycles", sort_cycles),
1335 };
1336
1337 #undef DIM
1338
1339 #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
1340
1341 static struct sort_dimension memory_sort_dimensions[] = {
1342 DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
1343 DIM(SORT_MEM_IADDR_SYMBOL, "symbol_iaddr", sort_mem_iaddr_sym),
1344 DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
1345 DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
1346 DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
1347 DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
1348 DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
1349 DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline),
1350 };
1351
1352 #undef DIM
1353
1354 struct hpp_dimension {
1355 const char *name;
1356 struct perf_hpp_fmt *fmt;
1357 int taken;
1358 };
1359
1360 #define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
1361
1362 static struct hpp_dimension hpp_sort_dimensions[] = {
1363 DIM(PERF_HPP__OVERHEAD, "overhead"),
1364 DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"),
1365 DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
1366 DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
1367 DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
1368 DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"),
1369 DIM(PERF_HPP__SAMPLES, "sample"),
1370 DIM(PERF_HPP__PERIOD, "period"),
1371 };
1372
1373 #undef DIM
1374
1375 struct hpp_sort_entry {
1376 struct perf_hpp_fmt hpp;
1377 struct sort_entry *se;
1378 };
1379
perf_hpp__same_sort_entry(struct perf_hpp_fmt * a,struct perf_hpp_fmt * b)1380 bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1381 {
1382 struct hpp_sort_entry *hse_a;
1383 struct hpp_sort_entry *hse_b;
1384
1385 if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
1386 return false;
1387
1388 hse_a = container_of(a, struct hpp_sort_entry, hpp);
1389 hse_b = container_of(b, struct hpp_sort_entry, hpp);
1390
1391 return hse_a->se == hse_b->se;
1392 }
1393
perf_hpp__reset_sort_width(struct perf_hpp_fmt * fmt,struct hists * hists)1394 void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists)
1395 {
1396 struct hpp_sort_entry *hse;
1397
1398 if (!perf_hpp__is_sort_entry(fmt))
1399 return;
1400
1401 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1402 hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name));
1403 }
1404
__sort__hpp_header(struct perf_hpp_fmt * fmt,struct perf_hpp * hpp,struct perf_evsel * evsel)1405 static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1406 struct perf_evsel *evsel)
1407 {
1408 struct hpp_sort_entry *hse;
1409 size_t len = fmt->user_len;
1410
1411 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1412
1413 if (!len)
1414 len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx);
1415
1416 return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name);
1417 }
1418
__sort__hpp_width(struct perf_hpp_fmt * fmt,struct perf_hpp * hpp __maybe_unused,struct perf_evsel * evsel)1419 static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
1420 struct perf_hpp *hpp __maybe_unused,
1421 struct perf_evsel *evsel)
1422 {
1423 struct hpp_sort_entry *hse;
1424 size_t len = fmt->user_len;
1425
1426 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1427
1428 if (!len)
1429 len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx);
1430
1431 return len;
1432 }
1433
__sort__hpp_entry(struct perf_hpp_fmt * fmt,struct perf_hpp * hpp,struct hist_entry * he)1434 static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1435 struct hist_entry *he)
1436 {
1437 struct hpp_sort_entry *hse;
1438 size_t len = fmt->user_len;
1439
1440 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1441
1442 if (!len)
1443 len = hists__col_len(he->hists, hse->se->se_width_idx);
1444
1445 return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
1446 }
1447
__sort__hpp_cmp(struct perf_hpp_fmt * fmt,struct hist_entry * a,struct hist_entry * b)1448 static int64_t __sort__hpp_cmp(struct perf_hpp_fmt *fmt,
1449 struct hist_entry *a, struct hist_entry *b)
1450 {
1451 struct hpp_sort_entry *hse;
1452
1453 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1454 return hse->se->se_cmp(a, b);
1455 }
1456
__sort__hpp_collapse(struct perf_hpp_fmt * fmt,struct hist_entry * a,struct hist_entry * b)1457 static int64_t __sort__hpp_collapse(struct perf_hpp_fmt *fmt,
1458 struct hist_entry *a, struct hist_entry *b)
1459 {
1460 struct hpp_sort_entry *hse;
1461 int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
1462
1463 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1464 collapse_fn = hse->se->se_collapse ?: hse->se->se_cmp;
1465 return collapse_fn(a, b);
1466 }
1467
__sort__hpp_sort(struct perf_hpp_fmt * fmt,struct hist_entry * a,struct hist_entry * b)1468 static int64_t __sort__hpp_sort(struct perf_hpp_fmt *fmt,
1469 struct hist_entry *a, struct hist_entry *b)
1470 {
1471 struct hpp_sort_entry *hse;
1472 int64_t (*sort_fn)(struct hist_entry *, struct hist_entry *);
1473
1474 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1475 sort_fn = hse->se->se_sort ?: hse->se->se_cmp;
1476 return sort_fn(a, b);
1477 }
1478
1479 static struct hpp_sort_entry *
__sort_dimension__alloc_hpp(struct sort_dimension * sd)1480 __sort_dimension__alloc_hpp(struct sort_dimension *sd)
1481 {
1482 struct hpp_sort_entry *hse;
1483
1484 hse = malloc(sizeof(*hse));
1485 if (hse == NULL) {
1486 pr_err("Memory allocation failed\n");
1487 return NULL;
1488 }
1489
1490 hse->se = sd->entry;
1491 hse->hpp.name = sd->entry->se_header;
1492 hse->hpp.header = __sort__hpp_header;
1493 hse->hpp.width = __sort__hpp_width;
1494 hse->hpp.entry = __sort__hpp_entry;
1495 hse->hpp.color = NULL;
1496
1497 hse->hpp.cmp = __sort__hpp_cmp;
1498 hse->hpp.collapse = __sort__hpp_collapse;
1499 hse->hpp.sort = __sort__hpp_sort;
1500
1501 INIT_LIST_HEAD(&hse->hpp.list);
1502 INIT_LIST_HEAD(&hse->hpp.sort_list);
1503 hse->hpp.elide = false;
1504 hse->hpp.len = 0;
1505 hse->hpp.user_len = 0;
1506
1507 return hse;
1508 }
1509
perf_hpp__is_sort_entry(struct perf_hpp_fmt * format)1510 bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
1511 {
1512 return format->header == __sort__hpp_header;
1513 }
1514
__sort_dimension__add_hpp_sort(struct sort_dimension * sd)1515 static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd)
1516 {
1517 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1518
1519 if (hse == NULL)
1520 return -1;
1521
1522 perf_hpp__register_sort_field(&hse->hpp);
1523 return 0;
1524 }
1525
__sort_dimension__add_hpp_output(struct sort_dimension * sd)1526 static int __sort_dimension__add_hpp_output(struct sort_dimension *sd)
1527 {
1528 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1529
1530 if (hse == NULL)
1531 return -1;
1532
1533 perf_hpp__column_register(&hse->hpp);
1534 return 0;
1535 }
1536
__sort_dimension__add(struct sort_dimension * sd)1537 static int __sort_dimension__add(struct sort_dimension *sd)
1538 {
1539 if (sd->taken)
1540 return 0;
1541
1542 if (__sort_dimension__add_hpp_sort(sd) < 0)
1543 return -1;
1544
1545 if (sd->entry->se_collapse)
1546 sort__need_collapse = 1;
1547
1548 sd->taken = 1;
1549
1550 return 0;
1551 }
1552
__hpp_dimension__add(struct hpp_dimension * hd)1553 static int __hpp_dimension__add(struct hpp_dimension *hd)
1554 {
1555 if (!hd->taken) {
1556 hd->taken = 1;
1557
1558 perf_hpp__register_sort_field(hd->fmt);
1559 }
1560 return 0;
1561 }
1562
__sort_dimension__add_output(struct sort_dimension * sd)1563 static int __sort_dimension__add_output(struct sort_dimension *sd)
1564 {
1565 if (sd->taken)
1566 return 0;
1567
1568 if (__sort_dimension__add_hpp_output(sd) < 0)
1569 return -1;
1570
1571 sd->taken = 1;
1572 return 0;
1573 }
1574
__hpp_dimension__add_output(struct hpp_dimension * hd)1575 static int __hpp_dimension__add_output(struct hpp_dimension *hd)
1576 {
1577 if (!hd->taken) {
1578 hd->taken = 1;
1579
1580 perf_hpp__column_register(hd->fmt);
1581 }
1582 return 0;
1583 }
1584
hpp_dimension__add_output(unsigned col)1585 int hpp_dimension__add_output(unsigned col)
1586 {
1587 BUG_ON(col >= PERF_HPP__MAX_INDEX);
1588 return __hpp_dimension__add_output(&hpp_sort_dimensions[col]);
1589 }
1590
sort_dimension__add(const char * tok)1591 int sort_dimension__add(const char *tok)
1592 {
1593 unsigned int i;
1594
1595 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1596 struct sort_dimension *sd = &common_sort_dimensions[i];
1597
1598 if (strncasecmp(tok, sd->name, strlen(tok)))
1599 continue;
1600
1601 if (sd->entry == &sort_parent) {
1602 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
1603 if (ret) {
1604 char err[BUFSIZ];
1605
1606 regerror(ret, &parent_regex, err, sizeof(err));
1607 pr_err("Invalid regex: %s\n%s", parent_pattern, err);
1608 return -EINVAL;
1609 }
1610 sort__has_parent = 1;
1611 } else if (sd->entry == &sort_sym) {
1612 sort__has_sym = 1;
1613 /*
1614 * perf diff displays the performance difference amongst
1615 * two or more perf.data files. Those files could come
1616 * from different binaries. So we should not compare
1617 * their ips, but the name of symbol.
1618 */
1619 if (sort__mode == SORT_MODE__DIFF)
1620 sd->entry->se_collapse = sort__sym_sort;
1621
1622 } else if (sd->entry == &sort_dso) {
1623 sort__has_dso = 1;
1624 } else if (sd->entry == &sort_socket) {
1625 sort__has_socket = 1;
1626 }
1627
1628 return __sort_dimension__add(sd);
1629 }
1630
1631 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1632 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1633
1634 if (strncasecmp(tok, hd->name, strlen(tok)))
1635 continue;
1636
1637 return __hpp_dimension__add(hd);
1638 }
1639
1640 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1641 struct sort_dimension *sd = &bstack_sort_dimensions[i];
1642
1643 if (strncasecmp(tok, sd->name, strlen(tok)))
1644 continue;
1645
1646 if (sort__mode != SORT_MODE__BRANCH)
1647 return -EINVAL;
1648
1649 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
1650 sort__has_sym = 1;
1651
1652 __sort_dimension__add(sd);
1653 return 0;
1654 }
1655
1656 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1657 struct sort_dimension *sd = &memory_sort_dimensions[i];
1658
1659 if (strncasecmp(tok, sd->name, strlen(tok)))
1660 continue;
1661
1662 if (sort__mode != SORT_MODE__MEMORY)
1663 return -EINVAL;
1664
1665 if (sd->entry == &sort_mem_daddr_sym)
1666 sort__has_sym = 1;
1667
1668 __sort_dimension__add(sd);
1669 return 0;
1670 }
1671
1672 return -ESRCH;
1673 }
1674
get_default_sort_order(void)1675 static const char *get_default_sort_order(void)
1676 {
1677 const char *default_sort_orders[] = {
1678 default_sort_order,
1679 default_branch_sort_order,
1680 default_mem_sort_order,
1681 default_top_sort_order,
1682 default_diff_sort_order,
1683 };
1684
1685 BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
1686
1687 return default_sort_orders[sort__mode];
1688 }
1689
setup_sort_order(void)1690 static int setup_sort_order(void)
1691 {
1692 char *new_sort_order;
1693
1694 /*
1695 * Append '+'-prefixed sort order to the default sort
1696 * order string.
1697 */
1698 if (!sort_order || is_strict_order(sort_order))
1699 return 0;
1700
1701 if (sort_order[1] == '\0') {
1702 error("Invalid --sort key: `+'");
1703 return -EINVAL;
1704 }
1705
1706 /*
1707 * We allocate new sort_order string, but we never free it,
1708 * because it's checked over the rest of the code.
1709 */
1710 if (asprintf(&new_sort_order, "%s,%s",
1711 get_default_sort_order(), sort_order + 1) < 0) {
1712 error("Not enough memory to set up --sort");
1713 return -ENOMEM;
1714 }
1715
1716 sort_order = new_sort_order;
1717 return 0;
1718 }
1719
__setup_sorting(void)1720 static int __setup_sorting(void)
1721 {
1722 char *tmp, *tok, *str;
1723 const char *sort_keys;
1724 int ret = 0;
1725
1726 ret = setup_sort_order();
1727 if (ret)
1728 return ret;
1729
1730 sort_keys = sort_order;
1731 if (sort_keys == NULL) {
1732 if (is_strict_order(field_order)) {
1733 /*
1734 * If user specified field order but no sort order,
1735 * we'll honor it and not add default sort orders.
1736 */
1737 return 0;
1738 }
1739
1740 sort_keys = get_default_sort_order();
1741 }
1742
1743 str = strdup(sort_keys);
1744 if (str == NULL) {
1745 error("Not enough memory to setup sort keys");
1746 return -ENOMEM;
1747 }
1748
1749 for (tok = strtok_r(str, ", ", &tmp);
1750 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1751 ret = sort_dimension__add(tok);
1752 if (ret == -EINVAL) {
1753 error("Invalid --sort key: `%s'", tok);
1754 break;
1755 } else if (ret == -ESRCH) {
1756 error("Unknown --sort key: `%s'", tok);
1757 break;
1758 }
1759 }
1760
1761 free(str);
1762 return ret;
1763 }
1764
perf_hpp__set_elide(int idx,bool elide)1765 void perf_hpp__set_elide(int idx, bool elide)
1766 {
1767 struct perf_hpp_fmt *fmt;
1768 struct hpp_sort_entry *hse;
1769
1770 perf_hpp__for_each_format(fmt) {
1771 if (!perf_hpp__is_sort_entry(fmt))
1772 continue;
1773
1774 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1775 if (hse->se->se_width_idx == idx) {
1776 fmt->elide = elide;
1777 break;
1778 }
1779 }
1780 }
1781
__get_elide(struct strlist * list,const char * list_name,FILE * fp)1782 static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)
1783 {
1784 if (list && strlist__nr_entries(list) == 1) {
1785 if (fp != NULL)
1786 fprintf(fp, "# %s: %s\n", list_name,
1787 strlist__entry(list, 0)->s);
1788 return true;
1789 }
1790 return false;
1791 }
1792
get_elide(int idx,FILE * output)1793 static bool get_elide(int idx, FILE *output)
1794 {
1795 switch (idx) {
1796 case HISTC_SYMBOL:
1797 return __get_elide(symbol_conf.sym_list, "symbol", output);
1798 case HISTC_DSO:
1799 return __get_elide(symbol_conf.dso_list, "dso", output);
1800 case HISTC_COMM:
1801 return __get_elide(symbol_conf.comm_list, "comm", output);
1802 default:
1803 break;
1804 }
1805
1806 if (sort__mode != SORT_MODE__BRANCH)
1807 return false;
1808
1809 switch (idx) {
1810 case HISTC_SYMBOL_FROM:
1811 return __get_elide(symbol_conf.sym_from_list, "sym_from", output);
1812 case HISTC_SYMBOL_TO:
1813 return __get_elide(symbol_conf.sym_to_list, "sym_to", output);
1814 case HISTC_DSO_FROM:
1815 return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
1816 case HISTC_DSO_TO:
1817 return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
1818 default:
1819 break;
1820 }
1821
1822 return false;
1823 }
1824
sort__setup_elide(FILE * output)1825 void sort__setup_elide(FILE *output)
1826 {
1827 struct perf_hpp_fmt *fmt;
1828 struct hpp_sort_entry *hse;
1829
1830 perf_hpp__for_each_format(fmt) {
1831 if (!perf_hpp__is_sort_entry(fmt))
1832 continue;
1833
1834 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1835 fmt->elide = get_elide(hse->se->se_width_idx, output);
1836 }
1837
1838 /*
1839 * It makes no sense to elide all of sort entries.
1840 * Just revert them to show up again.
1841 */
1842 perf_hpp__for_each_format(fmt) {
1843 if (!perf_hpp__is_sort_entry(fmt))
1844 continue;
1845
1846 if (!fmt->elide)
1847 return;
1848 }
1849
1850 perf_hpp__for_each_format(fmt) {
1851 if (!perf_hpp__is_sort_entry(fmt))
1852 continue;
1853
1854 fmt->elide = false;
1855 }
1856 }
1857
output_field_add(char * tok)1858 static int output_field_add(char *tok)
1859 {
1860 unsigned int i;
1861
1862 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1863 struct sort_dimension *sd = &common_sort_dimensions[i];
1864
1865 if (strncasecmp(tok, sd->name, strlen(tok)))
1866 continue;
1867
1868 return __sort_dimension__add_output(sd);
1869 }
1870
1871 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1872 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1873
1874 if (strncasecmp(tok, hd->name, strlen(tok)))
1875 continue;
1876
1877 return __hpp_dimension__add_output(hd);
1878 }
1879
1880 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1881 struct sort_dimension *sd = &bstack_sort_dimensions[i];
1882
1883 if (strncasecmp(tok, sd->name, strlen(tok)))
1884 continue;
1885
1886 return __sort_dimension__add_output(sd);
1887 }
1888
1889 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1890 struct sort_dimension *sd = &memory_sort_dimensions[i];
1891
1892 if (strncasecmp(tok, sd->name, strlen(tok)))
1893 continue;
1894
1895 return __sort_dimension__add_output(sd);
1896 }
1897
1898 return -ESRCH;
1899 }
1900
reset_dimensions(void)1901 static void reset_dimensions(void)
1902 {
1903 unsigned int i;
1904
1905 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++)
1906 common_sort_dimensions[i].taken = 0;
1907
1908 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++)
1909 hpp_sort_dimensions[i].taken = 0;
1910
1911 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++)
1912 bstack_sort_dimensions[i].taken = 0;
1913
1914 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++)
1915 memory_sort_dimensions[i].taken = 0;
1916 }
1917
is_strict_order(const char * order)1918 bool is_strict_order(const char *order)
1919 {
1920 return order && (*order != '+');
1921 }
1922
__setup_output_field(void)1923 static int __setup_output_field(void)
1924 {
1925 char *tmp, *tok, *str, *strp;
1926 int ret = -EINVAL;
1927
1928 if (field_order == NULL)
1929 return 0;
1930
1931 strp = str = strdup(field_order);
1932 if (str == NULL) {
1933 error("Not enough memory to setup output fields");
1934 return -ENOMEM;
1935 }
1936
1937 if (!is_strict_order(field_order))
1938 strp++;
1939
1940 if (!strlen(strp)) {
1941 error("Invalid --fields key: `+'");
1942 goto out;
1943 }
1944
1945 for (tok = strtok_r(strp, ", ", &tmp);
1946 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1947 ret = output_field_add(tok);
1948 if (ret == -EINVAL) {
1949 error("Invalid --fields key: `%s'", tok);
1950 break;
1951 } else if (ret == -ESRCH) {
1952 error("Unknown --fields key: `%s'", tok);
1953 break;
1954 }
1955 }
1956
1957 out:
1958 free(str);
1959 return ret;
1960 }
1961
setup_sorting(void)1962 int setup_sorting(void)
1963 {
1964 int err;
1965
1966 err = __setup_sorting();
1967 if (err < 0)
1968 return err;
1969
1970 if (parent_pattern != default_parent_pattern) {
1971 err = sort_dimension__add("parent");
1972 if (err < 0)
1973 return err;
1974 }
1975
1976 reset_dimensions();
1977
1978 /*
1979 * perf diff doesn't use default hpp output fields.
1980 */
1981 if (sort__mode != SORT_MODE__DIFF)
1982 perf_hpp__init();
1983
1984 err = __setup_output_field();
1985 if (err < 0)
1986 return err;
1987
1988 /* copy sort keys to output fields */
1989 perf_hpp__setup_output_field();
1990 /* and then copy output fields to sort keys */
1991 perf_hpp__append_sort_keys();
1992
1993 return 0;
1994 }
1995
reset_output_field(void)1996 void reset_output_field(void)
1997 {
1998 sort__need_collapse = 0;
1999 sort__has_parent = 0;
2000 sort__has_sym = 0;
2001 sort__has_dso = 0;
2002
2003 field_order = NULL;
2004 sort_order = NULL;
2005
2006 reset_dimensions();
2007 perf_hpp__reset_output_field();
2008 }
2009