Commit | Line | Data |
---|---|---|
8a0ecfb8 | 1 | #include "util.h" |
3d1d07ec | 2 | #include "hist.h" |
4e4f06e4 ACM |
3 | #include "session.h" |
4 | #include "sort.h" | |
9b33827d | 5 | #include <math.h> |
3d1d07ec JK |
6 | |
7 | struct callchain_param callchain_param = { | |
8 | .mode = CHAIN_GRAPH_REL, | |
9 | .min_percent = 0.5 | |
10 | }; | |
11 | ||
1c02c4d2 ACM |
12 | static void hist_entry__add_cpumode_count(struct hist_entry *self, |
13 | unsigned int cpumode, u64 count) | |
a1645ce1 | 14 | { |
28e2a106 | 15 | switch (cpumode) { |
a1645ce1 | 16 | case PERF_RECORD_MISC_KERNEL: |
1c02c4d2 | 17 | self->count_sys += count; |
a1645ce1 ZY |
18 | break; |
19 | case PERF_RECORD_MISC_USER: | |
1c02c4d2 | 20 | self->count_us += count; |
a1645ce1 ZY |
21 | break; |
22 | case PERF_RECORD_MISC_GUEST_KERNEL: | |
1c02c4d2 | 23 | self->count_guest_sys += count; |
a1645ce1 ZY |
24 | break; |
25 | case PERF_RECORD_MISC_GUEST_USER: | |
1c02c4d2 | 26 | self->count_guest_us += count; |
a1645ce1 ZY |
27 | break; |
28 | default: | |
29 | break; | |
30 | } | |
31 | } | |
32 | ||
3d1d07ec JK |
33 | /* |
34 | * histogram, sorted on item, collects counts | |
35 | */ | |
36 | ||
28e2a106 ACM |
37 | static struct hist_entry *hist_entry__new(struct hist_entry *template) |
38 | { | |
39 | size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_node) : 0; | |
40 | struct hist_entry *self = malloc(sizeof(*self) + callchain_size); | |
41 | ||
42 | if (self != NULL) { | |
43 | *self = *template; | |
44 | if (symbol_conf.use_callchain) | |
45 | callchain_init(self->callchain); | |
46 | } | |
47 | ||
48 | return self; | |
49 | } | |
50 | ||
fefb0b94 ACM |
51 | static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) |
52 | { | |
53 | if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen) | |
54 | self->max_sym_namelen = entry->ms.sym->namelen; | |
55 | ++self->nr_entries; | |
56 | } | |
57 | ||
1c02c4d2 ACM |
58 | struct hist_entry *__hists__add_entry(struct hists *self, |
59 | struct addr_location *al, | |
60 | struct symbol *sym_parent, u64 count) | |
9735abf1 | 61 | { |
1c02c4d2 | 62 | struct rb_node **p = &self->entries.rb_node; |
9735abf1 ACM |
63 | struct rb_node *parent = NULL; |
64 | struct hist_entry *he; | |
65 | struct hist_entry entry = { | |
1ed091c4 | 66 | .thread = al->thread, |
59fd5306 ACM |
67 | .ms = { |
68 | .map = al->map, | |
69 | .sym = al->sym, | |
70 | }, | |
1ed091c4 ACM |
71 | .ip = al->addr, |
72 | .level = al->level, | |
9735abf1 ACM |
73 | .count = count, |
74 | .parent = sym_parent, | |
75 | }; | |
76 | int cmp; | |
77 | ||
78 | while (*p != NULL) { | |
79 | parent = *p; | |
80 | he = rb_entry(parent, struct hist_entry, rb_node); | |
81 | ||
82 | cmp = hist_entry__cmp(&entry, he); | |
83 | ||
84 | if (!cmp) { | |
28e2a106 ACM |
85 | he->count += count; |
86 | goto out; | |
9735abf1 ACM |
87 | } |
88 | ||
89 | if (cmp < 0) | |
90 | p = &(*p)->rb_left; | |
91 | else | |
92 | p = &(*p)->rb_right; | |
93 | } | |
94 | ||
28e2a106 | 95 | he = hist_entry__new(&entry); |
9735abf1 ACM |
96 | if (!he) |
97 | return NULL; | |
9735abf1 | 98 | rb_link_node(&he->rb_node, parent, p); |
1c02c4d2 | 99 | rb_insert_color(&he->rb_node, &self->entries); |
fefb0b94 | 100 | hists__inc_nr_entries(self, he); |
28e2a106 | 101 | out: |
1c02c4d2 | 102 | hist_entry__add_cpumode_count(he, al->cpumode, count); |
9735abf1 ACM |
103 | return he; |
104 | } | |
105 | ||
3d1d07ec JK |
106 | int64_t |
107 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | |
108 | { | |
109 | struct sort_entry *se; | |
110 | int64_t cmp = 0; | |
111 | ||
112 | list_for_each_entry(se, &hist_entry__sort_list, list) { | |
fcd14984 | 113 | cmp = se->se_cmp(left, right); |
3d1d07ec JK |
114 | if (cmp) |
115 | break; | |
116 | } | |
117 | ||
118 | return cmp; | |
119 | } | |
120 | ||
121 | int64_t | |
122 | hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |
123 | { | |
124 | struct sort_entry *se; | |
125 | int64_t cmp = 0; | |
126 | ||
127 | list_for_each_entry(se, &hist_entry__sort_list, list) { | |
128 | int64_t (*f)(struct hist_entry *, struct hist_entry *); | |
129 | ||
fcd14984 | 130 | f = se->se_collapse ?: se->se_cmp; |
3d1d07ec JK |
131 | |
132 | cmp = f(left, right); | |
133 | if (cmp) | |
134 | break; | |
135 | } | |
136 | ||
137 | return cmp; | |
138 | } | |
139 | ||
140 | void hist_entry__free(struct hist_entry *he) | |
141 | { | |
142 | free(he); | |
143 | } | |
144 | ||
145 | /* | |
146 | * collapse the histogram | |
147 | */ | |
148 | ||
fefb0b94 | 149 | static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) |
3d1d07ec | 150 | { |
b9bf0892 | 151 | struct rb_node **p = &root->rb_node; |
3d1d07ec JK |
152 | struct rb_node *parent = NULL; |
153 | struct hist_entry *iter; | |
154 | int64_t cmp; | |
155 | ||
156 | while (*p != NULL) { | |
157 | parent = *p; | |
158 | iter = rb_entry(parent, struct hist_entry, rb_node); | |
159 | ||
160 | cmp = hist_entry__collapse(iter, he); | |
161 | ||
162 | if (!cmp) { | |
163 | iter->count += he->count; | |
164 | hist_entry__free(he); | |
fefb0b94 | 165 | return false; |
3d1d07ec JK |
166 | } |
167 | ||
168 | if (cmp < 0) | |
169 | p = &(*p)->rb_left; | |
170 | else | |
171 | p = &(*p)->rb_right; | |
172 | } | |
173 | ||
174 | rb_link_node(&he->rb_node, parent, p); | |
b9bf0892 | 175 | rb_insert_color(&he->rb_node, root); |
fefb0b94 | 176 | return true; |
3d1d07ec JK |
177 | } |
178 | ||
1c02c4d2 | 179 | void hists__collapse_resort(struct hists *self) |
3d1d07ec | 180 | { |
b9bf0892 | 181 | struct rb_root tmp; |
3d1d07ec JK |
182 | struct rb_node *next; |
183 | struct hist_entry *n; | |
184 | ||
185 | if (!sort__need_collapse) | |
186 | return; | |
187 | ||
b9bf0892 | 188 | tmp = RB_ROOT; |
1c02c4d2 | 189 | next = rb_first(&self->entries); |
fefb0b94 ACM |
190 | self->nr_entries = 0; |
191 | self->max_sym_namelen = 0; | |
b9bf0892 | 192 | |
3d1d07ec JK |
193 | while (next) { |
194 | n = rb_entry(next, struct hist_entry, rb_node); | |
195 | next = rb_next(&n->rb_node); | |
196 | ||
1c02c4d2 | 197 | rb_erase(&n->rb_node, &self->entries); |
fefb0b94 ACM |
198 | if (collapse__insert_entry(&tmp, n)) |
199 | hists__inc_nr_entries(self, n); | |
3d1d07ec | 200 | } |
b9bf0892 | 201 | |
1c02c4d2 | 202 | self->entries = tmp; |
3d1d07ec JK |
203 | } |
204 | ||
205 | /* | |
206 | * reverse the map, sort on count. | |
207 | */ | |
208 | ||
1c02c4d2 ACM |
209 | static void __hists__insert_output_entry(struct rb_root *entries, |
210 | struct hist_entry *he, | |
211 | u64 min_callchain_hits) | |
3d1d07ec | 212 | { |
1c02c4d2 | 213 | struct rb_node **p = &entries->rb_node; |
3d1d07ec JK |
214 | struct rb_node *parent = NULL; |
215 | struct hist_entry *iter; | |
216 | ||
d599db3f | 217 | if (symbol_conf.use_callchain) |
b9fb9304 | 218 | callchain_param.sort(&he->sorted_chain, he->callchain, |
3d1d07ec JK |
219 | min_callchain_hits, &callchain_param); |
220 | ||
221 | while (*p != NULL) { | |
222 | parent = *p; | |
223 | iter = rb_entry(parent, struct hist_entry, rb_node); | |
224 | ||
225 | if (he->count > iter->count) | |
226 | p = &(*p)->rb_left; | |
227 | else | |
228 | p = &(*p)->rb_right; | |
229 | } | |
230 | ||
231 | rb_link_node(&he->rb_node, parent, p); | |
1c02c4d2 | 232 | rb_insert_color(&he->rb_node, entries); |
3d1d07ec JK |
233 | } |
234 | ||
fefb0b94 | 235 | void hists__output_resort(struct hists *self) |
3d1d07ec | 236 | { |
b9bf0892 | 237 | struct rb_root tmp; |
3d1d07ec JK |
238 | struct rb_node *next; |
239 | struct hist_entry *n; | |
3d1d07ec JK |
240 | u64 min_callchain_hits; |
241 | ||
1c02c4d2 | 242 | min_callchain_hits = self->stats.total * (callchain_param.min_percent / 100); |
3d1d07ec | 243 | |
b9bf0892 | 244 | tmp = RB_ROOT; |
1c02c4d2 | 245 | next = rb_first(&self->entries); |
3d1d07ec | 246 | |
fefb0b94 ACM |
247 | self->nr_entries = 0; |
248 | self->max_sym_namelen = 0; | |
249 | ||
3d1d07ec JK |
250 | while (next) { |
251 | n = rb_entry(next, struct hist_entry, rb_node); | |
252 | next = rb_next(&n->rb_node); | |
253 | ||
1c02c4d2 ACM |
254 | rb_erase(&n->rb_node, &self->entries); |
255 | __hists__insert_output_entry(&tmp, n, min_callchain_hits); | |
fefb0b94 | 256 | hists__inc_nr_entries(self, n); |
3d1d07ec | 257 | } |
b9bf0892 | 258 | |
1c02c4d2 | 259 | self->entries = tmp; |
3d1d07ec | 260 | } |
4ecf84d0 ACM |
261 | |
262 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) | |
263 | { | |
264 | int i; | |
265 | int ret = fprintf(fp, " "); | |
266 | ||
267 | for (i = 0; i < left_margin; i++) | |
268 | ret += fprintf(fp, " "); | |
269 | ||
270 | return ret; | |
271 | } | |
272 | ||
273 | static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, | |
274 | int left_margin) | |
275 | { | |
276 | int i; | |
277 | size_t ret = callchain__fprintf_left_margin(fp, left_margin); | |
278 | ||
279 | for (i = 0; i < depth; i++) | |
280 | if (depth_mask & (1 << i)) | |
281 | ret += fprintf(fp, "| "); | |
282 | else | |
283 | ret += fprintf(fp, " "); | |
284 | ||
285 | ret += fprintf(fp, "\n"); | |
286 | ||
287 | return ret; | |
288 | } | |
289 | ||
290 | static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, | |
291 | int depth, int depth_mask, int count, | |
292 | u64 total_samples, int hits, | |
293 | int left_margin) | |
294 | { | |
295 | int i; | |
296 | size_t ret = 0; | |
297 | ||
298 | ret += callchain__fprintf_left_margin(fp, left_margin); | |
299 | for (i = 0; i < depth; i++) { | |
300 | if (depth_mask & (1 << i)) | |
301 | ret += fprintf(fp, "|"); | |
302 | else | |
303 | ret += fprintf(fp, " "); | |
304 | if (!count && i == depth - 1) { | |
305 | double percent; | |
306 | ||
307 | percent = hits * 100.0 / total_samples; | |
308 | ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); | |
309 | } else | |
310 | ret += fprintf(fp, "%s", " "); | |
311 | } | |
b3c9ac08 ACM |
312 | if (chain->ms.sym) |
313 | ret += fprintf(fp, "%s\n", chain->ms.sym->name); | |
4ecf84d0 ACM |
314 | else |
315 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); | |
316 | ||
317 | return ret; | |
318 | } | |
319 | ||
320 | static struct symbol *rem_sq_bracket; | |
321 | static struct callchain_list rem_hits; | |
322 | ||
323 | static void init_rem_hits(void) | |
324 | { | |
325 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | |
326 | if (!rem_sq_bracket) { | |
327 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | |
328 | return; | |
329 | } | |
330 | ||
331 | strcpy(rem_sq_bracket->name, "[...]"); | |
b3c9ac08 | 332 | rem_hits.ms.sym = rem_sq_bracket; |
4ecf84d0 ACM |
333 | } |
334 | ||
335 | static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |
336 | u64 total_samples, int depth, | |
337 | int depth_mask, int left_margin) | |
338 | { | |
339 | struct rb_node *node, *next; | |
340 | struct callchain_node *child; | |
341 | struct callchain_list *chain; | |
342 | int new_depth_mask = depth_mask; | |
343 | u64 new_total; | |
344 | u64 remaining; | |
345 | size_t ret = 0; | |
346 | int i; | |
232a5c94 | 347 | uint entries_printed = 0; |
4ecf84d0 ACM |
348 | |
349 | if (callchain_param.mode == CHAIN_GRAPH_REL) | |
350 | new_total = self->children_hit; | |
351 | else | |
352 | new_total = total_samples; | |
353 | ||
354 | remaining = new_total; | |
355 | ||
356 | node = rb_first(&self->rb_root); | |
357 | while (node) { | |
358 | u64 cumul; | |
359 | ||
360 | child = rb_entry(node, struct callchain_node, rb_node); | |
361 | cumul = cumul_hits(child); | |
362 | remaining -= cumul; | |
363 | ||
364 | /* | |
365 | * The depth mask manages the output of pipes that show | |
366 | * the depth. We don't want to keep the pipes of the current | |
367 | * level for the last child of this depth. | |
368 | * Except if we have remaining filtered hits. They will | |
369 | * supersede the last child | |
370 | */ | |
371 | next = rb_next(node); | |
372 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) | |
373 | new_depth_mask &= ~(1 << (depth - 1)); | |
374 | ||
375 | /* | |
3ad2f3fb | 376 | * But we keep the older depth mask for the line separator |
4ecf84d0 ACM |
377 | * to keep the level link until we reach the last child |
378 | */ | |
379 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, | |
380 | left_margin); | |
381 | i = 0; | |
382 | list_for_each_entry(chain, &child->val, list) { | |
4ecf84d0 ACM |
383 | ret += ipchain__fprintf_graph(fp, chain, depth, |
384 | new_depth_mask, i++, | |
385 | new_total, | |
386 | cumul, | |
387 | left_margin); | |
388 | } | |
389 | ret += __callchain__fprintf_graph(fp, child, new_total, | |
390 | depth + 1, | |
391 | new_depth_mask | (1 << depth), | |
392 | left_margin); | |
393 | node = next; | |
232a5c94 ACM |
394 | if (++entries_printed == callchain_param.print_limit) |
395 | break; | |
4ecf84d0 ACM |
396 | } |
397 | ||
398 | if (callchain_param.mode == CHAIN_GRAPH_REL && | |
399 | remaining && remaining != new_total) { | |
400 | ||
401 | if (!rem_sq_bracket) | |
402 | return ret; | |
403 | ||
404 | new_depth_mask &= ~(1 << (depth - 1)); | |
405 | ||
406 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | |
407 | new_depth_mask, 0, new_total, | |
408 | remaining, left_margin); | |
409 | } | |
410 | ||
411 | return ret; | |
412 | } | |
413 | ||
414 | static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |
415 | u64 total_samples, int left_margin) | |
416 | { | |
417 | struct callchain_list *chain; | |
418 | bool printed = false; | |
419 | int i = 0; | |
420 | int ret = 0; | |
232a5c94 | 421 | u32 entries_printed = 0; |
4ecf84d0 ACM |
422 | |
423 | list_for_each_entry(chain, &self->val, list) { | |
4ecf84d0 ACM |
424 | if (!i++ && sort__first_dimension == SORT_SYM) |
425 | continue; | |
426 | ||
427 | if (!printed) { | |
428 | ret += callchain__fprintf_left_margin(fp, left_margin); | |
429 | ret += fprintf(fp, "|\n"); | |
430 | ret += callchain__fprintf_left_margin(fp, left_margin); | |
431 | ret += fprintf(fp, "---"); | |
432 | ||
433 | left_margin += 3; | |
434 | printed = true; | |
435 | } else | |
436 | ret += callchain__fprintf_left_margin(fp, left_margin); | |
437 | ||
b3c9ac08 ACM |
438 | if (chain->ms.sym) |
439 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); | |
4ecf84d0 ACM |
440 | else |
441 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | |
232a5c94 ACM |
442 | |
443 | if (++entries_printed == callchain_param.print_limit) | |
444 | break; | |
4ecf84d0 ACM |
445 | } |
446 | ||
447 | ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); | |
448 | ||
449 | return ret; | |
450 | } | |
451 | ||
452 | static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | |
453 | u64 total_samples) | |
454 | { | |
455 | struct callchain_list *chain; | |
456 | size_t ret = 0; | |
457 | ||
458 | if (!self) | |
459 | return 0; | |
460 | ||
461 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); | |
462 | ||
463 | ||
464 | list_for_each_entry(chain, &self->val, list) { | |
465 | if (chain->ip >= PERF_CONTEXT_MAX) | |
466 | continue; | |
b3c9ac08 ACM |
467 | if (chain->ms.sym) |
468 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); | |
4ecf84d0 ACM |
469 | else |
470 | ret += fprintf(fp, " %p\n", | |
471 | (void *)(long)chain->ip); | |
472 | } | |
473 | ||
474 | return ret; | |
475 | } | |
476 | ||
477 | static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |
478 | u64 total_samples, int left_margin) | |
479 | { | |
480 | struct rb_node *rb_node; | |
481 | struct callchain_node *chain; | |
482 | size_t ret = 0; | |
232a5c94 | 483 | u32 entries_printed = 0; |
4ecf84d0 ACM |
484 | |
485 | rb_node = rb_first(&self->sorted_chain); | |
486 | while (rb_node) { | |
487 | double percent; | |
488 | ||
489 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | |
490 | percent = chain->hit * 100.0 / total_samples; | |
491 | switch (callchain_param.mode) { | |
492 | case CHAIN_FLAT: | |
493 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | |
494 | percent); | |
495 | ret += callchain__fprintf_flat(fp, chain, total_samples); | |
496 | break; | |
497 | case CHAIN_GRAPH_ABS: /* Falldown */ | |
498 | case CHAIN_GRAPH_REL: | |
499 | ret += callchain__fprintf_graph(fp, chain, total_samples, | |
500 | left_margin); | |
501 | case CHAIN_NONE: | |
502 | default: | |
503 | break; | |
504 | } | |
505 | ret += fprintf(fp, "\n"); | |
232a5c94 ACM |
506 | if (++entries_printed == callchain_param.print_limit) |
507 | break; | |
4ecf84d0 ACM |
508 | rb_node = rb_next(rb_node); |
509 | } | |
510 | ||
511 | return ret; | |
512 | } | |
513 | ||
1c02c4d2 ACM |
514 | int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, |
515 | struct hists *pair_hists, bool show_displacement, | |
516 | long displacement, bool color, u64 session_total) | |
4ecf84d0 ACM |
517 | { |
518 | struct sort_entry *se; | |
a1645ce1 | 519 | u64 count, total, count_sys, count_us, count_guest_sys, count_guest_us; |
c351c281 | 520 | const char *sep = symbol_conf.field_sep; |
a4e3b956 | 521 | int ret; |
4ecf84d0 ACM |
522 | |
523 | if (symbol_conf.exclude_other && !self->parent) | |
524 | return 0; | |
525 | ||
1c02c4d2 | 526 | if (pair_hists) { |
c351c281 | 527 | count = self->pair ? self->pair->count : 0; |
1c02c4d2 | 528 | total = pair_hists->stats.total; |
a1645ce1 ZY |
529 | count_sys = self->pair ? self->pair->count_sys : 0; |
530 | count_us = self->pair ? self->pair->count_us : 0; | |
531 | count_guest_sys = self->pair ? self->pair->count_guest_sys : 0; | |
532 | count_guest_us = self->pair ? self->pair->count_guest_us : 0; | |
c351c281 ACM |
533 | } else { |
534 | count = self->count; | |
eefc465c | 535 | total = session_total; |
a1645ce1 ZY |
536 | count_sys = self->count_sys; |
537 | count_us = self->count_us; | |
538 | count_guest_sys = self->count_guest_sys; | |
539 | count_guest_us = self->count_guest_us; | |
c351c281 ACM |
540 | } |
541 | ||
a4e3b956 ACM |
542 | if (total) { |
543 | if (color) | |
544 | ret = percent_color_snprintf(s, size, | |
545 | sep ? "%.2f" : " %6.2f%%", | |
546 | (count * 100.0) / total); | |
547 | else | |
548 | ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", | |
549 | (count * 100.0) / total); | |
a1645ce1 ZY |
550 | if (symbol_conf.show_cpu_utilization) { |
551 | ret += percent_color_snprintf(s + ret, size - ret, | |
552 | sep ? "%.2f" : " %6.2f%%", | |
553 | (count_sys * 100.0) / total); | |
554 | ret += percent_color_snprintf(s + ret, size - ret, | |
555 | sep ? "%.2f" : " %6.2f%%", | |
556 | (count_us * 100.0) / total); | |
557 | if (perf_guest) { | |
558 | ret += percent_color_snprintf(s + ret, | |
559 | size - ret, | |
560 | sep ? "%.2f" : " %6.2f%%", | |
561 | (count_guest_sys * 100.0) / | |
562 | total); | |
563 | ret += percent_color_snprintf(s + ret, | |
564 | size - ret, | |
565 | sep ? "%.2f" : " %6.2f%%", | |
566 | (count_guest_us * 100.0) / | |
567 | total); | |
568 | } | |
569 | } | |
a4e3b956 ACM |
570 | } else |
571 | ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count); | |
4ecf84d0 ACM |
572 | |
573 | if (symbol_conf.show_nr_samples) { | |
c351c281 | 574 | if (sep) |
a4e3b956 | 575 | ret += snprintf(s + ret, size - ret, "%c%lld", *sep, count); |
4ecf84d0 | 576 | else |
a4e3b956 | 577 | ret += snprintf(s + ret, size - ret, "%11lld", count); |
c351c281 ACM |
578 | } |
579 | ||
1c02c4d2 | 580 | if (pair_hists) { |
c351c281 ACM |
581 | char bf[32]; |
582 | double old_percent = 0, new_percent = 0, diff; | |
583 | ||
584 | if (total > 0) | |
9b33827d | 585 | old_percent = (count * 100.0) / total; |
eefc465c EM |
586 | if (session_total > 0) |
587 | new_percent = (self->count * 100.0) / session_total; | |
c351c281 | 588 | |
9b33827d | 589 | diff = new_percent - old_percent; |
c351c281 | 590 | |
9b33827d | 591 | if (fabs(diff) >= 0.01) |
c351c281 ACM |
592 | snprintf(bf, sizeof(bf), "%+4.2F%%", diff); |
593 | else | |
594 | snprintf(bf, sizeof(bf), " "); | |
595 | ||
596 | if (sep) | |
a4e3b956 | 597 | ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); |
c351c281 | 598 | else |
a4e3b956 | 599 | ret += snprintf(s + ret, size - ret, "%11.11s", bf); |
c351c281 ACM |
600 | |
601 | if (show_displacement) { | |
602 | if (displacement) | |
603 | snprintf(bf, sizeof(bf), "%+4ld", displacement); | |
604 | else | |
605 | snprintf(bf, sizeof(bf), " "); | |
606 | ||
607 | if (sep) | |
a4e3b956 | 608 | ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); |
c351c281 | 609 | else |
a4e3b956 | 610 | ret += snprintf(s + ret, size - ret, "%6.6s", bf); |
c351c281 | 611 | } |
4ecf84d0 ACM |
612 | } |
613 | ||
614 | list_for_each_entry(se, &hist_entry__sort_list, list) { | |
615 | if (se->elide) | |
616 | continue; | |
617 | ||
a4e3b956 | 618 | ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); |
fcd14984 FW |
619 | ret += se->se_snprintf(self, s + ret, size - ret, |
620 | se->se_width ? *se->se_width : 0); | |
4ecf84d0 ACM |
621 | } |
622 | ||
a4e3b956 ACM |
623 | return ret; |
624 | } | |
625 | ||
1c02c4d2 ACM |
626 | int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, |
627 | bool show_displacement, long displacement, FILE *fp, | |
a4e3b956 ACM |
628 | u64 session_total) |
629 | { | |
630 | char bf[512]; | |
1c02c4d2 | 631 | hist_entry__snprintf(self, bf, sizeof(bf), pair_hists, |
a4e3b956 ACM |
632 | show_displacement, displacement, |
633 | true, session_total); | |
634 | return fprintf(fp, "%s\n", bf); | |
3997d377 | 635 | } |
4ecf84d0 | 636 | |
3997d377 ACM |
637 | static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, |
638 | u64 session_total) | |
639 | { | |
640 | int left_margin = 0; | |
4ecf84d0 | 641 | |
3997d377 ACM |
642 | if (sort__first_dimension == SORT_COMM) { |
643 | struct sort_entry *se = list_first_entry(&hist_entry__sort_list, | |
644 | typeof(*se), list); | |
fcd14984 | 645 | left_margin = se->se_width ? *se->se_width : 0; |
3997d377 | 646 | left_margin -= thread__comm_len(self->thread); |
4ecf84d0 ACM |
647 | } |
648 | ||
3997d377 ACM |
649 | return hist_entry_callchain__fprintf(fp, self, session_total, |
650 | left_margin); | |
4ecf84d0 ACM |
651 | } |
652 | ||
1c02c4d2 ACM |
653 | size_t hists__fprintf(struct hists *self, struct hists *pair, |
654 | bool show_displacement, FILE *fp) | |
4ecf84d0 | 655 | { |
4ecf84d0 ACM |
656 | struct sort_entry *se; |
657 | struct rb_node *nd; | |
658 | size_t ret = 0; | |
c351c281 ACM |
659 | unsigned long position = 1; |
660 | long displacement = 0; | |
4ecf84d0 | 661 | unsigned int width; |
c351c281 | 662 | const char *sep = symbol_conf.field_sep; |
4ecf84d0 ACM |
663 | char *col_width = symbol_conf.col_width_list_str; |
664 | ||
665 | init_rem_hits(); | |
666 | ||
c351c281 ACM |
667 | fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); |
668 | ||
4ecf84d0 | 669 | if (symbol_conf.show_nr_samples) { |
c351c281 ACM |
670 | if (sep) |
671 | fprintf(fp, "%cSamples", *sep); | |
4ecf84d0 ACM |
672 | else |
673 | fputs(" Samples ", fp); | |
674 | } | |
c351c281 | 675 | |
a1645ce1 ZY |
676 | if (symbol_conf.show_cpu_utilization) { |
677 | if (sep) { | |
678 | ret += fprintf(fp, "%csys", *sep); | |
679 | ret += fprintf(fp, "%cus", *sep); | |
680 | if (perf_guest) { | |
681 | ret += fprintf(fp, "%cguest sys", *sep); | |
682 | ret += fprintf(fp, "%cguest us", *sep); | |
683 | } | |
684 | } else { | |
685 | ret += fprintf(fp, " sys "); | |
686 | ret += fprintf(fp, " us "); | |
687 | if (perf_guest) { | |
688 | ret += fprintf(fp, " guest sys "); | |
689 | ret += fprintf(fp, " guest us "); | |
690 | } | |
691 | } | |
692 | } | |
693 | ||
c351c281 ACM |
694 | if (pair) { |
695 | if (sep) | |
696 | ret += fprintf(fp, "%cDelta", *sep); | |
697 | else | |
698 | ret += fprintf(fp, " Delta "); | |
699 | ||
700 | if (show_displacement) { | |
701 | if (sep) | |
702 | ret += fprintf(fp, "%cDisplacement", *sep); | |
703 | else | |
704 | ret += fprintf(fp, " Displ"); | |
705 | } | |
706 | } | |
707 | ||
4ecf84d0 ACM |
708 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
709 | if (se->elide) | |
710 | continue; | |
c351c281 | 711 | if (sep) { |
fcd14984 | 712 | fprintf(fp, "%c%s", *sep, se->se_header); |
4ecf84d0 ACM |
713 | continue; |
714 | } | |
fcd14984 FW |
715 | width = strlen(se->se_header); |
716 | if (se->se_width) { | |
4ecf84d0 ACM |
717 | if (symbol_conf.col_width_list_str) { |
718 | if (col_width) { | |
fcd14984 | 719 | *se->se_width = atoi(col_width); |
4ecf84d0 ACM |
720 | col_width = strchr(col_width, ','); |
721 | if (col_width) | |
722 | ++col_width; | |
723 | } | |
724 | } | |
fcd14984 | 725 | width = *se->se_width = max(*se->se_width, width); |
4ecf84d0 | 726 | } |
fcd14984 | 727 | fprintf(fp, " %*s", width, se->se_header); |
4ecf84d0 ACM |
728 | } |
729 | fprintf(fp, "\n"); | |
730 | ||
c351c281 | 731 | if (sep) |
4ecf84d0 ACM |
732 | goto print_entries; |
733 | ||
734 | fprintf(fp, "# ........"); | |
735 | if (symbol_conf.show_nr_samples) | |
736 | fprintf(fp, " .........."); | |
c351c281 ACM |
737 | if (pair) { |
738 | fprintf(fp, " .........."); | |
739 | if (show_displacement) | |
740 | fprintf(fp, " ....."); | |
741 | } | |
4ecf84d0 ACM |
742 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
743 | unsigned int i; | |
744 | ||
745 | if (se->elide) | |
746 | continue; | |
747 | ||
748 | fprintf(fp, " "); | |
fcd14984 FW |
749 | if (se->se_width) |
750 | width = *se->se_width; | |
4ecf84d0 | 751 | else |
fcd14984 | 752 | width = strlen(se->se_header); |
4ecf84d0 ACM |
753 | for (i = 0; i < width; i++) |
754 | fprintf(fp, "."); | |
755 | } | |
4ecf84d0 | 756 | |
c351c281 | 757 | fprintf(fp, "\n#\n"); |
4ecf84d0 ACM |
758 | |
759 | print_entries: | |
1c02c4d2 | 760 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { |
c351c281 ACM |
761 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
762 | ||
763 | if (show_displacement) { | |
764 | if (h->pair != NULL) | |
765 | displacement = ((long)h->pair->position - | |
766 | (long)position); | |
767 | else | |
768 | displacement = 0; | |
769 | ++position; | |
770 | } | |
eefc465c | 771 | ret += hist_entry__fprintf(h, pair, show_displacement, |
1c02c4d2 | 772 | displacement, fp, self->stats.total); |
3997d377 ACM |
773 | |
774 | if (symbol_conf.use_callchain) | |
1c02c4d2 | 775 | ret += hist_entry__fprintf_callchain(h, fp, self->stats.total); |
3997d377 | 776 | |
59fd5306 | 777 | if (h->ms.map == NULL && verbose > 1) { |
65f2ed2b | 778 | __map_groups__fprintf_maps(&h->thread->mg, |
c6e718ff | 779 | MAP__FUNCTION, verbose, fp); |
65f2ed2b ACM |
780 | fprintf(fp, "%.10s end\n", graph_dotted_line); |
781 | } | |
4ecf84d0 ACM |
782 | } |
783 | ||
4ecf84d0 ACM |
784 | free(rem_sq_bracket); |
785 | ||
786 | return ret; | |
787 | } | |
b09e0190 ACM |
788 | |
789 | enum hist_filter { | |
790 | HIST_FILTER__DSO, | |
791 | HIST_FILTER__THREAD, | |
792 | }; | |
793 | ||
794 | void hists__filter_by_dso(struct hists *self, const struct dso *dso) | |
795 | { | |
796 | struct rb_node *nd; | |
797 | ||
798 | self->nr_entries = self->stats.total = 0; | |
799 | self->max_sym_namelen = 0; | |
800 | ||
801 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | |
802 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | |
803 | ||
804 | if (symbol_conf.exclude_other && !h->parent) | |
805 | continue; | |
806 | ||
807 | if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { | |
808 | h->filtered |= (1 << HIST_FILTER__DSO); | |
809 | continue; | |
810 | } | |
811 | ||
812 | h->filtered &= ~(1 << HIST_FILTER__DSO); | |
813 | if (!h->filtered) { | |
814 | ++self->nr_entries; | |
815 | self->stats.total += h->count; | |
816 | if (h->ms.sym && | |
817 | self->max_sym_namelen < h->ms.sym->namelen) | |
818 | self->max_sym_namelen = h->ms.sym->namelen; | |
819 | } | |
820 | } | |
821 | } | |
822 | ||
823 | void hists__filter_by_thread(struct hists *self, const struct thread *thread) | |
824 | { | |
825 | struct rb_node *nd; | |
826 | ||
827 | self->nr_entries = self->stats.total = 0; | |
828 | self->max_sym_namelen = 0; | |
829 | ||
830 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | |
831 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | |
832 | ||
833 | if (thread != NULL && h->thread != thread) { | |
834 | h->filtered |= (1 << HIST_FILTER__THREAD); | |
835 | continue; | |
836 | } | |
837 | h->filtered &= ~(1 << HIST_FILTER__THREAD); | |
838 | if (!h->filtered) { | |
839 | ++self->nr_entries; | |
840 | self->stats.total += h->count; | |
841 | if (h->ms.sym && | |
842 | self->max_sym_namelen < h->ms.sym->namelen) | |
843 | self->max_sym_namelen = h->ms.sym->namelen; | |
844 | } | |
845 | } | |
846 | } | |
ef7b93a1 ACM |
847 | |
848 | static int symbol__alloc_hist(struct symbol *self) | |
849 | { | |
850 | struct sym_priv *priv = symbol__priv(self); | |
851 | const int size = (sizeof(*priv->hist) + | |
852 | (self->end - self->start) * sizeof(u64)); | |
853 | ||
854 | priv->hist = zalloc(size); | |
855 | return priv->hist == NULL ? -1 : 0; | |
856 | } | |
857 | ||
858 | int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) | |
859 | { | |
860 | unsigned int sym_size, offset; | |
861 | struct symbol *sym = self->ms.sym; | |
862 | struct sym_priv *priv; | |
863 | struct sym_hist *h; | |
864 | ||
865 | if (!sym || !self->ms.map) | |
866 | return 0; | |
867 | ||
868 | priv = symbol__priv(sym); | |
869 | if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) | |
870 | return -ENOMEM; | |
871 | ||
872 | sym_size = sym->end - sym->start; | |
873 | offset = ip - sym->start; | |
874 | ||
875 | pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); | |
876 | ||
877 | if (offset >= sym_size) | |
878 | return 0; | |
879 | ||
880 | h = priv->hist; | |
881 | h->sum++; | |
882 | h->ip[offset]++; | |
883 | ||
884 | pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, | |
885 | self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); | |
886 | return 0; | |
887 | } | |
888 | ||
889 | static struct objdump_line *objdump_line__new(s64 offset, char *line) | |
890 | { | |
891 | struct objdump_line *self = malloc(sizeof(*self)); | |
892 | ||
893 | if (self != NULL) { | |
894 | self->offset = offset; | |
895 | self->line = line; | |
896 | } | |
897 | ||
898 | return self; | |
899 | } | |
900 | ||
901 | void objdump_line__free(struct objdump_line *self) | |
902 | { | |
903 | free(self->line); | |
904 | free(self); | |
905 | } | |
906 | ||
907 | static void objdump__add_line(struct list_head *head, struct objdump_line *line) | |
908 | { | |
909 | list_add_tail(&line->node, head); | |
910 | } | |
911 | ||
912 | struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | |
913 | struct objdump_line *pos) | |
914 | { | |
915 | list_for_each_entry_continue(pos, head, node) | |
916 | if (pos->offset >= 0) | |
917 | return pos; | |
918 | ||
919 | return NULL; | |
920 | } | |
921 | ||
922 | static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, | |
923 | struct list_head *head) | |
924 | { | |
925 | struct symbol *sym = self->ms.sym; | |
926 | struct objdump_line *objdump_line; | |
927 | char *line = NULL, *tmp, *tmp2, *c; | |
928 | size_t line_len; | |
929 | s64 line_ip, offset = -1; | |
930 | ||
931 | if (getline(&line, &line_len, file) < 0) | |
932 | return -1; | |
933 | ||
934 | if (!line) | |
935 | return -1; | |
936 | ||
937 | while (line_len != 0 && isspace(line[line_len - 1])) | |
938 | line[--line_len] = '\0'; | |
939 | ||
940 | c = strchr(line, '\n'); | |
941 | if (c) | |
942 | *c = 0; | |
943 | ||
944 | line_ip = -1; | |
945 | ||
946 | /* | |
947 | * Strip leading spaces: | |
948 | */ | |
949 | tmp = line; | |
950 | while (*tmp) { | |
951 | if (*tmp != ' ') | |
952 | break; | |
953 | tmp++; | |
954 | } | |
955 | ||
956 | if (*tmp) { | |
957 | /* | |
958 | * Parse hexa addresses followed by ':' | |
959 | */ | |
960 | line_ip = strtoull(tmp, &tmp2, 16); | |
961 | if (*tmp2 != ':') | |
962 | line_ip = -1; | |
963 | } | |
964 | ||
965 | if (line_ip != -1) { | |
966 | u64 start = map__rip_2objdump(self->ms.map, sym->start); | |
967 | offset = line_ip - start; | |
968 | } | |
969 | ||
970 | objdump_line = objdump_line__new(offset, line); | |
971 | if (objdump_line == NULL) { | |
972 | free(line); | |
973 | return -1; | |
974 | } | |
975 | objdump__add_line(head, objdump_line); | |
976 | ||
977 | return 0; | |
978 | } | |
979 | ||
980 | int hist_entry__annotate(struct hist_entry *self, struct list_head *head) | |
981 | { | |
982 | struct symbol *sym = self->ms.sym; | |
983 | struct map *map = self->ms.map; | |
984 | struct dso *dso = map->dso; | |
985 | const char *filename = dso->long_name; | |
986 | char command[PATH_MAX * 2]; | |
987 | FILE *file; | |
988 | u64 len; | |
989 | ||
990 | if (!filename) | |
991 | return -1; | |
992 | ||
993 | if (dso->origin == DSO__ORIG_KERNEL) { | |
994 | if (dso->annotate_warned) | |
995 | return 0; | |
996 | dso->annotate_warned = 1; | |
997 | pr_err("Can't annotate %s: No vmlinux file was found in the " | |
998 | "path:\n", sym->name); | |
999 | vmlinux_path__fprintf(stderr); | |
1000 | return -1; | |
1001 | } | |
1002 | ||
1003 | pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, | |
1004 | filename, sym->name, map->unmap_ip(map, sym->start), | |
1005 | map->unmap_ip(map, sym->end)); | |
1006 | ||
1007 | len = sym->end - sym->start; | |
1008 | ||
1009 | pr_debug("annotating [%p] %30s : [%p] %30s\n", | |
1010 | dso, dso->long_name, sym, sym->name); | |
1011 | ||
1012 | snprintf(command, sizeof(command), | |
1013 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", | |
1014 | map__rip_2objdump(map, sym->start), | |
1015 | map__rip_2objdump(map, sym->end), | |
1016 | filename, filename); | |
1017 | ||
1018 | pr_debug("Executing: %s\n", command); | |
1019 | ||
1020 | file = popen(command, "r"); | |
1021 | if (!file) | |
1022 | return -1; | |
1023 | ||
1024 | while (!feof(file)) | |
1025 | if (hist_entry__parse_objdump_line(self, file, head) < 0) | |
1026 | break; | |
1027 | ||
1028 | pclose(file); | |
1029 | return 0; | |
1030 | } | |
c8446b9b ACM |
1031 | |
1032 | void hists__inc_nr_events(struct hists *self, u32 type) | |
1033 | { | |
1034 | ++self->hists.stats.nr_events[0]; | |
1035 | ++self->hists.stats.nr_events[type]; | |
1036 | } | |
1037 | ||
1038 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | |
1039 | { | |
1040 | int i; | |
1041 | size_t ret = 0; | |
1042 | ||
1043 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { | |
1044 | if (!event__name[i]) | |
1045 | continue; | |
1046 | ret += fprintf(fp, "%10s events: %10d\n", | |
1047 | event__name[i], self->stats.nr_events[i]); | |
1048 | } | |
1049 | ||
1050 | return ret; | |
1051 | } |