6 struct callchain_param callchain_param = {
7 .mode = CHAIN_GRAPH_REL,
11 static void perf_session__add_cpumode_count(struct hist_entry *he,
12 unsigned int cpumode, u64 count)
15 case PERF_RECORD_MISC_KERNEL:
16 he->count_sys += count;
18 case PERF_RECORD_MISC_USER:
19 he->count_us += count;
21 case PERF_RECORD_MISC_GUEST_KERNEL:
22 he->count_guest_sys += count;
24 case PERF_RECORD_MISC_GUEST_USER:
25 he->count_guest_us += count;
33 * histogram, sorted on item, collects counts
36 static struct hist_entry *hist_entry__new(struct hist_entry *template)
38 size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_node) : 0;
39 struct hist_entry *self = malloc(sizeof(*self) + callchain_size);
43 if (symbol_conf.use_callchain)
44 callchain_init(self->callchain);
50 struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists,
51 struct addr_location *al,
52 struct symbol *sym_parent,
55 struct rb_node **p = &hists->rb_node;
56 struct rb_node *parent = NULL;
57 struct hist_entry *he;
58 struct hist_entry entry = {
73 he = rb_entry(parent, struct hist_entry, rb_node);
75 cmp = hist_entry__cmp(&entry, he);
88 he = hist_entry__new(&entry);
91 rb_link_node(&he->rb_node, parent, p);
92 rb_insert_color(&he->rb_node, hists);
94 perf_session__add_cpumode_count(he, al->cpumode, count);
99 hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
101 struct sort_entry *se;
104 list_for_each_entry(se, &hist_entry__sort_list, list) {
105 cmp = se->se_cmp(left, right);
114 hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
116 struct sort_entry *se;
119 list_for_each_entry(se, &hist_entry__sort_list, list) {
120 int64_t (*f)(struct hist_entry *, struct hist_entry *);
122 f = se->se_collapse ?: se->se_cmp;
124 cmp = f(left, right);
132 void hist_entry__free(struct hist_entry *he)
138 * collapse the histogram
141 static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he)
143 struct rb_node **p = &root->rb_node;
144 struct rb_node *parent = NULL;
145 struct hist_entry *iter;
150 iter = rb_entry(parent, struct hist_entry, rb_node);
152 cmp = hist_entry__collapse(iter, he);
155 iter->count += he->count;
156 hist_entry__free(he);
166 rb_link_node(&he->rb_node, parent, p);
167 rb_insert_color(&he->rb_node, root);
170 void perf_session__collapse_resort(struct rb_root *hists)
173 struct rb_node *next;
174 struct hist_entry *n;
176 if (!sort__need_collapse)
180 next = rb_first(hists);
183 n = rb_entry(next, struct hist_entry, rb_node);
184 next = rb_next(&n->rb_node);
186 rb_erase(&n->rb_node, hists);
187 collapse__insert_entry(&tmp, n);
194 * reverse the map, sort on count.
197 static void perf_session__insert_output_hist_entry(struct rb_root *root,
198 struct hist_entry *he,
199 u64 min_callchain_hits)
201 struct rb_node **p = &root->rb_node;
202 struct rb_node *parent = NULL;
203 struct hist_entry *iter;
205 if (symbol_conf.use_callchain)
206 callchain_param.sort(&he->sorted_chain, he->callchain,
207 min_callchain_hits, &callchain_param);
211 iter = rb_entry(parent, struct hist_entry, rb_node);
213 if (he->count > iter->count)
219 rb_link_node(&he->rb_node, parent, p);
220 rb_insert_color(&he->rb_node, root);
223 u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples)
226 struct rb_node *next;
227 struct hist_entry *n;
228 u64 min_callchain_hits;
232 total_samples * (callchain_param.min_percent / 100);
235 next = rb_first(hists);
238 n = rb_entry(next, struct hist_entry, rb_node);
239 next = rb_next(&n->rb_node);
241 rb_erase(&n->rb_node, hists);
242 perf_session__insert_output_hist_entry(&tmp, n,
251 static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
254 int ret = fprintf(fp, " ");
256 for (i = 0; i < left_margin; i++)
257 ret += fprintf(fp, " ");
262 static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
266 size_t ret = callchain__fprintf_left_margin(fp, left_margin);
268 for (i = 0; i < depth; i++)
269 if (depth_mask & (1 << i))
270 ret += fprintf(fp, "| ");
272 ret += fprintf(fp, " ");
274 ret += fprintf(fp, "\n");
279 static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
280 int depth, int depth_mask, int count,
281 u64 total_samples, int hits,
287 ret += callchain__fprintf_left_margin(fp, left_margin);
288 for (i = 0; i < depth; i++) {
289 if (depth_mask & (1 << i))
290 ret += fprintf(fp, "|");
292 ret += fprintf(fp, " ");
293 if (!count && i == depth - 1) {
296 percent = hits * 100.0 / total_samples;
297 ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
299 ret += fprintf(fp, "%s", " ");
302 ret += fprintf(fp, "%s\n", chain->ms.sym->name);
304 ret += fprintf(fp, "%p\n", (void *)(long)chain->ip);
309 static struct symbol *rem_sq_bracket;
310 static struct callchain_list rem_hits;
312 static void init_rem_hits(void)
314 rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
315 if (!rem_sq_bracket) {
316 fprintf(stderr, "Not enough memory to display remaining hits\n");
320 strcpy(rem_sq_bracket->name, "[...]");
321 rem_hits.ms.sym = rem_sq_bracket;
324 static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
325 u64 total_samples, int depth,
326 int depth_mask, int left_margin)
328 struct rb_node *node, *next;
329 struct callchain_node *child;
330 struct callchain_list *chain;
331 int new_depth_mask = depth_mask;
336 uint entries_printed = 0;
338 if (callchain_param.mode == CHAIN_GRAPH_REL)
339 new_total = self->children_hit;
341 new_total = total_samples;
343 remaining = new_total;
345 node = rb_first(&self->rb_root);
349 child = rb_entry(node, struct callchain_node, rb_node);
350 cumul = cumul_hits(child);
354 * The depth mask manages the output of pipes that show
355 * the depth. We don't want to keep the pipes of the current
356 * level for the last child of this depth.
357 * Except if we have remaining filtered hits. They will
358 * supersede the last child
360 next = rb_next(node);
361 if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
362 new_depth_mask &= ~(1 << (depth - 1));
365 * But we keep the older depth mask for the line separator
366 * to keep the level link until we reach the last child
368 ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
371 list_for_each_entry(chain, &child->val, list) {
372 ret += ipchain__fprintf_graph(fp, chain, depth,
378 ret += __callchain__fprintf_graph(fp, child, new_total,
380 new_depth_mask | (1 << depth),
383 if (++entries_printed == callchain_param.print_limit)
387 if (callchain_param.mode == CHAIN_GRAPH_REL &&
388 remaining && remaining != new_total) {
393 new_depth_mask &= ~(1 << (depth - 1));
395 ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
396 new_depth_mask, 0, new_total,
397 remaining, left_margin);
403 static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
404 u64 total_samples, int left_margin)
406 struct callchain_list *chain;
407 bool printed = false;
410 u32 entries_printed = 0;
412 list_for_each_entry(chain, &self->val, list) {
413 if (!i++ && sort__first_dimension == SORT_SYM)
417 ret += callchain__fprintf_left_margin(fp, left_margin);
418 ret += fprintf(fp, "|\n");
419 ret += callchain__fprintf_left_margin(fp, left_margin);
420 ret += fprintf(fp, "---");
425 ret += callchain__fprintf_left_margin(fp, left_margin);
428 ret += fprintf(fp, " %s\n", chain->ms.sym->name);
430 ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
432 if (++entries_printed == callchain_param.print_limit)
436 ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin);
441 static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
444 struct callchain_list *chain;
450 ret += callchain__fprintf_flat(fp, self->parent, total_samples);
453 list_for_each_entry(chain, &self->val, list) {
454 if (chain->ip >= PERF_CONTEXT_MAX)
457 ret += fprintf(fp, " %s\n", chain->ms.sym->name);
459 ret += fprintf(fp, " %p\n",
460 (void *)(long)chain->ip);
466 static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
467 u64 total_samples, int left_margin)
469 struct rb_node *rb_node;
470 struct callchain_node *chain;
472 u32 entries_printed = 0;
474 rb_node = rb_first(&self->sorted_chain);
478 chain = rb_entry(rb_node, struct callchain_node, rb_node);
479 percent = chain->hit * 100.0 / total_samples;
480 switch (callchain_param.mode) {
482 ret += percent_color_fprintf(fp, " %6.2f%%\n",
484 ret += callchain__fprintf_flat(fp, chain, total_samples);
486 case CHAIN_GRAPH_ABS: /* Falldown */
487 case CHAIN_GRAPH_REL:
488 ret += callchain__fprintf_graph(fp, chain, total_samples,
494 ret += fprintf(fp, "\n");
495 if (++entries_printed == callchain_param.print_limit)
497 rb_node = rb_next(rb_node);
503 int hist_entry__snprintf(struct hist_entry *self,
504 char *s, size_t size,
505 struct perf_session *pair_session,
506 bool show_displacement,
507 long displacement, bool color,
510 struct sort_entry *se;
511 u64 count, total, count_sys, count_us, count_guest_sys, count_guest_us;
512 const char *sep = symbol_conf.field_sep;
515 if (symbol_conf.exclude_other && !self->parent)
519 count = self->pair ? self->pair->count : 0;
520 total = pair_session->events_stats.total;
521 count_sys = self->pair ? self->pair->count_sys : 0;
522 count_us = self->pair ? self->pair->count_us : 0;
523 count_guest_sys = self->pair ? self->pair->count_guest_sys : 0;
524 count_guest_us = self->pair ? self->pair->count_guest_us : 0;
527 total = session_total;
528 count_sys = self->count_sys;
529 count_us = self->count_us;
530 count_guest_sys = self->count_guest_sys;
531 count_guest_us = self->count_guest_us;
536 ret = percent_color_snprintf(s, size,
537 sep ? "%.2f" : " %6.2f%%",
538 (count * 100.0) / total);
540 ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%",
541 (count * 100.0) / total);
542 if (symbol_conf.show_cpu_utilization) {
543 ret += percent_color_snprintf(s + ret, size - ret,
544 sep ? "%.2f" : " %6.2f%%",
545 (count_sys * 100.0) / total);
546 ret += percent_color_snprintf(s + ret, size - ret,
547 sep ? "%.2f" : " %6.2f%%",
548 (count_us * 100.0) / total);
550 ret += percent_color_snprintf(s + ret,
552 sep ? "%.2f" : " %6.2f%%",
553 (count_guest_sys * 100.0) /
555 ret += percent_color_snprintf(s + ret,
557 sep ? "%.2f" : " %6.2f%%",
558 (count_guest_us * 100.0) /
563 ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count);
565 if (symbol_conf.show_nr_samples) {
567 ret += snprintf(s + ret, size - ret, "%c%lld", *sep, count);
569 ret += snprintf(s + ret, size - ret, "%11lld", count);
574 double old_percent = 0, new_percent = 0, diff;
577 old_percent = (count * 100.0) / total;
578 if (session_total > 0)
579 new_percent = (self->count * 100.0) / session_total;
581 diff = new_percent - old_percent;
583 if (fabs(diff) >= 0.01)
584 snprintf(bf, sizeof(bf), "%+4.2F%%", diff);
586 snprintf(bf, sizeof(bf), " ");
589 ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
591 ret += snprintf(s + ret, size - ret, "%11.11s", bf);
593 if (show_displacement) {
595 snprintf(bf, sizeof(bf), "%+4ld", displacement);
597 snprintf(bf, sizeof(bf), " ");
600 ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
602 ret += snprintf(s + ret, size - ret, "%6.6s", bf);
606 list_for_each_entry(se, &hist_entry__sort_list, list) {
610 ret += snprintf(s + ret, size - ret, "%s", sep ?: " ");
611 ret += se->se_snprintf(self, s + ret, size - ret,
612 se->se_width ? *se->se_width : 0);
618 int hist_entry__fprintf(struct hist_entry *self,
619 struct perf_session *pair_session,
620 bool show_displacement,
621 long displacement, FILE *fp,
625 hist_entry__snprintf(self, bf, sizeof(bf), pair_session,
626 show_displacement, displacement,
627 true, session_total);
628 return fprintf(fp, "%s\n", bf);
631 static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp,
636 if (sort__first_dimension == SORT_COMM) {
637 struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
639 left_margin = se->se_width ? *se->se_width : 0;
640 left_margin -= thread__comm_len(self->thread);
643 return hist_entry_callchain__fprintf(fp, self, session_total,
647 size_t perf_session__fprintf_hists(struct rb_root *hists,
648 struct perf_session *pair,
649 bool show_displacement, FILE *fp,
652 struct sort_entry *se;
655 unsigned long position = 1;
656 long displacement = 0;
658 const char *sep = symbol_conf.field_sep;
659 char *col_width = symbol_conf.col_width_list_str;
663 fprintf(fp, "# %s", pair ? "Baseline" : "Overhead");
665 if (symbol_conf.show_nr_samples) {
667 fprintf(fp, "%cSamples", *sep);
669 fputs(" Samples ", fp);
672 if (symbol_conf.show_cpu_utilization) {
674 ret += fprintf(fp, "%csys", *sep);
675 ret += fprintf(fp, "%cus", *sep);
677 ret += fprintf(fp, "%cguest sys", *sep);
678 ret += fprintf(fp, "%cguest us", *sep);
681 ret += fprintf(fp, " sys ");
682 ret += fprintf(fp, " us ");
684 ret += fprintf(fp, " guest sys ");
685 ret += fprintf(fp, " guest us ");
692 ret += fprintf(fp, "%cDelta", *sep);
694 ret += fprintf(fp, " Delta ");
696 if (show_displacement) {
698 ret += fprintf(fp, "%cDisplacement", *sep);
700 ret += fprintf(fp, " Displ");
704 list_for_each_entry(se, &hist_entry__sort_list, list) {
708 fprintf(fp, "%c%s", *sep, se->se_header);
711 width = strlen(se->se_header);
713 if (symbol_conf.col_width_list_str) {
715 *se->se_width = atoi(col_width);
716 col_width = strchr(col_width, ',');
721 width = *se->se_width = max(*se->se_width, width);
723 fprintf(fp, " %*s", width, se->se_header);
730 fprintf(fp, "# ........");
731 if (symbol_conf.show_nr_samples)
732 fprintf(fp, " ..........");
734 fprintf(fp, " ..........");
735 if (show_displacement)
736 fprintf(fp, " .....");
738 list_for_each_entry(se, &hist_entry__sort_list, list) {
746 width = *se->se_width;
748 width = strlen(se->se_header);
749 for (i = 0; i < width; i++)
753 fprintf(fp, "\n#\n");
756 for (nd = rb_first(hists); nd; nd = rb_next(nd)) {
757 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
759 if (show_displacement) {
761 displacement = ((long)h->pair->position -
767 ret += hist_entry__fprintf(h, pair, show_displacement,
768 displacement, fp, session_total);
770 if (symbol_conf.use_callchain)
771 ret += hist_entry__fprintf_callchain(h, fp, session_total);
773 if (h->ms.map == NULL && verbose > 1) {
774 __map_groups__fprintf_maps(&h->thread->mg,
775 MAP__FUNCTION, verbose, fp);
776 fprintf(fp, "%.10s end\n", graph_dotted_line);
780 free(rem_sq_bracket);