1 #include "sort.h"
2 #include "hist.h"
3
4 regex_t parent_regex;
5 const char default_parent_pattern[] = "^sys_|^do_page_fault";
6 const char *parent_pattern = default_parent_pattern;
7 const char default_sort_order[] = "comm,dso,symbol";
8 const char *sort_order = default_sort_order;
9 int sort__need_collapse = 0;
10 int sort__has_parent = 0;
11 int sort__has_sym = 0;
12 int sort__branch_mode = -1; /* -1 = means not set */
13
14 enum sort_type sort__first_dimension;
15
16 LIST_HEAD(hist_entry__sort_list);
17
repsep_snprintf(char * bf,size_t size,const char * fmt,...)18 static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
19 {
20 int n;
21 va_list ap;
22
23 va_start(ap, fmt);
24 n = vsnprintf(bf, size, fmt, ap);
25 if (symbol_conf.field_sep && n > 0) {
26 char *sep = bf;
27
28 while (1) {
29 sep = strchr(sep, *symbol_conf.field_sep);
30 if (sep == NULL)
31 break;
32 *sep = '.';
33 }
34 }
35 va_end(ap);
36
37 if (n >= (int)size)
38 return size - 1;
39 return n;
40 }
41
cmp_null(void * l,void * r)42 static int64_t cmp_null(void *l, void *r)
43 {
44 if (!l && !r)
45 return 0;
46 else if (!l)
47 return -1;
48 else
49 return 1;
50 }
51
52 /* --sort pid */
53
54 static int64_t
sort__thread_cmp(struct hist_entry * left,struct hist_entry * right)55 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
56 {
57 return right->thread->pid - left->thread->pid;
58 }
59
hist_entry__thread_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)60 static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
61 size_t size, unsigned int width)
62 {
63 return repsep_snprintf(bf, size, "%*s:%5d", width - 6,
64 self->thread->comm ?: "", self->thread->pid);
65 }
66
67 struct sort_entry sort_thread = {
68 .se_header = "Command: Pid",
69 .se_cmp = sort__thread_cmp,
70 .se_snprintf = hist_entry__thread_snprintf,
71 .se_width_idx = HISTC_THREAD,
72 };
73
74 /* --sort comm */
75
76 static int64_t
sort__comm_cmp(struct hist_entry * left,struct hist_entry * right)77 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
78 {
79 return right->thread->pid - left->thread->pid;
80 }
81
82 static int64_t
sort__comm_collapse(struct hist_entry * left,struct hist_entry * right)83 sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
84 {
85 char *comm_l = left->thread->comm;
86 char *comm_r = right->thread->comm;
87
88 if (!comm_l || !comm_r)
89 return cmp_null(comm_l, comm_r);
90
91 return strcmp(comm_l, comm_r);
92 }
93
hist_entry__comm_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)94 static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
95 size_t size, unsigned int width)
96 {
97 return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
98 }
99
100 struct sort_entry sort_comm = {
101 .se_header = "Command",
102 .se_cmp = sort__comm_cmp,
103 .se_collapse = sort__comm_collapse,
104 .se_snprintf = hist_entry__comm_snprintf,
105 .se_width_idx = HISTC_COMM,
106 };
107
108 /* --sort dso */
109
_sort__dso_cmp(struct map * map_l,struct map * map_r)110 static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
111 {
112 struct dso *dso_l = map_l ? map_l->dso : NULL;
113 struct dso *dso_r = map_r ? map_r->dso : NULL;
114 const char *dso_name_l, *dso_name_r;
115
116 if (!dso_l || !dso_r)
117 return cmp_null(dso_l, dso_r);
118
119 if (verbose) {
120 dso_name_l = dso_l->long_name;
121 dso_name_r = dso_r->long_name;
122 } else {
123 dso_name_l = dso_l->short_name;
124 dso_name_r = dso_r->short_name;
125 }
126
127 return strcmp(dso_name_l, dso_name_r);
128 }
129
130 static int64_t
sort__dso_cmp(struct hist_entry * left,struct hist_entry * right)131 sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
132 {
133 return _sort__dso_cmp(left->ms.map, right->ms.map);
134 }
135
_hist_entry__dso_snprintf(struct map * map,char * bf,size_t size,unsigned int width)136 static int _hist_entry__dso_snprintf(struct map *map, char *bf,
137 size_t size, unsigned int width)
138 {
139 if (map && map->dso) {
140 const char *dso_name = !verbose ? map->dso->short_name :
141 map->dso->long_name;
142 return repsep_snprintf(bf, size, "%-*s", width, dso_name);
143 }
144
145 return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
146 }
147
hist_entry__dso_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)148 static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
149 size_t size, unsigned int width)
150 {
151 return _hist_entry__dso_snprintf(self->ms.map, bf, size, width);
152 }
153
154 struct sort_entry sort_dso = {
155 .se_header = "Shared Object",
156 .se_cmp = sort__dso_cmp,
157 .se_snprintf = hist_entry__dso_snprintf,
158 .se_width_idx = HISTC_DSO,
159 };
160
161 /* --sort symbol */
162
_sort__sym_cmp(struct symbol * sym_l,struct symbol * sym_r)163 static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
164 {
165 u64 ip_l, ip_r;
166
167 if (!sym_l || !sym_r)
168 return cmp_null(sym_l, sym_r);
169
170 if (sym_l == sym_r)
171 return 0;
172
173 ip_l = sym_l->start;
174 ip_r = sym_r->start;
175
176 return (int64_t)(ip_r - ip_l);
177 }
178
179 static int64_t
sort__sym_cmp(struct hist_entry * left,struct hist_entry * right)180 sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
181 {
182 if (!left->ms.sym && !right->ms.sym)
183 return right->level - left->level;
184
185 return _sort__sym_cmp(left->ms.sym, right->ms.sym);
186 }
187
_hist_entry__sym_snprintf(struct map * map,struct symbol * sym,u64 ip,char level,char * bf,size_t size,unsigned int width)188 static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
189 u64 ip, char level, char *bf, size_t size,
190 unsigned int width)
191 {
192 size_t ret = 0;
193
194 if (verbose) {
195 char o = map ? dso__symtab_origin(map->dso) : '!';
196 ret += repsep_snprintf(bf, size, "%-#*llx %c ",
197 BITS_PER_LONG / 4, ip, o);
198 }
199
200 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
201 if (sym && map) {
202 if (map->type == MAP__VARIABLE) {
203 ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
204 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
205 ip - map->unmap_ip(map, sym->start));
206 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
207 width - ret, "");
208 } else {
209 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
210 width - ret,
211 sym->name);
212 }
213 } else {
214 size_t len = BITS_PER_LONG / 4;
215 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
216 len, ip);
217 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
218 width - ret, "");
219 }
220
221 return ret;
222 }
223
hist_entry__sym_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)224 static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
225 size_t size, unsigned int width)
226 {
227 return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip,
228 self->level, bf, size, width);
229 }
230
231 struct sort_entry sort_sym = {
232 .se_header = "Symbol",
233 .se_cmp = sort__sym_cmp,
234 .se_snprintf = hist_entry__sym_snprintf,
235 .se_width_idx = HISTC_SYMBOL,
236 };
237
238 /* --sort srcline */
239
240 static int64_t
sort__srcline_cmp(struct hist_entry * left,struct hist_entry * right)241 sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
242 {
243 return (int64_t)(right->ip - left->ip);
244 }
245
hist_entry__srcline_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width __maybe_unused)246 static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf,
247 size_t size,
248 unsigned int width __maybe_unused)
249 {
250 FILE *fp = NULL;
251 char cmd[PATH_MAX + 2], *path = self->srcline, *nl;
252 size_t line_len;
253
254 if (path != NULL)
255 goto out_path;
256
257 if (!self->ms.map)
258 goto out_ip;
259
260 if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10))
261 goto out_ip;
262
263 snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64,
264 self->ms.map->dso->long_name, self->ip);
265 fp = popen(cmd, "r");
266 if (!fp)
267 goto out_ip;
268
269 if (getline(&path, &line_len, fp) < 0 || !line_len)
270 goto out_ip;
271 self->srcline = strdup(path);
272 if (self->srcline == NULL)
273 goto out_ip;
274
275 nl = strchr(self->srcline, '\n');
276 if (nl != NULL)
277 *nl = '\0';
278 path = self->srcline;
279 out_path:
280 if (fp)
281 pclose(fp);
282 return repsep_snprintf(bf, size, "%s", path);
283 out_ip:
284 if (fp)
285 pclose(fp);
286 return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip);
287 }
288
289 struct sort_entry sort_srcline = {
290 .se_header = "Source:Line",
291 .se_cmp = sort__srcline_cmp,
292 .se_snprintf = hist_entry__srcline_snprintf,
293 .se_width_idx = HISTC_SRCLINE,
294 };
295
296 /* --sort parent */
297
298 static int64_t
sort__parent_cmp(struct hist_entry * left,struct hist_entry * right)299 sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
300 {
301 struct symbol *sym_l = left->parent;
302 struct symbol *sym_r = right->parent;
303
304 if (!sym_l || !sym_r)
305 return cmp_null(sym_l, sym_r);
306
307 return strcmp(sym_l->name, sym_r->name);
308 }
309
hist_entry__parent_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)310 static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
311 size_t size, unsigned int width)
312 {
313 return repsep_snprintf(bf, size, "%-*s", width,
314 self->parent ? self->parent->name : "[other]");
315 }
316
317 struct sort_entry sort_parent = {
318 .se_header = "Parent symbol",
319 .se_cmp = sort__parent_cmp,
320 .se_snprintf = hist_entry__parent_snprintf,
321 .se_width_idx = HISTC_PARENT,
322 };
323
324 /* --sort cpu */
325
326 static int64_t
sort__cpu_cmp(struct hist_entry * left,struct hist_entry * right)327 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
328 {
329 return right->cpu - left->cpu;
330 }
331
hist_entry__cpu_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)332 static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
333 size_t size, unsigned int width)
334 {
335 return repsep_snprintf(bf, size, "%*d", width, self->cpu);
336 }
337
338 struct sort_entry sort_cpu = {
339 .se_header = "CPU",
340 .se_cmp = sort__cpu_cmp,
341 .se_snprintf = hist_entry__cpu_snprintf,
342 .se_width_idx = HISTC_CPU,
343 };
344
345 /* sort keys for branch stacks */
346
347 static int64_t
sort__dso_from_cmp(struct hist_entry * left,struct hist_entry * right)348 sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
349 {
350 return _sort__dso_cmp(left->branch_info->from.map,
351 right->branch_info->from.map);
352 }
353
hist_entry__dso_from_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)354 static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf,
355 size_t size, unsigned int width)
356 {
357 return _hist_entry__dso_snprintf(self->branch_info->from.map,
358 bf, size, width);
359 }
360
361 static int64_t
sort__dso_to_cmp(struct hist_entry * left,struct hist_entry * right)362 sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
363 {
364 return _sort__dso_cmp(left->branch_info->to.map,
365 right->branch_info->to.map);
366 }
367
hist_entry__dso_to_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)368 static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf,
369 size_t size, unsigned int width)
370 {
371 return _hist_entry__dso_snprintf(self->branch_info->to.map,
372 bf, size, width);
373 }
374
375 static int64_t
sort__sym_from_cmp(struct hist_entry * left,struct hist_entry * right)376 sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
377 {
378 struct addr_map_symbol *from_l = &left->branch_info->from;
379 struct addr_map_symbol *from_r = &right->branch_info->from;
380
381 if (!from_l->sym && !from_r->sym)
382 return right->level - left->level;
383
384 return _sort__sym_cmp(from_l->sym, from_r->sym);
385 }
386
387 static int64_t
sort__sym_to_cmp(struct hist_entry * left,struct hist_entry * right)388 sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
389 {
390 struct addr_map_symbol *to_l = &left->branch_info->to;
391 struct addr_map_symbol *to_r = &right->branch_info->to;
392
393 if (!to_l->sym && !to_r->sym)
394 return right->level - left->level;
395
396 return _sort__sym_cmp(to_l->sym, to_r->sym);
397 }
398
hist_entry__sym_from_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)399 static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
400 size_t size, unsigned int width)
401 {
402 struct addr_map_symbol *from = &self->branch_info->from;
403 return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
404 self->level, bf, size, width);
405
406 }
407
hist_entry__sym_to_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)408 static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
409 size_t size, unsigned int width)
410 {
411 struct addr_map_symbol *to = &self->branch_info->to;
412 return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
413 self->level, bf, size, width);
414
415 }
416
417 struct sort_entry sort_dso_from = {
418 .se_header = "Source Shared Object",
419 .se_cmp = sort__dso_from_cmp,
420 .se_snprintf = hist_entry__dso_from_snprintf,
421 .se_width_idx = HISTC_DSO_FROM,
422 };
423
424 struct sort_entry sort_dso_to = {
425 .se_header = "Target Shared Object",
426 .se_cmp = sort__dso_to_cmp,
427 .se_snprintf = hist_entry__dso_to_snprintf,
428 .se_width_idx = HISTC_DSO_TO,
429 };
430
431 struct sort_entry sort_sym_from = {
432 .se_header = "Source Symbol",
433 .se_cmp = sort__sym_from_cmp,
434 .se_snprintf = hist_entry__sym_from_snprintf,
435 .se_width_idx = HISTC_SYMBOL_FROM,
436 };
437
438 struct sort_entry sort_sym_to = {
439 .se_header = "Target Symbol",
440 .se_cmp = sort__sym_to_cmp,
441 .se_snprintf = hist_entry__sym_to_snprintf,
442 .se_width_idx = HISTC_SYMBOL_TO,
443 };
444
445 static int64_t
sort__mispredict_cmp(struct hist_entry * left,struct hist_entry * right)446 sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
447 {
448 const unsigned char mp = left->branch_info->flags.mispred !=
449 right->branch_info->flags.mispred;
450 const unsigned char p = left->branch_info->flags.predicted !=
451 right->branch_info->flags.predicted;
452
453 return mp || p;
454 }
455
hist_entry__mispredict_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)456 static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf,
457 size_t size, unsigned int width){
458 static const char *out = "N/A";
459
460 if (self->branch_info->flags.predicted)
461 out = "N";
462 else if (self->branch_info->flags.mispred)
463 out = "Y";
464
465 return repsep_snprintf(bf, size, "%-*s", width, out);
466 }
467
468 /* --sort daddr_sym */
469 static int64_t
sort__daddr_cmp(struct hist_entry * left,struct hist_entry * right)470 sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
471 {
472 uint64_t l = 0, r = 0;
473
474 if (left->mem_info)
475 l = left->mem_info->daddr.addr;
476 if (right->mem_info)
477 r = right->mem_info->daddr.addr;
478
479 return (int64_t)(r - l);
480 }
481
hist_entry__daddr_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)482 static int hist_entry__daddr_snprintf(struct hist_entry *self, char *bf,
483 size_t size, unsigned int width)
484 {
485 uint64_t addr = 0;
486 struct map *map = NULL;
487 struct symbol *sym = NULL;
488
489 if (self->mem_info) {
490 addr = self->mem_info->daddr.addr;
491 map = self->mem_info->daddr.map;
492 sym = self->mem_info->daddr.sym;
493 }
494 return _hist_entry__sym_snprintf(map, sym, addr, self->level, bf, size,
495 width);
496 }
497
498 static int64_t
sort__dso_daddr_cmp(struct hist_entry * left,struct hist_entry * right)499 sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
500 {
501 struct map *map_l = NULL;
502 struct map *map_r = NULL;
503
504 if (left->mem_info)
505 map_l = left->mem_info->daddr.map;
506 if (right->mem_info)
507 map_r = right->mem_info->daddr.map;
508
509 return _sort__dso_cmp(map_l, map_r);
510 }
511
hist_entry__dso_daddr_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)512 static int hist_entry__dso_daddr_snprintf(struct hist_entry *self, char *bf,
513 size_t size, unsigned int width)
514 {
515 struct map *map = NULL;
516
517 if (self->mem_info)
518 map = self->mem_info->daddr.map;
519
520 return _hist_entry__dso_snprintf(map, bf, size, width);
521 }
522
523 static int64_t
sort__locked_cmp(struct hist_entry * left,struct hist_entry * right)524 sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
525 {
526 union perf_mem_data_src data_src_l;
527 union perf_mem_data_src data_src_r;
528
529 if (left->mem_info)
530 data_src_l = left->mem_info->data_src;
531 else
532 data_src_l.mem_lock = PERF_MEM_LOCK_NA;
533
534 if (right->mem_info)
535 data_src_r = right->mem_info->data_src;
536 else
537 data_src_r.mem_lock = PERF_MEM_LOCK_NA;
538
539 return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
540 }
541
hist_entry__locked_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)542 static int hist_entry__locked_snprintf(struct hist_entry *self, char *bf,
543 size_t size, unsigned int width)
544 {
545 const char *out;
546 u64 mask = PERF_MEM_LOCK_NA;
547
548 if (self->mem_info)
549 mask = self->mem_info->data_src.mem_lock;
550
551 if (mask & PERF_MEM_LOCK_NA)
552 out = "N/A";
553 else if (mask & PERF_MEM_LOCK_LOCKED)
554 out = "Yes";
555 else
556 out = "No";
557
558 return repsep_snprintf(bf, size, "%-*s", width, out);
559 }
560
561 static int64_t
sort__tlb_cmp(struct hist_entry * left,struct hist_entry * right)562 sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
563 {
564 union perf_mem_data_src data_src_l;
565 union perf_mem_data_src data_src_r;
566
567 if (left->mem_info)
568 data_src_l = left->mem_info->data_src;
569 else
570 data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
571
572 if (right->mem_info)
573 data_src_r = right->mem_info->data_src;
574 else
575 data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
576
577 return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
578 }
579
580 static const char * const tlb_access[] = {
581 "N/A",
582 "HIT",
583 "MISS",
584 "L1",
585 "L2",
586 "Walker",
587 "Fault",
588 };
589 #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
590
hist_entry__tlb_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)591 static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf,
592 size_t size, unsigned int width)
593 {
594 char out[64];
595 size_t sz = sizeof(out) - 1; /* -1 for null termination */
596 size_t l = 0, i;
597 u64 m = PERF_MEM_TLB_NA;
598 u64 hit, miss;
599
600 out[0] = '\0';
601
602 if (self->mem_info)
603 m = self->mem_info->data_src.mem_dtlb;
604
605 hit = m & PERF_MEM_TLB_HIT;
606 miss = m & PERF_MEM_TLB_MISS;
607
608 /* already taken care of */
609 m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
610
611 for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) {
612 if (!(m & 0x1))
613 continue;
614 if (l) {
615 strcat(out, " or ");
616 l += 4;
617 }
618 strncat(out, tlb_access[i], sz - l);
619 l += strlen(tlb_access[i]);
620 }
621 if (*out == '\0')
622 strcpy(out, "N/A");
623 if (hit)
624 strncat(out, " hit", sz - l);
625 if (miss)
626 strncat(out, " miss", sz - l);
627
628 return repsep_snprintf(bf, size, "%-*s", width, out);
629 }
630
631 static int64_t
sort__lvl_cmp(struct hist_entry * left,struct hist_entry * right)632 sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
633 {
634 union perf_mem_data_src data_src_l;
635 union perf_mem_data_src data_src_r;
636
637 if (left->mem_info)
638 data_src_l = left->mem_info->data_src;
639 else
640 data_src_l.mem_lvl = PERF_MEM_LVL_NA;
641
642 if (right->mem_info)
643 data_src_r = right->mem_info->data_src;
644 else
645 data_src_r.mem_lvl = PERF_MEM_LVL_NA;
646
647 return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
648 }
649
650 static const char * const mem_lvl[] = {
651 "N/A",
652 "HIT",
653 "MISS",
654 "L1",
655 "LFB",
656 "L2",
657 "L3",
658 "Local RAM",
659 "Remote RAM (1 hop)",
660 "Remote RAM (2 hops)",
661 "Remote Cache (1 hop)",
662 "Remote Cache (2 hops)",
663 "I/O",
664 "Uncached",
665 };
666 #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
667
hist_entry__lvl_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)668 static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf,
669 size_t size, unsigned int width)
670 {
671 char out[64];
672 size_t sz = sizeof(out) - 1; /* -1 for null termination */
673 size_t i, l = 0;
674 u64 m = PERF_MEM_LVL_NA;
675 u64 hit, miss;
676
677 if (self->mem_info)
678 m = self->mem_info->data_src.mem_lvl;
679
680 out[0] = '\0';
681
682 hit = m & PERF_MEM_LVL_HIT;
683 miss = m & PERF_MEM_LVL_MISS;
684
685 /* already taken care of */
686 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
687
688 for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) {
689 if (!(m & 0x1))
690 continue;
691 if (l) {
692 strcat(out, " or ");
693 l += 4;
694 }
695 strncat(out, mem_lvl[i], sz - l);
696 l += strlen(mem_lvl[i]);
697 }
698 if (*out == '\0')
699 strcpy(out, "N/A");
700 if (hit)
701 strncat(out, " hit", sz - l);
702 if (miss)
703 strncat(out, " miss", sz - l);
704
705 return repsep_snprintf(bf, size, "%-*s", width, out);
706 }
707
708 static int64_t
sort__snoop_cmp(struct hist_entry * left,struct hist_entry * right)709 sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
710 {
711 union perf_mem_data_src data_src_l;
712 union perf_mem_data_src data_src_r;
713
714 if (left->mem_info)
715 data_src_l = left->mem_info->data_src;
716 else
717 data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
718
719 if (right->mem_info)
720 data_src_r = right->mem_info->data_src;
721 else
722 data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
723
724 return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
725 }
726
727 static const char * const snoop_access[] = {
728 "N/A",
729 "None",
730 "Miss",
731 "Hit",
732 "HitM",
733 };
734 #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
735
hist_entry__snoop_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)736 static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf,
737 size_t size, unsigned int width)
738 {
739 char out[64];
740 size_t sz = sizeof(out) - 1; /* -1 for null termination */
741 size_t i, l = 0;
742 u64 m = PERF_MEM_SNOOP_NA;
743
744 out[0] = '\0';
745
746 if (self->mem_info)
747 m = self->mem_info->data_src.mem_snoop;
748
749 for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {
750 if (!(m & 0x1))
751 continue;
752 if (l) {
753 strcat(out, " or ");
754 l += 4;
755 }
756 strncat(out, snoop_access[i], sz - l);
757 l += strlen(snoop_access[i]);
758 }
759
760 if (*out == '\0')
761 strcpy(out, "N/A");
762
763 return repsep_snprintf(bf, size, "%-*s", width, out);
764 }
765
766 struct sort_entry sort_mispredict = {
767 .se_header = "Branch Mispredicted",
768 .se_cmp = sort__mispredict_cmp,
769 .se_snprintf = hist_entry__mispredict_snprintf,
770 .se_width_idx = HISTC_MISPREDICT,
771 };
772
he_weight(struct hist_entry * he)773 static u64 he_weight(struct hist_entry *he)
774 {
775 return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
776 }
777
778 static int64_t
sort__local_weight_cmp(struct hist_entry * left,struct hist_entry * right)779 sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
780 {
781 return he_weight(left) - he_weight(right);
782 }
783
hist_entry__local_weight_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)784 static int hist_entry__local_weight_snprintf(struct hist_entry *self, char *bf,
785 size_t size, unsigned int width)
786 {
787 return repsep_snprintf(bf, size, "%-*llu", width, he_weight(self));
788 }
789
790 struct sort_entry sort_local_weight = {
791 .se_header = "Local Weight",
792 .se_cmp = sort__local_weight_cmp,
793 .se_snprintf = hist_entry__local_weight_snprintf,
794 .se_width_idx = HISTC_LOCAL_WEIGHT,
795 };
796
797 static int64_t
sort__global_weight_cmp(struct hist_entry * left,struct hist_entry * right)798 sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
799 {
800 return left->stat.weight - right->stat.weight;
801 }
802
hist_entry__global_weight_snprintf(struct hist_entry * self,char * bf,size_t size,unsigned int width)803 static int hist_entry__global_weight_snprintf(struct hist_entry *self, char *bf,
804 size_t size, unsigned int width)
805 {
806 return repsep_snprintf(bf, size, "%-*llu", width, self->stat.weight);
807 }
808
809 struct sort_entry sort_global_weight = {
810 .se_header = "Weight",
811 .se_cmp = sort__global_weight_cmp,
812 .se_snprintf = hist_entry__global_weight_snprintf,
813 .se_width_idx = HISTC_GLOBAL_WEIGHT,
814 };
815
816 struct sort_entry sort_mem_daddr_sym = {
817 .se_header = "Data Symbol",
818 .se_cmp = sort__daddr_cmp,
819 .se_snprintf = hist_entry__daddr_snprintf,
820 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
821 };
822
823 struct sort_entry sort_mem_daddr_dso = {
824 .se_header = "Data Object",
825 .se_cmp = sort__dso_daddr_cmp,
826 .se_snprintf = hist_entry__dso_daddr_snprintf,
827 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
828 };
829
830 struct sort_entry sort_mem_locked = {
831 .se_header = "Locked",
832 .se_cmp = sort__locked_cmp,
833 .se_snprintf = hist_entry__locked_snprintf,
834 .se_width_idx = HISTC_MEM_LOCKED,
835 };
836
837 struct sort_entry sort_mem_tlb = {
838 .se_header = "TLB access",
839 .se_cmp = sort__tlb_cmp,
840 .se_snprintf = hist_entry__tlb_snprintf,
841 .se_width_idx = HISTC_MEM_TLB,
842 };
843
844 struct sort_entry sort_mem_lvl = {
845 .se_header = "Memory access",
846 .se_cmp = sort__lvl_cmp,
847 .se_snprintf = hist_entry__lvl_snprintf,
848 .se_width_idx = HISTC_MEM_LVL,
849 };
850
851 struct sort_entry sort_mem_snoop = {
852 .se_header = "Snoop",
853 .se_cmp = sort__snoop_cmp,
854 .se_snprintf = hist_entry__snoop_snprintf,
855 .se_width_idx = HISTC_MEM_SNOOP,
856 };
857
858 struct sort_dimension {
859 const char *name;
860 struct sort_entry *entry;
861 int taken;
862 };
863
864 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
865
866 static struct sort_dimension common_sort_dimensions[] = {
867 DIM(SORT_PID, "pid", sort_thread),
868 DIM(SORT_COMM, "comm", sort_comm),
869 DIM(SORT_DSO, "dso", sort_dso),
870 DIM(SORT_SYM, "symbol", sort_sym),
871 DIM(SORT_PARENT, "parent", sort_parent),
872 DIM(SORT_CPU, "cpu", sort_cpu),
873 DIM(SORT_SRCLINE, "srcline", sort_srcline),
874 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
875 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
876 DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
877 DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
878 DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
879 DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
880 DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
881 DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
882 };
883
884 #undef DIM
885
886 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
887
888 static struct sort_dimension bstack_sort_dimensions[] = {
889 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
890 DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
891 DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
892 DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
893 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
894 };
895
896 #undef DIM
897
sort_dimension__add(const char * tok)898 int sort_dimension__add(const char *tok)
899 {
900 unsigned int i;
901
902 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
903 struct sort_dimension *sd = &common_sort_dimensions[i];
904
905 if (strncasecmp(tok, sd->name, strlen(tok)))
906 continue;
907
908 if (sd->entry == &sort_parent) {
909 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
910 if (ret) {
911 char err[BUFSIZ];
912
913 regerror(ret, &parent_regex, err, sizeof(err));
914 pr_err("Invalid regex: %s\n%s", parent_pattern, err);
915 return -EINVAL;
916 }
917 sort__has_parent = 1;
918 } else if (sd->entry == &sort_sym ||
919 sd->entry == &sort_sym_from ||
920 sd->entry == &sort_sym_to ||
921 sd->entry == &sort_mem_daddr_sym) {
922 sort__has_sym = 1;
923 }
924
925 if (sd->taken)
926 return 0;
927
928 if (sd->entry->se_collapse)
929 sort__need_collapse = 1;
930
931 if (list_empty(&hist_entry__sort_list))
932 sort__first_dimension = i;
933
934 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
935 sd->taken = 1;
936
937 return 0;
938 }
939
940 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
941 struct sort_dimension *sd = &bstack_sort_dimensions[i];
942
943 if (strncasecmp(tok, sd->name, strlen(tok)))
944 continue;
945
946 if (sort__branch_mode != 1)
947 return -EINVAL;
948
949 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
950 sort__has_sym = 1;
951
952 if (sd->taken)
953 return 0;
954
955 if (sd->entry->se_collapse)
956 sort__need_collapse = 1;
957
958 if (list_empty(&hist_entry__sort_list))
959 sort__first_dimension = i + __SORT_BRANCH_STACK;
960
961 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
962 sd->taken = 1;
963
964 return 0;
965 }
966
967 return -ESRCH;
968 }
969
setup_sorting(void)970 int setup_sorting(void)
971 {
972 char *tmp, *tok, *str = strdup(sort_order);
973 int ret = 0;
974
975 if (str == NULL) {
976 error("Not enough memory to setup sort keys");
977 return -ENOMEM;
978 }
979
980 for (tok = strtok_r(str, ", ", &tmp);
981 tok; tok = strtok_r(NULL, ", ", &tmp)) {
982 ret = sort_dimension__add(tok);
983 if (ret == -EINVAL) {
984 error("Invalid --sort key: `%s'", tok);
985 break;
986 } else if (ret == -ESRCH) {
987 error("Unknown --sort key: `%s'", tok);
988 break;
989 }
990 }
991
992 free(str);
993 return ret;
994 }
995
sort_entry__setup_elide(struct sort_entry * self,struct strlist * list,const char * list_name,FILE * fp)996 void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
997 const char *list_name, FILE *fp)
998 {
999 if (list && strlist__nr_entries(list) == 1) {
1000 if (fp != NULL)
1001 fprintf(fp, "# %s: %s\n", list_name,
1002 strlist__entry(list, 0)->s);
1003 self->elide = true;
1004 }
1005 }
1006