perf hists browser: Honour symbol_conf.show_{nr_samples,total_period}
[deliverable/linux.git] / tools / perf / util / hist.c
CommitLineData
78f7defe 1#include "annotate.h"
8a0ecfb8 2#include "util.h"
598357eb 3#include "build-id.h"
3d1d07ec 4#include "hist.h"
4e4f06e4
ACM
5#include "session.h"
6#include "sort.h"
9b33827d 7#include <math.h>
3d1d07ec 8
90cf1fb5
ACM
9static bool hists__filter_entry_by_dso(struct hists *hists,
10 struct hist_entry *he);
11static bool hists__filter_entry_by_thread(struct hists *hists,
12 struct hist_entry *he);
13
7a007ca9
ACM
14enum hist_filter {
15 HIST_FILTER__DSO,
16 HIST_FILTER__THREAD,
17 HIST_FILTER__PARENT,
18};
19
3d1d07ec
JK
20struct callchain_param callchain_param = {
21 .mode = CHAIN_GRAPH_REL,
d797fdc5
SL
22 .min_percent = 0.5,
23 .order = ORDER_CALLEE
3d1d07ec
JK
24};
25
42b28ac0 26u16 hists__col_len(struct hists *hists, enum hist_column col)
8a6c5b26 27{
42b28ac0 28 return hists->col_len[col];
8a6c5b26
ACM
29}
30
42b28ac0 31void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len)
8a6c5b26 32{
42b28ac0 33 hists->col_len[col] = len;
8a6c5b26
ACM
34}
35
42b28ac0 36bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len)
8a6c5b26 37{
42b28ac0
ACM
38 if (len > hists__col_len(hists, col)) {
39 hists__set_col_len(hists, col, len);
8a6c5b26
ACM
40 return true;
41 }
42 return false;
43}
44
42b28ac0 45static void hists__reset_col_len(struct hists *hists)
8a6c5b26
ACM
46{
47 enum hist_column col;
48
49 for (col = 0; col < HISTC_NR_COLS; ++col)
42b28ac0 50 hists__set_col_len(hists, col, 0);
8a6c5b26
ACM
51}
52
42b28ac0 53static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
8a6c5b26
ACM
54{
55 u16 len;
56
57 if (h->ms.sym)
42b28ac0 58 hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen);
d7603d51
ACM
59 else {
60 const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
61
42b28ac0 62 if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width &&
d7603d51
ACM
63 !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
64 !symbol_conf.dso_list)
42b28ac0 65 hists__set_col_len(hists, HISTC_DSO,
d7603d51
ACM
66 unresolved_col_width);
67 }
8a6c5b26
ACM
68
69 len = thread__comm_len(h->thread);
42b28ac0
ACM
70 if (hists__new_col_len(hists, HISTC_COMM, len))
71 hists__set_col_len(hists, HISTC_THREAD, len + 6);
8a6c5b26
ACM
72
73 if (h->ms.map) {
74 len = dso__name_len(h->ms.map->dso);
42b28ac0 75 hists__new_col_len(hists, HISTC_DSO, len);
8a6c5b26
ACM
76 }
77}
78
c82ee828
ACM
79static void hist_entry__add_cpumode_period(struct hist_entry *self,
80 unsigned int cpumode, u64 period)
a1645ce1 81{
28e2a106 82 switch (cpumode) {
a1645ce1 83 case PERF_RECORD_MISC_KERNEL:
c82ee828 84 self->period_sys += period;
a1645ce1
ZY
85 break;
86 case PERF_RECORD_MISC_USER:
c82ee828 87 self->period_us += period;
a1645ce1
ZY
88 break;
89 case PERF_RECORD_MISC_GUEST_KERNEL:
c82ee828 90 self->period_guest_sys += period;
a1645ce1
ZY
91 break;
92 case PERF_RECORD_MISC_GUEST_USER:
c82ee828 93 self->period_guest_us += period;
a1645ce1
ZY
94 break;
95 default:
96 break;
97 }
98}
99
ab81f3fd
ACM
100static void hist_entry__decay(struct hist_entry *he)
101{
102 he->period = (he->period * 7) / 8;
103 he->nr_events = (he->nr_events * 7) / 8;
104}
105
106static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
107{
df71d95f
ACM
108 if (he->period == 0)
109 return true;
ab81f3fd
ACM
110 hists->stats.total_period -= he->period;
111 hist_entry__decay(he);
112 hists->stats.total_period += he->period;
113 return he->period == 0;
114}
115
b079d4e9
ACM
116static void __hists__decay_entries(struct hists *hists, bool zap_user,
117 bool zap_kernel, bool threaded)
ab81f3fd
ACM
118{
119 struct rb_node *next = rb_first(&hists->entries);
120 struct hist_entry *n;
121
122 while (next) {
123 n = rb_entry(next, struct hist_entry, rb_node);
124 next = rb_next(&n->rb_node);
df71d95f
ACM
125 /*
126 * We may be annotating this, for instance, so keep it here in
127 * case some it gets new samples, we'll eventually free it when
128 * the user stops browsing and it agains gets fully decayed.
129 */
b079d4e9
ACM
130 if (((zap_user && n->level == '.') ||
131 (zap_kernel && n->level != '.') ||
132 hists__decay_entry(hists, n)) &&
133 !n->used) {
ab81f3fd
ACM
134 rb_erase(&n->rb_node, &hists->entries);
135
e345fa18 136 if (sort__need_collapse || threaded)
ab81f3fd
ACM
137 rb_erase(&n->rb_node_in, &hists->entries_collapsed);
138
139 hist_entry__free(n);
140 --hists->nr_entries;
141 }
142 }
143}
144
b079d4e9 145void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)
e345fa18 146{
b079d4e9 147 return __hists__decay_entries(hists, zap_user, zap_kernel, false);
e345fa18
ACM
148}
149
b079d4e9
ACM
150void hists__decay_entries_threaded(struct hists *hists,
151 bool zap_user, bool zap_kernel)
e345fa18 152{
b079d4e9 153 return __hists__decay_entries(hists, zap_user, zap_kernel, true);
e345fa18
ACM
154}
155
3d1d07ec 156/*
c82ee828 157 * histogram, sorted on item, collects periods
3d1d07ec
JK
158 */
159
28e2a106
ACM
160static struct hist_entry *hist_entry__new(struct hist_entry *template)
161{
d2009c51 162 size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0;
28e2a106
ACM
163 struct hist_entry *self = malloc(sizeof(*self) + callchain_size);
164
165 if (self != NULL) {
166 *self = *template;
c82ee828 167 self->nr_events = 1;
0a1eae39
ACM
168 if (self->ms.map)
169 self->ms.map->referenced = true;
28e2a106
ACM
170 if (symbol_conf.use_callchain)
171 callchain_init(self->callchain);
172 }
173
174 return self;
175}
176
42b28ac0 177static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h)
fefb0b94 178{
8a6c5b26 179 if (!h->filtered) {
42b28ac0
ACM
180 hists__calc_col_len(hists, h);
181 ++hists->nr_entries;
1980c2eb 182 hists->stats.total_period += h->period;
8a6c5b26 183 }
fefb0b94
ACM
184}
185
7a007ca9
ACM
186static u8 symbol__parent_filter(const struct symbol *parent)
187{
188 if (symbol_conf.exclude_other && parent == NULL)
189 return 1 << HIST_FILTER__PARENT;
190 return 0;
191}
192
42b28ac0 193struct hist_entry *__hists__add_entry(struct hists *hists,
1c02c4d2 194 struct addr_location *al,
c82ee828 195 struct symbol *sym_parent, u64 period)
9735abf1 196{
1980c2eb 197 struct rb_node **p;
9735abf1
ACM
198 struct rb_node *parent = NULL;
199 struct hist_entry *he;
200 struct hist_entry entry = {
1ed091c4 201 .thread = al->thread,
59fd5306
ACM
202 .ms = {
203 .map = al->map,
204 .sym = al->sym,
205 },
f60f3593 206 .cpu = al->cpu,
1ed091c4
ACM
207 .ip = al->addr,
208 .level = al->level,
c82ee828 209 .period = period,
9735abf1 210 .parent = sym_parent,
7a007ca9 211 .filtered = symbol__parent_filter(sym_parent),
9735abf1
ACM
212 };
213 int cmp;
214
1980c2eb
ACM
215 pthread_mutex_lock(&hists->lock);
216
217 p = &hists->entries_in->rb_node;
218
9735abf1
ACM
219 while (*p != NULL) {
220 parent = *p;
1980c2eb 221 he = rb_entry(parent, struct hist_entry, rb_node_in);
9735abf1
ACM
222
223 cmp = hist_entry__cmp(&entry, he);
224
225 if (!cmp) {
c82ee828
ACM
226 he->period += period;
227 ++he->nr_events;
28e2a106 228 goto out;
9735abf1
ACM
229 }
230
231 if (cmp < 0)
232 p = &(*p)->rb_left;
233 else
234 p = &(*p)->rb_right;
235 }
236
28e2a106 237 he = hist_entry__new(&entry);
9735abf1 238 if (!he)
1980c2eb
ACM
239 goto out_unlock;
240
241 rb_link_node(&he->rb_node_in, parent, p);
242 rb_insert_color(&he->rb_node_in, hists->entries_in);
28e2a106 243out:
c82ee828 244 hist_entry__add_cpumode_period(he, al->cpumode, period);
1980c2eb
ACM
245out_unlock:
246 pthread_mutex_unlock(&hists->lock);
9735abf1
ACM
247 return he;
248}
249
3d1d07ec
JK
250int64_t
251hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
252{
253 struct sort_entry *se;
254 int64_t cmp = 0;
255
256 list_for_each_entry(se, &hist_entry__sort_list, list) {
fcd14984 257 cmp = se->se_cmp(left, right);
3d1d07ec
JK
258 if (cmp)
259 break;
260 }
261
262 return cmp;
263}
264
265int64_t
266hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
267{
268 struct sort_entry *se;
269 int64_t cmp = 0;
270
271 list_for_each_entry(se, &hist_entry__sort_list, list) {
272 int64_t (*f)(struct hist_entry *, struct hist_entry *);
273
fcd14984 274 f = se->se_collapse ?: se->se_cmp;
3d1d07ec
JK
275
276 cmp = f(left, right);
277 if (cmp)
278 break;
279 }
280
281 return cmp;
282}
283
284void hist_entry__free(struct hist_entry *he)
285{
286 free(he);
287}
288
289/*
290 * collapse the histogram
291 */
292
42b28ac0 293static bool hists__collapse_insert_entry(struct hists *hists,
1b3a0e95
FW
294 struct rb_root *root,
295 struct hist_entry *he)
3d1d07ec 296{
b9bf0892 297 struct rb_node **p = &root->rb_node;
3d1d07ec
JK
298 struct rb_node *parent = NULL;
299 struct hist_entry *iter;
300 int64_t cmp;
301
302 while (*p != NULL) {
303 parent = *p;
1980c2eb 304 iter = rb_entry(parent, struct hist_entry, rb_node_in);
3d1d07ec
JK
305
306 cmp = hist_entry__collapse(iter, he);
307
308 if (!cmp) {
c82ee828 309 iter->period += he->period;
e39622ce 310 iter->nr_events += he->nr_events;
1b3a0e95 311 if (symbol_conf.use_callchain) {
42b28ac0
ACM
312 callchain_cursor_reset(&hists->callchain_cursor);
313 callchain_merge(&hists->callchain_cursor, iter->callchain,
1b3a0e95
FW
314 he->callchain);
315 }
3d1d07ec 316 hist_entry__free(he);
fefb0b94 317 return false;
3d1d07ec
JK
318 }
319
320 if (cmp < 0)
321 p = &(*p)->rb_left;
322 else
323 p = &(*p)->rb_right;
324 }
325
1980c2eb
ACM
326 rb_link_node(&he->rb_node_in, parent, p);
327 rb_insert_color(&he->rb_node_in, root);
fefb0b94 328 return true;
3d1d07ec
JK
329}
330
1980c2eb 331static struct rb_root *hists__get_rotate_entries_in(struct hists *hists)
3d1d07ec 332{
1980c2eb
ACM
333 struct rb_root *root;
334
335 pthread_mutex_lock(&hists->lock);
336
337 root = hists->entries_in;
338 if (++hists->entries_in > &hists->entries_in_array[1])
339 hists->entries_in = &hists->entries_in_array[0];
340
341 pthread_mutex_unlock(&hists->lock);
342
343 return root;
344}
345
90cf1fb5
ACM
346static void hists__apply_filters(struct hists *hists, struct hist_entry *he)
347{
348 hists__filter_entry_by_dso(hists, he);
349 hists__filter_entry_by_thread(hists, he);
350}
351
1980c2eb
ACM
352static void __hists__collapse_resort(struct hists *hists, bool threaded)
353{
354 struct rb_root *root;
3d1d07ec
JK
355 struct rb_node *next;
356 struct hist_entry *n;
357
1980c2eb 358 if (!sort__need_collapse && !threaded)
3d1d07ec
JK
359 return;
360
1980c2eb
ACM
361 root = hists__get_rotate_entries_in(hists);
362 next = rb_first(root);
363 hists->stats.total_period = 0;
b9bf0892 364
3d1d07ec 365 while (next) {
1980c2eb
ACM
366 n = rb_entry(next, struct hist_entry, rb_node_in);
367 next = rb_next(&n->rb_node_in);
3d1d07ec 368
1980c2eb 369 rb_erase(&n->rb_node_in, root);
90cf1fb5
ACM
370 if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n)) {
371 /*
372 * If it wasn't combined with one of the entries already
373 * collapsed, we need to apply the filters that may have
374 * been set by, say, the hist_browser.
375 */
376 hists__apply_filters(hists, n);
42b28ac0 377 hists__inc_nr_entries(hists, n);
90cf1fb5 378 }
3d1d07ec 379 }
1980c2eb 380}
b9bf0892 381
1980c2eb
ACM
382void hists__collapse_resort(struct hists *hists)
383{
384 return __hists__collapse_resort(hists, false);
385}
386
387void hists__collapse_resort_threaded(struct hists *hists)
388{
389 return __hists__collapse_resort(hists, true);
3d1d07ec
JK
390}
391
392/*
c82ee828 393 * reverse the map, sort on period.
3d1d07ec
JK
394 */
395
1c02c4d2
ACM
396static void __hists__insert_output_entry(struct rb_root *entries,
397 struct hist_entry *he,
398 u64 min_callchain_hits)
3d1d07ec 399{
1c02c4d2 400 struct rb_node **p = &entries->rb_node;
3d1d07ec
JK
401 struct rb_node *parent = NULL;
402 struct hist_entry *iter;
403
d599db3f 404 if (symbol_conf.use_callchain)
b9fb9304 405 callchain_param.sort(&he->sorted_chain, he->callchain,
3d1d07ec
JK
406 min_callchain_hits, &callchain_param);
407
408 while (*p != NULL) {
409 parent = *p;
410 iter = rb_entry(parent, struct hist_entry, rb_node);
411
c82ee828 412 if (he->period > iter->period)
3d1d07ec
JK
413 p = &(*p)->rb_left;
414 else
415 p = &(*p)->rb_right;
416 }
417
418 rb_link_node(&he->rb_node, parent, p);
1c02c4d2 419 rb_insert_color(&he->rb_node, entries);
3d1d07ec
JK
420}
421
1980c2eb 422static void __hists__output_resort(struct hists *hists, bool threaded)
3d1d07ec 423{
1980c2eb 424 struct rb_root *root;
3d1d07ec
JK
425 struct rb_node *next;
426 struct hist_entry *n;
3d1d07ec
JK
427 u64 min_callchain_hits;
428
42b28ac0 429 min_callchain_hits = hists->stats.total_period * (callchain_param.min_percent / 100);
3d1d07ec 430
1980c2eb
ACM
431 if (sort__need_collapse || threaded)
432 root = &hists->entries_collapsed;
433 else
434 root = hists->entries_in;
435
436 next = rb_first(root);
437 hists->entries = RB_ROOT;
3d1d07ec 438
42b28ac0
ACM
439 hists->nr_entries = 0;
440 hists__reset_col_len(hists);
fefb0b94 441
3d1d07ec 442 while (next) {
1980c2eb
ACM
443 n = rb_entry(next, struct hist_entry, rb_node_in);
444 next = rb_next(&n->rb_node_in);
3d1d07ec 445
1980c2eb 446 __hists__insert_output_entry(&hists->entries, n, min_callchain_hits);
42b28ac0 447 hists__inc_nr_entries(hists, n);
3d1d07ec 448 }
1980c2eb 449}
b9bf0892 450
1980c2eb
ACM
451void hists__output_resort(struct hists *hists)
452{
453 return __hists__output_resort(hists, false);
454}
455
456void hists__output_resort_threaded(struct hists *hists)
457{
458 return __hists__output_resort(hists, true);
3d1d07ec 459}
4ecf84d0
ACM
460
461static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
462{
463 int i;
464 int ret = fprintf(fp, " ");
465
466 for (i = 0; i < left_margin; i++)
467 ret += fprintf(fp, " ");
468
469 return ret;
470}
471
472static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
473 int left_margin)
474{
475 int i;
476 size_t ret = callchain__fprintf_left_margin(fp, left_margin);
477
478 for (i = 0; i < depth; i++)
479 if (depth_mask & (1 << i))
480 ret += fprintf(fp, "| ");
481 else
482 ret += fprintf(fp, " ");
483
484 ret += fprintf(fp, "\n");
485
486 return ret;
487}
488
489static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
c82ee828 490 int depth, int depth_mask, int period,
d425de54 491 u64 total_samples, u64 hits,
4ecf84d0
ACM
492 int left_margin)
493{
494 int i;
495 size_t ret = 0;
496
497 ret += callchain__fprintf_left_margin(fp, left_margin);
498 for (i = 0; i < depth; i++) {
499 if (depth_mask & (1 << i))
500 ret += fprintf(fp, "|");
501 else
502 ret += fprintf(fp, " ");
c82ee828 503 if (!period && i == depth - 1) {
4ecf84d0
ACM
504 double percent;
505
506 percent = hits * 100.0 / total_samples;
507 ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
508 } else
509 ret += fprintf(fp, "%s", " ");
510 }
b3c9ac08
ACM
511 if (chain->ms.sym)
512 ret += fprintf(fp, "%s\n", chain->ms.sym->name);
4ecf84d0
ACM
513 else
514 ret += fprintf(fp, "%p\n", (void *)(long)chain->ip);
515
516 return ret;
517}
518
519static struct symbol *rem_sq_bracket;
520static struct callchain_list rem_hits;
521
522static void init_rem_hits(void)
523{
524 rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
525 if (!rem_sq_bracket) {
526 fprintf(stderr, "Not enough memory to display remaining hits\n");
527 return;
528 }
529
530 strcpy(rem_sq_bracket->name, "[...]");
b3c9ac08 531 rem_hits.ms.sym = rem_sq_bracket;
4ecf84d0
ACM
532}
533
534static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
535 u64 total_samples, int depth,
536 int depth_mask, int left_margin)
537{
538 struct rb_node *node, *next;
539 struct callchain_node *child;
540 struct callchain_list *chain;
541 int new_depth_mask = depth_mask;
542 u64 new_total;
543 u64 remaining;
544 size_t ret = 0;
545 int i;
232a5c94 546 uint entries_printed = 0;
4ecf84d0
ACM
547
548 if (callchain_param.mode == CHAIN_GRAPH_REL)
549 new_total = self->children_hit;
550 else
551 new_total = total_samples;
552
553 remaining = new_total;
554
555 node = rb_first(&self->rb_root);
556 while (node) {
557 u64 cumul;
558
559 child = rb_entry(node, struct callchain_node, rb_node);
f08c3154 560 cumul = callchain_cumul_hits(child);
4ecf84d0
ACM
561 remaining -= cumul;
562
563 /*
564 * The depth mask manages the output of pipes that show
565 * the depth. We don't want to keep the pipes of the current
566 * level for the last child of this depth.
567 * Except if we have remaining filtered hits. They will
568 * supersede the last child
569 */
570 next = rb_next(node);
571 if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
572 new_depth_mask &= ~(1 << (depth - 1));
573
574 /*
3ad2f3fb 575 * But we keep the older depth mask for the line separator
4ecf84d0
ACM
576 * to keep the level link until we reach the last child
577 */
578 ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
579 left_margin);
580 i = 0;
581 list_for_each_entry(chain, &child->val, list) {
4ecf84d0
ACM
582 ret += ipchain__fprintf_graph(fp, chain, depth,
583 new_depth_mask, i++,
584 new_total,
585 cumul,
586 left_margin);
587 }
588 ret += __callchain__fprintf_graph(fp, child, new_total,
589 depth + 1,
590 new_depth_mask | (1 << depth),
591 left_margin);
592 node = next;
232a5c94
ACM
593 if (++entries_printed == callchain_param.print_limit)
594 break;
4ecf84d0
ACM
595 }
596
597 if (callchain_param.mode == CHAIN_GRAPH_REL &&
598 remaining && remaining != new_total) {
599
600 if (!rem_sq_bracket)
601 return ret;
602
603 new_depth_mask &= ~(1 << (depth - 1));
604
605 ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
606 new_depth_mask, 0, new_total,
607 remaining, left_margin);
608 }
609
610 return ret;
611}
612
613static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
614 u64 total_samples, int left_margin)
615{
616 struct callchain_list *chain;
617 bool printed = false;
618 int i = 0;
619 int ret = 0;
232a5c94 620 u32 entries_printed = 0;
4ecf84d0
ACM
621
622 list_for_each_entry(chain, &self->val, list) {
4ecf84d0
ACM
623 if (!i++ && sort__first_dimension == SORT_SYM)
624 continue;
625
626 if (!printed) {
627 ret += callchain__fprintf_left_margin(fp, left_margin);
628 ret += fprintf(fp, "|\n");
629 ret += callchain__fprintf_left_margin(fp, left_margin);
630 ret += fprintf(fp, "---");
631
632 left_margin += 3;
633 printed = true;
634 } else
635 ret += callchain__fprintf_left_margin(fp, left_margin);
636
b3c9ac08
ACM
637 if (chain->ms.sym)
638 ret += fprintf(fp, " %s\n", chain->ms.sym->name);
4ecf84d0
ACM
639 else
640 ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
232a5c94
ACM
641
642 if (++entries_printed == callchain_param.print_limit)
643 break;
4ecf84d0
ACM
644 }
645
646 ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin);
647
648 return ret;
649}
650
651static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
652 u64 total_samples)
653{
654 struct callchain_list *chain;
655 size_t ret = 0;
656
657 if (!self)
658 return 0;
659
660 ret += callchain__fprintf_flat(fp, self->parent, total_samples);
661
662
663 list_for_each_entry(chain, &self->val, list) {
664 if (chain->ip >= PERF_CONTEXT_MAX)
665 continue;
b3c9ac08
ACM
666 if (chain->ms.sym)
667 ret += fprintf(fp, " %s\n", chain->ms.sym->name);
4ecf84d0
ACM
668 else
669 ret += fprintf(fp, " %p\n",
670 (void *)(long)chain->ip);
671 }
672
673 return ret;
674}
675
676static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
677 u64 total_samples, int left_margin)
678{
679 struct rb_node *rb_node;
680 struct callchain_node *chain;
681 size_t ret = 0;
232a5c94 682 u32 entries_printed = 0;
4ecf84d0
ACM
683
684 rb_node = rb_first(&self->sorted_chain);
685 while (rb_node) {
686 double percent;
687
688 chain = rb_entry(rb_node, struct callchain_node, rb_node);
689 percent = chain->hit * 100.0 / total_samples;
690 switch (callchain_param.mode) {
691 case CHAIN_FLAT:
692 ret += percent_color_fprintf(fp, " %6.2f%%\n",
693 percent);
694 ret += callchain__fprintf_flat(fp, chain, total_samples);
695 break;
696 case CHAIN_GRAPH_ABS: /* Falldown */
697 case CHAIN_GRAPH_REL:
698 ret += callchain__fprintf_graph(fp, chain, total_samples,
699 left_margin);
700 case CHAIN_NONE:
701 default:
702 break;
703 }
704 ret += fprintf(fp, "\n");
232a5c94
ACM
705 if (++entries_printed == callchain_param.print_limit)
706 break;
4ecf84d0
ACM
707 rb_node = rb_next(rb_node);
708 }
709
710 return ret;
711}
712
ab81f3fd
ACM
713void hists__output_recalc_col_len(struct hists *hists, int max_rows)
714{
715 struct rb_node *next = rb_first(&hists->entries);
716 struct hist_entry *n;
717 int row = 0;
718
719 hists__reset_col_len(hists);
720
721 while (next && row++ < max_rows) {
722 n = rb_entry(next, struct hist_entry, rb_node);
723 hists__calc_col_len(hists, n);
724 next = rb_next(&n->rb_node);
725 }
726}
727
f1cf602c
ACM
728static int hist_entry__pcnt_snprintf(struct hist_entry *self, char *s,
729 size_t size, struct hists *pair_hists,
730 bool show_displacement, long displacement,
731 bool color, u64 session_total)
4ecf84d0 732{
c82ee828 733 u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us;
fec9cbd1 734 u64 nr_events;
c351c281 735 const char *sep = symbol_conf.field_sep;
a4e3b956 736 int ret;
4ecf84d0
ACM
737
738 if (symbol_conf.exclude_other && !self->parent)
739 return 0;
740
1c02c4d2 741 if (pair_hists) {
c82ee828 742 period = self->pair ? self->pair->period : 0;
fec9cbd1 743 nr_events = self->pair ? self->pair->nr_events : 0;
cee75ac7 744 total = pair_hists->stats.total_period;
c82ee828
ACM
745 period_sys = self->pair ? self->pair->period_sys : 0;
746 period_us = self->pair ? self->pair->period_us : 0;
747 period_guest_sys = self->pair ? self->pair->period_guest_sys : 0;
748 period_guest_us = self->pair ? self->pair->period_guest_us : 0;
c351c281 749 } else {
c82ee828 750 period = self->period;
fec9cbd1 751 nr_events = self->nr_events;
eefc465c 752 total = session_total;
c82ee828
ACM
753 period_sys = self->period_sys;
754 period_us = self->period_us;
755 period_guest_sys = self->period_guest_sys;
756 period_guest_us = self->period_guest_us;
c351c281
ACM
757 }
758
a4e3b956
ACM
759 if (total) {
760 if (color)
761 ret = percent_color_snprintf(s, size,
762 sep ? "%.2f" : " %6.2f%%",
c82ee828 763 (period * 100.0) / total);
a4e3b956
ACM
764 else
765 ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%",
c82ee828 766 (period * 100.0) / total);
a1645ce1
ZY
767 if (symbol_conf.show_cpu_utilization) {
768 ret += percent_color_snprintf(s + ret, size - ret,
769 sep ? "%.2f" : " %6.2f%%",
c82ee828 770 (period_sys * 100.0) / total);
a1645ce1
ZY
771 ret += percent_color_snprintf(s + ret, size - ret,
772 sep ? "%.2f" : " %6.2f%%",
c82ee828 773 (period_us * 100.0) / total);
a1645ce1
ZY
774 if (perf_guest) {
775 ret += percent_color_snprintf(s + ret,
776 size - ret,
777 sep ? "%.2f" : " %6.2f%%",
c82ee828 778 (period_guest_sys * 100.0) /
a1645ce1
ZY
779 total);
780 ret += percent_color_snprintf(s + ret,
781 size - ret,
782 sep ? "%.2f" : " %6.2f%%",
c82ee828 783 (period_guest_us * 100.0) /
a1645ce1
ZY
784 total);
785 }
786 }
a4e3b956 787 } else
9486aa38 788 ret = snprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period);
4ecf84d0
ACM
789
790 if (symbol_conf.show_nr_samples) {
c351c281 791 if (sep)
fec9cbd1 792 ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events);
4ecf84d0 793 else
fec9cbd1 794 ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events);
c351c281
ACM
795 }
796
3f2728bd
ACM
797 if (symbol_conf.show_total_period) {
798 if (sep)
799 ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period);
800 else
801 ret += snprintf(s + ret, size - ret, " %12" PRIu64, period);
802 }
803
1c02c4d2 804 if (pair_hists) {
c351c281
ACM
805 char bf[32];
806 double old_percent = 0, new_percent = 0, diff;
807
808 if (total > 0)
c82ee828 809 old_percent = (period * 100.0) / total;
eefc465c 810 if (session_total > 0)
c82ee828 811 new_percent = (self->period * 100.0) / session_total;
c351c281 812
9b33827d 813 diff = new_percent - old_percent;
c351c281 814
9b33827d 815 if (fabs(diff) >= 0.01)
c351c281
ACM
816 snprintf(bf, sizeof(bf), "%+4.2F%%", diff);
817 else
818 snprintf(bf, sizeof(bf), " ");
819
820 if (sep)
a4e3b956 821 ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
c351c281 822 else
a4e3b956 823 ret += snprintf(s + ret, size - ret, "%11.11s", bf);
c351c281
ACM
824
825 if (show_displacement) {
826 if (displacement)
827 snprintf(bf, sizeof(bf), "%+4ld", displacement);
828 else
829 snprintf(bf, sizeof(bf), " ");
830
831 if (sep)
a4e3b956 832 ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
c351c281 833 else
a4e3b956 834 ret += snprintf(s + ret, size - ret, "%6.6s", bf);
c351c281 835 }
4ecf84d0
ACM
836 }
837
f1cf602c
ACM
838 return ret;
839}
840
841int hist_entry__snprintf(struct hist_entry *he, char *s, size_t size,
842 struct hists *hists)
843{
844 const char *sep = symbol_conf.field_sep;
845 struct sort_entry *se;
846 int ret = 0;
847
4ecf84d0
ACM
848 list_for_each_entry(se, &hist_entry__sort_list, list) {
849 if (se->elide)
850 continue;
851
a4e3b956 852 ret += snprintf(s + ret, size - ret, "%s", sep ?: " ");
f1cf602c 853 ret += se->se_snprintf(he, s + ret, size - ret,
8a6c5b26 854 hists__col_len(hists, se->se_width_idx));
4ecf84d0
ACM
855 }
856
a4e3b956
ACM
857 return ret;
858}
859
ef9dfe6e 860int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists,
8a6c5b26
ACM
861 struct hists *pair_hists, bool show_displacement,
862 long displacement, FILE *fp, u64 session_total)
a4e3b956
ACM
863{
864 char bf[512];
f1cf602c 865 int ret;
ef9dfe6e
ACM
866
867 if (size == 0 || size > sizeof(bf))
868 size = sizeof(bf);
869
f1cf602c
ACM
870 ret = hist_entry__pcnt_snprintf(he, bf, size, pair_hists,
871 show_displacement, displacement,
872 true, session_total);
873 hist_entry__snprintf(he, bf + ret, size - ret, hists);
a4e3b956 874 return fprintf(fp, "%s\n", bf);
3997d377 875}
4ecf84d0 876
8a6c5b26
ACM
877static size_t hist_entry__fprintf_callchain(struct hist_entry *self,
878 struct hists *hists, FILE *fp,
3997d377
ACM
879 u64 session_total)
880{
881 int left_margin = 0;
4ecf84d0 882
3997d377
ACM
883 if (sort__first_dimension == SORT_COMM) {
884 struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
885 typeof(*se), list);
8a6c5b26 886 left_margin = hists__col_len(hists, se->se_width_idx);
3997d377 887 left_margin -= thread__comm_len(self->thread);
4ecf84d0
ACM
888 }
889
3997d377
ACM
890 return hist_entry_callchain__fprintf(fp, self, session_total,
891 left_margin);
4ecf84d0
ACM
892}
893
42b28ac0 894size_t hists__fprintf(struct hists *hists, struct hists *pair,
ef9dfe6e
ACM
895 bool show_displacement, bool show_header, int max_rows,
896 int max_cols, FILE *fp)
4ecf84d0 897{
4ecf84d0
ACM
898 struct sort_entry *se;
899 struct rb_node *nd;
900 size_t ret = 0;
c351c281
ACM
901 unsigned long position = 1;
902 long displacement = 0;
4ecf84d0 903 unsigned int width;
c351c281 904 const char *sep = symbol_conf.field_sep;
edb7c60e 905 const char *col_width = symbol_conf.col_width_list_str;
ef9dfe6e 906 int nr_rows = 0;
4ecf84d0
ACM
907
908 init_rem_hits();
909
ef9dfe6e
ACM
910 if (!show_header)
911 goto print_entries;
912
c351c281
ACM
913 fprintf(fp, "# %s", pair ? "Baseline" : "Overhead");
914
4ecf84d0 915 if (symbol_conf.show_nr_samples) {
c351c281
ACM
916 if (sep)
917 fprintf(fp, "%cSamples", *sep);
4ecf84d0
ACM
918 else
919 fputs(" Samples ", fp);
920 }
c351c281 921
3f2728bd
ACM
922 if (symbol_conf.show_total_period) {
923 if (sep)
924 ret += fprintf(fp, "%cPeriod", *sep);
925 else
926 ret += fprintf(fp, " Period ");
927 }
928
a1645ce1
ZY
929 if (symbol_conf.show_cpu_utilization) {
930 if (sep) {
931 ret += fprintf(fp, "%csys", *sep);
932 ret += fprintf(fp, "%cus", *sep);
933 if (perf_guest) {
934 ret += fprintf(fp, "%cguest sys", *sep);
935 ret += fprintf(fp, "%cguest us", *sep);
936 }
937 } else {
938 ret += fprintf(fp, " sys ");
939 ret += fprintf(fp, " us ");
940 if (perf_guest) {
941 ret += fprintf(fp, " guest sys ");
942 ret += fprintf(fp, " guest us ");
943 }
944 }
945 }
946
c351c281
ACM
947 if (pair) {
948 if (sep)
949 ret += fprintf(fp, "%cDelta", *sep);
950 else
951 ret += fprintf(fp, " Delta ");
952
953 if (show_displacement) {
954 if (sep)
955 ret += fprintf(fp, "%cDisplacement", *sep);
956 else
957 ret += fprintf(fp, " Displ");
958 }
959 }
960
4ecf84d0
ACM
961 list_for_each_entry(se, &hist_entry__sort_list, list) {
962 if (se->elide)
963 continue;
c351c281 964 if (sep) {
fcd14984 965 fprintf(fp, "%c%s", *sep, se->se_header);
4ecf84d0
ACM
966 continue;
967 }
fcd14984 968 width = strlen(se->se_header);
8a6c5b26
ACM
969 if (symbol_conf.col_width_list_str) {
970 if (col_width) {
42b28ac0 971 hists__set_col_len(hists, se->se_width_idx,
8a6c5b26
ACM
972 atoi(col_width));
973 col_width = strchr(col_width, ',');
974 if (col_width)
975 ++col_width;
4ecf84d0 976 }
4ecf84d0 977 }
42b28ac0
ACM
978 if (!hists__new_col_len(hists, se->se_width_idx, width))
979 width = hists__col_len(hists, se->se_width_idx);
fcd14984 980 fprintf(fp, " %*s", width, se->se_header);
4ecf84d0 981 }
ef9dfe6e 982
4ecf84d0 983 fprintf(fp, "\n");
ef9dfe6e
ACM
984 if (max_rows && ++nr_rows >= max_rows)
985 goto out;
4ecf84d0 986
c351c281 987 if (sep)
4ecf84d0
ACM
988 goto print_entries;
989
990 fprintf(fp, "# ........");
991 if (symbol_conf.show_nr_samples)
992 fprintf(fp, " ..........");
3f2728bd
ACM
993 if (symbol_conf.show_total_period)
994 fprintf(fp, " ............");
c351c281
ACM
995 if (pair) {
996 fprintf(fp, " ..........");
997 if (show_displacement)
998 fprintf(fp, " .....");
999 }
4ecf84d0
ACM
1000 list_for_each_entry(se, &hist_entry__sort_list, list) {
1001 unsigned int i;
1002
1003 if (se->elide)
1004 continue;
1005
1006 fprintf(fp, " ");
42b28ac0 1007 width = hists__col_len(hists, se->se_width_idx);
8a6c5b26 1008 if (width == 0)
fcd14984 1009 width = strlen(se->se_header);
4ecf84d0
ACM
1010 for (i = 0; i < width; i++)
1011 fprintf(fp, ".");
1012 }
4ecf84d0 1013
ef9dfe6e
ACM
1014 fprintf(fp, "\n");
1015 if (max_rows && ++nr_rows >= max_rows)
1016 goto out;
1017
1018 fprintf(fp, "#\n");
1019 if (max_rows && ++nr_rows >= max_rows)
1020 goto out;
4ecf84d0
ACM
1021
1022print_entries:
42b28ac0 1023 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
c351c281
ACM
1024 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1025
e84d2122
FW
1026 if (h->filtered)
1027 continue;
1028
c351c281
ACM
1029 if (show_displacement) {
1030 if (h->pair != NULL)
1031 displacement = ((long)h->pair->position -
1032 (long)position);
1033 else
1034 displacement = 0;
1035 ++position;
1036 }
ef9dfe6e 1037 ret += hist_entry__fprintf(h, max_cols, hists, pair, show_displacement,
42b28ac0 1038 displacement, fp, hists->stats.total_period);
3997d377
ACM
1039
1040 if (symbol_conf.use_callchain)
42b28ac0
ACM
1041 ret += hist_entry__fprintf_callchain(h, hists, fp,
1042 hists->stats.total_period);
ef9dfe6e
ACM
1043 if (max_rows && ++nr_rows >= max_rows)
1044 goto out;
1045
59fd5306 1046 if (h->ms.map == NULL && verbose > 1) {
65f2ed2b 1047 __map_groups__fprintf_maps(&h->thread->mg,
c6e718ff 1048 MAP__FUNCTION, verbose, fp);
65f2ed2b
ACM
1049 fprintf(fp, "%.10s end\n", graph_dotted_line);
1050 }
4ecf84d0 1051 }
ef9dfe6e 1052out:
4ecf84d0
ACM
1053 free(rem_sq_bracket);
1054
1055 return ret;
1056}
b09e0190 1057
06daaaba
ACM
1058/*
1059 * See hists__fprintf to match the column widths
1060 */
42b28ac0 1061unsigned int hists__sort_list_width(struct hists *hists)
06daaaba
ACM
1062{
1063 struct sort_entry *se;
1064 int ret = 9; /* total % */
1065
1066 if (symbol_conf.show_cpu_utilization) {
1067 ret += 7; /* count_sys % */
1068 ret += 6; /* count_us % */
1069 if (perf_guest) {
1070 ret += 13; /* count_guest_sys % */
1071 ret += 12; /* count_guest_us % */
1072 }
1073 }
1074
1075 if (symbol_conf.show_nr_samples)
1076 ret += 11;
1077
3f2728bd
ACM
1078 if (symbol_conf.show_total_period)
1079 ret += 13;
1080
06daaaba
ACM
1081 list_for_each_entry(se, &hist_entry__sort_list, list)
1082 if (!se->elide)
42b28ac0 1083 ret += 2 + hists__col_len(hists, se->se_width_idx);
06daaaba 1084
903cce6e
ACM
1085 if (verbose) /* Addr + origin */
1086 ret += 3 + BITS_PER_LONG / 4;
1087
06daaaba
ACM
1088 return ret;
1089}
1090
42b28ac0 1091static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h,
cc5edb0e
ACM
1092 enum hist_filter filter)
1093{
1094 h->filtered &= ~(1 << filter);
1095 if (h->filtered)
1096 return;
1097
42b28ac0 1098 ++hists->nr_entries;
0f0cbf7a 1099 if (h->ms.unfolded)
42b28ac0 1100 hists->nr_entries += h->nr_rows;
0f0cbf7a 1101 h->row_offset = 0;
42b28ac0
ACM
1102 hists->stats.total_period += h->period;
1103 hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
cc5edb0e 1104
42b28ac0 1105 hists__calc_col_len(hists, h);
cc5edb0e
ACM
1106}
1107
90cf1fb5
ACM
1108
1109static bool hists__filter_entry_by_dso(struct hists *hists,
1110 struct hist_entry *he)
1111{
1112 if (hists->dso_filter != NULL &&
1113 (he->ms.map == NULL || he->ms.map->dso != hists->dso_filter)) {
1114 he->filtered |= (1 << HIST_FILTER__DSO);
1115 return true;
1116 }
1117
1118 return false;
1119}
1120
d7b76f09 1121void hists__filter_by_dso(struct hists *hists)
b09e0190
ACM
1122{
1123 struct rb_node *nd;
1124
42b28ac0
ACM
1125 hists->nr_entries = hists->stats.total_period = 0;
1126 hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
1127 hists__reset_col_len(hists);
b09e0190 1128
42b28ac0 1129 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
b09e0190
ACM
1130 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1131
1132 if (symbol_conf.exclude_other && !h->parent)
1133 continue;
1134
90cf1fb5 1135 if (hists__filter_entry_by_dso(hists, h))
b09e0190 1136 continue;
b09e0190 1137
42b28ac0 1138 hists__remove_entry_filter(hists, h, HIST_FILTER__DSO);
b09e0190
ACM
1139 }
1140}
1141
90cf1fb5
ACM
1142static bool hists__filter_entry_by_thread(struct hists *hists,
1143 struct hist_entry *he)
1144{
1145 if (hists->thread_filter != NULL &&
1146 he->thread != hists->thread_filter) {
1147 he->filtered |= (1 << HIST_FILTER__THREAD);
1148 return true;
1149 }
1150
1151 return false;
1152}
1153
d7b76f09 1154void hists__filter_by_thread(struct hists *hists)
b09e0190
ACM
1155{
1156 struct rb_node *nd;
1157
42b28ac0
ACM
1158 hists->nr_entries = hists->stats.total_period = 0;
1159 hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
1160 hists__reset_col_len(hists);
b09e0190 1161
42b28ac0 1162 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
b09e0190
ACM
1163 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1164
90cf1fb5 1165 if (hists__filter_entry_by_thread(hists, h))
b09e0190 1166 continue;
cc5edb0e 1167
42b28ac0 1168 hists__remove_entry_filter(hists, h, HIST_FILTER__THREAD);
b09e0190
ACM
1169 }
1170}
ef7b93a1 1171
2f525d01 1172int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip)
ef7b93a1 1173{
2f525d01 1174 return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip);
ef7b93a1
ACM
1175}
1176
ce6f4fab 1177int hist_entry__annotate(struct hist_entry *he, size_t privsize)
ef7b93a1 1178{
ce6f4fab 1179 return symbol__annotate(he->ms.sym, he->ms.map, privsize);
ef7b93a1 1180}
c8446b9b 1181
42b28ac0 1182void hists__inc_nr_events(struct hists *hists, u32 type)
c8446b9b 1183{
42b28ac0
ACM
1184 ++hists->stats.nr_events[0];
1185 ++hists->stats.nr_events[type];
c8446b9b
ACM
1186}
1187
42b28ac0 1188size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)
c8446b9b
ACM
1189{
1190 int i;
1191 size_t ret = 0;
1192
1193 for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
e248de33 1194 const char *name;
3835bc00 1195
42b28ac0 1196 if (hists->stats.nr_events[i] == 0)
e248de33
ACM
1197 continue;
1198
1199 name = perf_event__name(i);
3835bc00 1200 if (!strcmp(name, "UNKNOWN"))
c8446b9b 1201 continue;
3835bc00
TG
1202
1203 ret += fprintf(fp, "%16s events: %10d\n", name,
42b28ac0 1204 hists->stats.nr_events[i]);
c8446b9b
ACM
1205 }
1206
1207 return ret;
1208}
1980c2eb
ACM
1209
1210void hists__init(struct hists *hists)
1211{
1212 memset(hists, 0, sizeof(*hists));
1213 hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT;
1214 hists->entries_in = &hists->entries_in_array[0];
1215 hists->entries_collapsed = RB_ROOT;
1216 hists->entries = RB_ROOT;
1217 pthread_mutex_init(&hists->lock, NULL);
1218}
This page took 0.323763 seconds and 5 git commands to generate.