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
);