]>
Commit | Line | Data |
---|---|---|
dd68ada2 | 1 | #include "sort.h" |
8a6c5b26 | 2 | #include "hist.h" |
dd68ada2 JK |
3 | |
4 | regex_t parent_regex; | |
edb7c60e ACM |
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; | |
af0a6fa4 FW |
9 | int sort__need_collapse = 0; |
10 | int sort__has_parent = 0; | |
1af55640 | 11 | int sort__has_sym = 0; |
993ac88d | 12 | int sort__branch_mode = -1; /* -1 = means not set */ |
a4fb581b FW |
13 | |
14 | enum sort_type sort__first_dimension; | |
dd68ada2 | 15 | |
dd68ada2 JK |
16 | LIST_HEAD(hist_entry__sort_list); |
17 | ||
a4e3b956 | 18 | static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) |
dd68ada2 JK |
19 | { |
20 | int n; | |
21 | va_list ap; | |
22 | ||
23 | va_start(ap, fmt); | |
a4e3b956 | 24 | n = vsnprintf(bf, size, fmt, ap); |
0ca0c130 | 25 | if (symbol_conf.field_sep && n > 0) { |
a4e3b956 ACM |
26 | char *sep = bf; |
27 | ||
28 | while (1) { | |
0ca0c130 | 29 | sep = strchr(sep, *symbol_conf.field_sep); |
a4e3b956 ACM |
30 | if (sep == NULL) |
31 | break; | |
32 | *sep = '.'; | |
dd68ada2 | 33 | } |
dd68ada2 JK |
34 | } |
35 | va_end(ap); | |
b832796c AB |
36 | |
37 | if (n >= (int)size) | |
38 | return size - 1; | |
dd68ada2 JK |
39 | return n; |
40 | } | |
41 | ||
872a878f FW |
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 | |
55 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | |
56 | { | |
57 | return right->thread->pid - left->thread->pid; | |
58 | } | |
59 | ||
a4e3b956 ACM |
60 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, |
61 | size_t size, unsigned int width) | |
dd68ada2 | 62 | { |
fb29a338 | 63 | return repsep_snprintf(bf, size, "%*s:%5d", width - 6, |
dd68ada2 JK |
64 | self->thread->comm ?: "", self->thread->pid); |
65 | } | |
66 | ||
872a878f FW |
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 | |
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 | |
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 | ||
a4e3b956 ACM |
94 | static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, |
95 | size_t size, unsigned int width) | |
dd68ada2 | 96 | { |
a4e3b956 | 97 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); |
dd68ada2 JK |
98 | } |
99 | ||
14d1ac74 NK |
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 | ||
b5387528 RAV |
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 | ||
872a878f | 130 | static int64_t |
dd68ada2 JK |
131 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) |
132 | { | |
b5387528 RAV |
133 | return _sort__dso_cmp(left->ms.map, right->ms.map); |
134 | } | |
dd68ada2 | 135 | |
14d1ac74 NK |
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 | ||
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 */ | |
dd68ada2 | 162 | |
51f27d14 | 163 | static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r) |
b5387528 | 164 | { |
51f27d14 NK |
165 | u64 ip_l, ip_r; |
166 | ||
b5387528 RAV |
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 | ||
53985a7b SL |
173 | ip_l = sym_l->start; |
174 | ip_r = sym_r->start; | |
b5387528 RAV |
175 | |
176 | return (int64_t)(ip_r - ip_l); | |
177 | } | |
178 | ||
14d1ac74 NK |
179 | static int64_t |
180 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | |
b5387528 | 181 | { |
14d1ac74 NK |
182 | if (!left->ms.sym && !right->ms.sym) |
183 | return right->level - left->level; | |
dd68ada2 | 184 | |
51f27d14 | 185 | return _sort__sym_cmp(left->ms.sym, right->ms.sym); |
b5387528 RAV |
186 | } |
187 | ||
188 | static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, | |
189 | u64 ip, char level, char *bf, size_t size, | |
43355522 | 190 | unsigned int width) |
b5387528 RAV |
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); | |
439d473b | 198 | } |
dd68ada2 | 199 | |
b5387528 | 200 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); |
98a3b32c SE |
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 - 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 { | |
b5387528 RAV |
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; | |
dd68ada2 JK |
222 | } |
223 | ||
b5387528 | 224 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, |
43355522 | 225 | size_t size, unsigned int width) |
b5387528 RAV |
226 | { |
227 | return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, | |
228 | self->level, bf, size, width); | |
229 | } | |
dd68ada2 | 230 | |
872a878f FW |
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 | }; | |
dd68ada2 | 237 | |
409a8be6 ACM |
238 | /* --sort srcline */ |
239 | ||
240 | static int64_t | |
241 | sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) | |
242 | { | |
243 | return (int64_t)(right->ip - left->ip); | |
244 | } | |
245 | ||
246 | static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, | |
1d037ca1 IT |
247 | size_t size, |
248 | unsigned int width __maybe_unused) | |
409a8be6 | 249 | { |
8eb44dd7 | 250 | FILE *fp = NULL; |
409a8be6 ACM |
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 | ||
ffe10c6f NK |
257 | if (!self->ms.map) |
258 | goto out_ip; | |
259 | ||
88481b6b NK |
260 | if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10)) |
261 | goto out_ip; | |
262 | ||
409a8be6 ACM |
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; | |
409a8be6 ACM |
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: | |
8eb44dd7 TJ |
280 | if (fp) |
281 | pclose(fp); | |
409a8be6 ACM |
282 | return repsep_snprintf(bf, size, "%s", path); |
283 | out_ip: | |
8eb44dd7 TJ |
284 | if (fp) |
285 | pclose(fp); | |
409a8be6 ACM |
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 | ||
dd68ada2 JK |
296 | /* --sort parent */ |
297 | ||
872a878f | 298 | static int64_t |
dd68ada2 JK |
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 | ||
a4e3b956 ACM |
310 | static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, |
311 | size_t size, unsigned int width) | |
dd68ada2 | 312 | { |
a4e3b956 | 313 | return repsep_snprintf(bf, size, "%-*s", width, |
dd68ada2 JK |
314 | self->parent ? self->parent->name : "[other]"); |
315 | } | |
316 | ||
872a878f FW |
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 | ||
f60f3593 AS |
324 | /* --sort cpu */ |
325 | ||
872a878f | 326 | static int64_t |
f60f3593 AS |
327 | sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) |
328 | { | |
329 | return right->cpu - left->cpu; | |
330 | } | |
331 | ||
332 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | |
333 | size_t size, unsigned int width) | |
334 | { | |
dccf1805 | 335 | return repsep_snprintf(bf, size, "%*d", width, self->cpu); |
f60f3593 AS |
336 | } |
337 | ||
872a878f FW |
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 | ||
14d1ac74 NK |
345 | /* sort keys for branch stacks */ |
346 | ||
b5387528 RAV |
347 | static int64_t |
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 | ||
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 | ||
b5387528 RAV |
361 | static int64_t |
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 | ||
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 | |
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 | ||
51f27d14 | 384 | return _sort__sym_cmp(from_l->sym, from_r->sym); |
b5387528 RAV |
385 | } |
386 | ||
387 | static int64_t | |
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 | ||
51f27d14 | 396 | return _sort__sym_cmp(to_l->sym, to_r->sym); |
b5387528 RAV |
397 | } |
398 | ||
399 | static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, | |
43355522 | 400 | size_t size, unsigned int width) |
b5387528 RAV |
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 | ||
408 | static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, | |
43355522 | 409 | size_t size, unsigned int width) |
b5387528 RAV |
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 | ||
14d1ac74 NK |
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 | ||
b5387528 RAV |
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 | |
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 | ||
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 | ||
98a3b32c SE |
468 | /* --sort daddr_sym */ |
469 | static int64_t | |
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 | ||
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 | |
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 | ||
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 | |
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 | ||
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 | |
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 | ||
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 | |
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 | ||
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 | |
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 | ||
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 | ||
b5387528 RAV |
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 | ||
05484298 AK |
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 | |
779 | sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right) | |
780 | { | |
781 | return he_weight(left) - he_weight(right); | |
782 | } | |
783 | ||
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 | |
798 | sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right) | |
799 | { | |
800 | return left->stat.weight - right->stat.weight; | |
801 | } | |
802 | ||
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 | ||
98a3b32c SE |
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 | ||
872a878f FW |
858 | struct sort_dimension { |
859 | const char *name; | |
860 | struct sort_entry *entry; | |
861 | int taken; | |
862 | }; | |
863 | ||
b5387528 RAV |
864 | #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) } |
865 | ||
fc5871ed | 866 | static struct sort_dimension common_sort_dimensions[] = { |
b5387528 RAV |
867 | DIM(SORT_PID, "pid", sort_thread), |
868 | DIM(SORT_COMM, "comm", sort_comm), | |
869 | DIM(SORT_DSO, "dso", sort_dso), | |
b5387528 | 870 | DIM(SORT_SYM, "symbol", sort_sym), |
b5387528 RAV |
871 | DIM(SORT_PARENT, "parent", sort_parent), |
872 | DIM(SORT_CPU, "cpu", sort_cpu), | |
409a8be6 | 873 | DIM(SORT_SRCLINE, "srcline", sort_srcline), |
05484298 AK |
874 | DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), |
875 | DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), | |
98a3b32c SE |
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), | |
872a878f FW |
882 | }; |
883 | ||
fc5871ed NK |
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 | ||
dd68ada2 JK |
898 | int sort_dimension__add(const char *tok) |
899 | { | |
900 | unsigned int i; | |
901 | ||
fc5871ed NK |
902 | for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) { |
903 | struct sort_dimension *sd = &common_sort_dimensions[i]; | |
dd68ada2 | 904 | |
dd68ada2 JK |
905 | if (strncasecmp(tok, sd->name, strlen(tok))) |
906 | continue; | |
fc5871ed | 907 | |
dd68ada2 JK |
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)); | |
2aefa4f7 ACM |
914 | pr_err("Invalid regex: %s\n%s", parent_pattern, err); |
915 | return -EINVAL; | |
dd68ada2 JK |
916 | } |
917 | sort__has_parent = 1; | |
98a3b32c SE |
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) { | |
1af55640 | 922 | sort__has_sym = 1; |
dd68ada2 JK |
923 | } |
924 | ||
fd8ea212 FW |
925 | if (sd->taken) |
926 | return 0; | |
927 | ||
928 | if (sd->entry->se_collapse) | |
929 | sort__need_collapse = 1; | |
930 | ||
6f38cf25 NK |
931 | if (list_empty(&hist_entry__sort_list)) |
932 | sort__first_dimension = i; | |
af0a6fa4 | 933 | |
dd68ada2 JK |
934 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); |
935 | sd->taken = 1; | |
936 | ||
937 | return 0; | |
938 | } | |
fc5871ed NK |
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 | ||
dd68ada2 JK |
967 | return -ESRCH; |
968 | } | |
c8829c7a | 969 | |
55309985 | 970 | int setup_sorting(void) |
c8829c7a ACM |
971 | { |
972 | char *tmp, *tok, *str = strdup(sort_order); | |
55309985 | 973 | int ret = 0; |
c8829c7a | 974 | |
5936f54d NK |
975 | if (str == NULL) { |
976 | error("Not enough memory to setup sort keys"); | |
977 | return -ENOMEM; | |
978 | } | |
979 | ||
c8829c7a ACM |
980 | for (tok = strtok_r(str, ", ", &tmp); |
981 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | |
55309985 | 982 | ret = sort_dimension__add(tok); |
fc5871ed NK |
983 | if (ret == -EINVAL) { |
984 | error("Invalid --sort key: `%s'", tok); | |
55309985 | 985 | break; |
fc5871ed | 986 | } else if (ret == -ESRCH) { |
c8829c7a | 987 | error("Unknown --sort key: `%s'", tok); |
55309985 | 988 | break; |
c8829c7a ACM |
989 | } |
990 | } | |
991 | ||
992 | free(str); | |
55309985 | 993 | return ret; |
c8829c7a | 994 | } |
c351c281 ACM |
995 | |
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 | } |