6 struct callchain_param callchain_param
= {
7 .mode
= CHAIN_GRAPH_REL
,
12 * histogram, sorted on item, collects counts
15 struct hist_entry
*__perf_session__add_hist_entry(struct rb_root
*hists
,
16 struct addr_location
*al
,
17 struct symbol
*sym_parent
,
20 struct rb_node
**p
= &hists
->rb_node
;
21 struct rb_node
*parent
= NULL
;
22 struct hist_entry
*he
;
23 struct hist_entry entry
= {
36 he
= rb_entry(parent
, struct hist_entry
, rb_node
);
38 cmp
= hist_entry__cmp(&entry
, he
);
51 he
= malloc(sizeof(*he
));
55 rb_link_node(&he
->rb_node
, parent
, p
);
56 rb_insert_color(&he
->rb_node
, hists
);
62 hist_entry__cmp(struct hist_entry
*left
, struct hist_entry
*right
)
64 struct sort_entry
*se
;
67 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
68 cmp
= se
->cmp(left
, right
);
77 hist_entry__collapse(struct hist_entry
*left
, struct hist_entry
*right
)
79 struct sort_entry
*se
;
82 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
83 int64_t (*f
)(struct hist_entry
*, struct hist_entry
*);
85 f
= se
->collapse
?: se
->cmp
;
95 void hist_entry__free(struct hist_entry
*he
)
101 * collapse the histogram
104 static void collapse__insert_entry(struct rb_root
*root
, struct hist_entry
*he
)
106 struct rb_node
**p
= &root
->rb_node
;
107 struct rb_node
*parent
= NULL
;
108 struct hist_entry
*iter
;
113 iter
= rb_entry(parent
, struct hist_entry
, rb_node
);
115 cmp
= hist_entry__collapse(iter
, he
);
118 iter
->count
+= he
->count
;
119 hist_entry__free(he
);
129 rb_link_node(&he
->rb_node
, parent
, p
);
130 rb_insert_color(&he
->rb_node
, root
);
133 void perf_session__collapse_resort(struct rb_root
*hists
)
136 struct rb_node
*next
;
137 struct hist_entry
*n
;
139 if (!sort__need_collapse
)
143 next
= rb_first(hists
);
146 n
= rb_entry(next
, struct hist_entry
, rb_node
);
147 next
= rb_next(&n
->rb_node
);
149 rb_erase(&n
->rb_node
, hists
);
150 collapse__insert_entry(&tmp
, n
);
157 * reverse the map, sort on count.
160 static void perf_session__insert_output_hist_entry(struct rb_root
*root
,
161 struct hist_entry
*he
,
162 u64 min_callchain_hits
)
164 struct rb_node
**p
= &root
->rb_node
;
165 struct rb_node
*parent
= NULL
;
166 struct hist_entry
*iter
;
168 if (symbol_conf
.use_callchain
)
169 callchain_param
.sort(&he
->sorted_chain
, &he
->callchain
,
170 min_callchain_hits
, &callchain_param
);
174 iter
= rb_entry(parent
, struct hist_entry
, rb_node
);
176 if (he
->count
> iter
->count
)
182 rb_link_node(&he
->rb_node
, parent
, p
);
183 rb_insert_color(&he
->rb_node
, root
);
186 void perf_session__output_resort(struct rb_root
*hists
, u64 total_samples
)
189 struct rb_node
*next
;
190 struct hist_entry
*n
;
191 u64 min_callchain_hits
;
194 total_samples
* (callchain_param
.min_percent
/ 100);
197 next
= rb_first(hists
);
200 n
= rb_entry(next
, struct hist_entry
, rb_node
);
201 next
= rb_next(&n
->rb_node
);
203 rb_erase(&n
->rb_node
, hists
);
204 perf_session__insert_output_hist_entry(&tmp
, n
,
211 static size_t callchain__fprintf_left_margin(FILE *fp
, int left_margin
)
214 int ret
= fprintf(fp
, " ");
216 for (i
= 0; i
< left_margin
; i
++)
217 ret
+= fprintf(fp
, " ");
222 static size_t ipchain__fprintf_graph_line(FILE *fp
, int depth
, int depth_mask
,
226 size_t ret
= callchain__fprintf_left_margin(fp
, left_margin
);
228 for (i
= 0; i
< depth
; i
++)
229 if (depth_mask
& (1 << i
))
230 ret
+= fprintf(fp
, "| ");
232 ret
+= fprintf(fp
, " ");
234 ret
+= fprintf(fp
, "\n");
239 static size_t ipchain__fprintf_graph(FILE *fp
, struct callchain_list
*chain
,
240 int depth
, int depth_mask
, int count
,
241 u64 total_samples
, int hits
,
247 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
248 for (i
= 0; i
< depth
; i
++) {
249 if (depth_mask
& (1 << i
))
250 ret
+= fprintf(fp
, "|");
252 ret
+= fprintf(fp
, " ");
253 if (!count
&& i
== depth
- 1) {
256 percent
= hits
* 100.0 / total_samples
;
257 ret
+= percent_color_fprintf(fp
, "--%2.2f%%-- ", percent
);
259 ret
+= fprintf(fp
, "%s", " ");
262 ret
+= fprintf(fp
, "%s\n", chain
->sym
->name
);
264 ret
+= fprintf(fp
, "%p\n", (void *)(long)chain
->ip
);
269 static struct symbol
*rem_sq_bracket
;
270 static struct callchain_list rem_hits
;
272 static void init_rem_hits(void)
274 rem_sq_bracket
= malloc(sizeof(*rem_sq_bracket
) + 6);
275 if (!rem_sq_bracket
) {
276 fprintf(stderr
, "Not enough memory to display remaining hits\n");
280 strcpy(rem_sq_bracket
->name
, "[...]");
281 rem_hits
.sym
= rem_sq_bracket
;
284 static size_t __callchain__fprintf_graph(FILE *fp
, struct callchain_node
*self
,
285 u64 total_samples
, int depth
,
286 int depth_mask
, int left_margin
)
288 struct rb_node
*node
, *next
;
289 struct callchain_node
*child
;
290 struct callchain_list
*chain
;
291 int new_depth_mask
= depth_mask
;
297 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
298 new_total
= self
->children_hit
;
300 new_total
= total_samples
;
302 remaining
= new_total
;
304 node
= rb_first(&self
->rb_root
);
308 child
= rb_entry(node
, struct callchain_node
, rb_node
);
309 cumul
= cumul_hits(child
);
313 * The depth mask manages the output of pipes that show
314 * the depth. We don't want to keep the pipes of the current
315 * level for the last child of this depth.
316 * Except if we have remaining filtered hits. They will
317 * supersede the last child
319 next
= rb_next(node
);
320 if (!next
&& (callchain_param
.mode
!= CHAIN_GRAPH_REL
|| !remaining
))
321 new_depth_mask
&= ~(1 << (depth
- 1));
324 * But we keep the older depth mask for the line separator
325 * to keep the level link until we reach the last child
327 ret
+= ipchain__fprintf_graph_line(fp
, depth
, depth_mask
,
330 list_for_each_entry(chain
, &child
->val
, list
) {
331 ret
+= ipchain__fprintf_graph(fp
, chain
, depth
,
337 ret
+= __callchain__fprintf_graph(fp
, child
, new_total
,
339 new_depth_mask
| (1 << depth
),
344 if (callchain_param
.mode
== CHAIN_GRAPH_REL
&&
345 remaining
&& remaining
!= new_total
) {
350 new_depth_mask
&= ~(1 << (depth
- 1));
352 ret
+= ipchain__fprintf_graph(fp
, &rem_hits
, depth
,
353 new_depth_mask
, 0, new_total
,
354 remaining
, left_margin
);
360 static size_t callchain__fprintf_graph(FILE *fp
, struct callchain_node
*self
,
361 u64 total_samples
, int left_margin
)
363 struct callchain_list
*chain
;
364 bool printed
= false;
368 list_for_each_entry(chain
, &self
->val
, list
) {
369 if (!i
++ && sort__first_dimension
== SORT_SYM
)
373 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
374 ret
+= fprintf(fp
, "|\n");
375 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
376 ret
+= fprintf(fp
, "---");
381 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
384 ret
+= fprintf(fp
, " %s\n", chain
->sym
->name
);
386 ret
+= fprintf(fp
, " %p\n", (void *)(long)chain
->ip
);
389 ret
+= __callchain__fprintf_graph(fp
, self
, total_samples
, 1, 1, left_margin
);
394 static size_t callchain__fprintf_flat(FILE *fp
, struct callchain_node
*self
,
397 struct callchain_list
*chain
;
403 ret
+= callchain__fprintf_flat(fp
, self
->parent
, total_samples
);
406 list_for_each_entry(chain
, &self
->val
, list
) {
407 if (chain
->ip
>= PERF_CONTEXT_MAX
)
410 ret
+= fprintf(fp
, " %s\n", chain
->sym
->name
);
412 ret
+= fprintf(fp
, " %p\n",
413 (void *)(long)chain
->ip
);
419 static size_t hist_entry_callchain__fprintf(FILE *fp
, struct hist_entry
*self
,
420 u64 total_samples
, int left_margin
)
422 struct rb_node
*rb_node
;
423 struct callchain_node
*chain
;
426 rb_node
= rb_first(&self
->sorted_chain
);
430 chain
= rb_entry(rb_node
, struct callchain_node
, rb_node
);
431 percent
= chain
->hit
* 100.0 / total_samples
;
432 switch (callchain_param
.mode
) {
434 ret
+= percent_color_fprintf(fp
, " %6.2f%%\n",
436 ret
+= callchain__fprintf_flat(fp
, chain
, total_samples
);
438 case CHAIN_GRAPH_ABS
: /* Falldown */
439 case CHAIN_GRAPH_REL
:
440 ret
+= callchain__fprintf_graph(fp
, chain
, total_samples
,
446 ret
+= fprintf(fp
, "\n");
447 rb_node
= rb_next(rb_node
);
453 size_t hist_entry__fprintf(struct hist_entry
*self
,
454 struct perf_session
*pair_session
,
455 bool show_displacement
,
456 long displacement
, FILE *fp
,
459 struct sort_entry
*se
;
461 const char *sep
= symbol_conf
.field_sep
;
464 if (symbol_conf
.exclude_other
&& !self
->parent
)
468 count
= self
->pair
? self
->pair
->count
: 0;
469 total
= pair_session
->events_stats
.total
;
472 total
= session_total
;
476 ret
= percent_color_fprintf(fp
, sep
? "%.2f" : " %6.2f%%",
477 (count
* 100.0) / total
);
479 ret
= fprintf(fp
, sep
? "%lld" : "%12lld ", count
);
481 if (symbol_conf
.show_nr_samples
) {
483 ret
+= fprintf(fp
, "%c%lld", *sep
, count
);
485 ret
+= fprintf(fp
, "%11lld", count
);
490 double old_percent
= 0, new_percent
= 0, diff
;
493 old_percent
= (count
* 100.0) / total
;
494 if (session_total
> 0)
495 new_percent
= (self
->count
* 100.0) / session_total
;
497 diff
= new_percent
- old_percent
;
499 if (fabs(diff
) >= 0.01)
500 snprintf(bf
, sizeof(bf
), "%+4.2F%%", diff
);
502 snprintf(bf
, sizeof(bf
), " ");
505 ret
+= fprintf(fp
, "%c%s", *sep
, bf
);
507 ret
+= fprintf(fp
, "%11.11s", bf
);
509 if (show_displacement
) {
511 snprintf(bf
, sizeof(bf
), "%+4ld", displacement
);
513 snprintf(bf
, sizeof(bf
), " ");
516 ret
+= fprintf(fp
, "%c%s", *sep
, bf
);
518 ret
+= fprintf(fp
, "%6.6s", bf
);
522 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
526 ret
+= fprintf(fp
, "%s", sep
?: " ");
527 ret
+= se
->print(fp
, self
, se
->width
? *se
->width
: 0);
530 return ret
+ fprintf(fp
, "\n");
533 static size_t hist_entry__fprintf_callchain(struct hist_entry
*self
, FILE *fp
,
538 if (sort__first_dimension
== SORT_COMM
) {
539 struct sort_entry
*se
= list_first_entry(&hist_entry__sort_list
,
541 left_margin
= se
->width
? *se
->width
: 0;
542 left_margin
-= thread__comm_len(self
->thread
);
545 return hist_entry_callchain__fprintf(fp
, self
, session_total
,
549 size_t perf_session__fprintf_hists(struct rb_root
*hists
,
550 struct perf_session
*pair
,
551 bool show_displacement
, FILE *fp
,
554 struct sort_entry
*se
;
557 unsigned long position
= 1;
558 long displacement
= 0;
560 const char *sep
= symbol_conf
.field_sep
;
561 char *col_width
= symbol_conf
.col_width_list_str
;
565 fprintf(fp
, "# %s", pair
? "Baseline" : "Overhead");
567 if (symbol_conf
.show_nr_samples
) {
569 fprintf(fp
, "%cSamples", *sep
);
571 fputs(" Samples ", fp
);
576 ret
+= fprintf(fp
, "%cDelta", *sep
);
578 ret
+= fprintf(fp
, " Delta ");
580 if (show_displacement
) {
582 ret
+= fprintf(fp
, "%cDisplacement", *sep
);
584 ret
+= fprintf(fp
, " Displ");
588 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
592 fprintf(fp
, "%c%s", *sep
, se
->header
);
595 width
= strlen(se
->header
);
597 if (symbol_conf
.col_width_list_str
) {
599 *se
->width
= atoi(col_width
);
600 col_width
= strchr(col_width
, ',');
605 width
= *se
->width
= max(*se
->width
, width
);
607 fprintf(fp
, " %*s", width
, se
->header
);
614 fprintf(fp
, "# ........");
615 if (symbol_conf
.show_nr_samples
)
616 fprintf(fp
, " ..........");
618 fprintf(fp
, " ..........");
619 if (show_displacement
)
620 fprintf(fp
, " .....");
622 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
632 width
= strlen(se
->header
);
633 for (i
= 0; i
< width
; i
++)
637 fprintf(fp
, "\n#\n");
640 for (nd
= rb_first(hists
); nd
; nd
= rb_next(nd
)) {
641 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
643 if (show_displacement
) {
645 displacement
= ((long)h
->pair
->position
-
651 ret
+= hist_entry__fprintf(h
, pair
, show_displacement
,
652 displacement
, fp
, session_total
);
654 if (symbol_conf
.use_callchain
)
655 ret
+= hist_entry__fprintf_callchain(h
, fp
, session_total
);
657 if (h
->map
== NULL
&& verbose
> 1) {
658 __map_groups__fprintf_maps(&h
->thread
->mg
,
660 fprintf(fp
, "%.10s end\n", graph_dotted_line
);
664 free(rem_sq_bracket
);