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 perf_session
*self
,
16 struct addr_location
*al
,
17 struct symbol
*sym_parent
,
20 struct rb_node
**p
= &self
->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
, &self
->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 perf_session
*self
)
136 struct rb_node
*next
;
137 struct hist_entry
*n
;
139 if (!sort__need_collapse
)
143 next
= rb_first(&self
->hists
);
146 n
= rb_entry(next
, struct hist_entry
, rb_node
);
147 next
= rb_next(&n
->rb_node
);
149 rb_erase(&n
->rb_node
, &self
->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 perf_session
*self
, 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(&self
->hists
);
200 n
= rb_entry(next
, struct hist_entry
, rb_node
);
201 next
= rb_next(&n
->rb_node
);
203 rb_erase(&n
->rb_node
, &self
->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 seperator
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 if (chain
->ip
>= PERF_CONTEXT_MAX
)
333 ret
+= ipchain__fprintf_graph(fp
, chain
, depth
,
339 ret
+= __callchain__fprintf_graph(fp
, child
, new_total
,
341 new_depth_mask
| (1 << depth
),
346 if (callchain_param
.mode
== CHAIN_GRAPH_REL
&&
347 remaining
&& remaining
!= new_total
) {
352 new_depth_mask
&= ~(1 << (depth
- 1));
354 ret
+= ipchain__fprintf_graph(fp
, &rem_hits
, depth
,
355 new_depth_mask
, 0, new_total
,
356 remaining
, left_margin
);
362 static size_t callchain__fprintf_graph(FILE *fp
, struct callchain_node
*self
,
363 u64 total_samples
, int left_margin
)
365 struct callchain_list
*chain
;
366 bool printed
= false;
370 list_for_each_entry(chain
, &self
->val
, list
) {
371 if (chain
->ip
>= PERF_CONTEXT_MAX
)
374 if (!i
++ && sort__first_dimension
== SORT_SYM
)
378 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
379 ret
+= fprintf(fp
, "|\n");
380 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
381 ret
+= fprintf(fp
, "---");
386 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
389 ret
+= fprintf(fp
, " %s\n", chain
->sym
->name
);
391 ret
+= fprintf(fp
, " %p\n", (void *)(long)chain
->ip
);
394 ret
+= __callchain__fprintf_graph(fp
, self
, total_samples
, 1, 1, left_margin
);
399 static size_t callchain__fprintf_flat(FILE *fp
, struct callchain_node
*self
,
402 struct callchain_list
*chain
;
408 ret
+= callchain__fprintf_flat(fp
, self
->parent
, total_samples
);
411 list_for_each_entry(chain
, &self
->val
, list
) {
412 if (chain
->ip
>= PERF_CONTEXT_MAX
)
415 ret
+= fprintf(fp
, " %s\n", chain
->sym
->name
);
417 ret
+= fprintf(fp
, " %p\n",
418 (void *)(long)chain
->ip
);
424 static size_t hist_entry_callchain__fprintf(FILE *fp
, struct hist_entry
*self
,
425 u64 total_samples
, int left_margin
)
427 struct rb_node
*rb_node
;
428 struct callchain_node
*chain
;
431 rb_node
= rb_first(&self
->sorted_chain
);
435 chain
= rb_entry(rb_node
, struct callchain_node
, rb_node
);
436 percent
= chain
->hit
* 100.0 / total_samples
;
437 switch (callchain_param
.mode
) {
439 ret
+= percent_color_fprintf(fp
, " %6.2f%%\n",
441 ret
+= callchain__fprintf_flat(fp
, chain
, total_samples
);
443 case CHAIN_GRAPH_ABS
: /* Falldown */
444 case CHAIN_GRAPH_REL
:
445 ret
+= callchain__fprintf_graph(fp
, chain
, total_samples
,
451 ret
+= fprintf(fp
, "\n");
452 rb_node
= rb_next(rb_node
);
458 static size_t hist_entry__fprintf(struct hist_entry
*self
,
459 struct perf_session
*session
,
460 struct perf_session
*pair_session
,
461 bool show_displacement
,
462 long displacement
, FILE *fp
)
464 struct sort_entry
*se
;
466 const char *sep
= symbol_conf
.field_sep
;
469 if (symbol_conf
.exclude_other
&& !self
->parent
)
473 count
= self
->pair
? self
->pair
->count
: 0;
474 total
= pair_session
->events_stats
.total
;
477 total
= session
->events_stats
.total
;
481 ret
= percent_color_fprintf(fp
, sep
? "%.2f" : " %6.2f%%",
482 (count
* 100.0) / total
);
484 ret
= fprintf(fp
, sep
? "%lld" : "%12lld ", count
);
486 if (symbol_conf
.show_nr_samples
) {
488 fprintf(fp
, "%c%lld", *sep
, count
);
490 fprintf(fp
, "%11lld", count
);
495 double old_percent
= 0, new_percent
= 0, diff
;
498 old_percent
= (count
* 100.0) / total
;
499 if (session
->events_stats
.total
> 0)
500 new_percent
= (self
->count
* 100.0) / session
->events_stats
.total
;
502 diff
= new_percent
- old_percent
;
504 if (fabs(diff
) >= 0.01)
505 snprintf(bf
, sizeof(bf
), "%+4.2F%%", diff
);
507 snprintf(bf
, sizeof(bf
), " ");
510 ret
+= fprintf(fp
, "%c%s", *sep
, bf
);
512 ret
+= fprintf(fp
, "%11.11s", bf
);
514 if (show_displacement
) {
516 snprintf(bf
, sizeof(bf
), "%+4ld", displacement
);
518 snprintf(bf
, sizeof(bf
), " ");
521 fprintf(fp
, "%c%s", *sep
, bf
);
523 fprintf(fp
, "%6.6s", bf
);
527 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
531 fprintf(fp
, "%s", sep
?: " ");
532 ret
+= se
->print(fp
, self
, se
->width
? *se
->width
: 0);
535 ret
+= fprintf(fp
, "\n");
537 if (symbol_conf
.use_callchain
) {
540 if (sort__first_dimension
== SORT_COMM
) {
541 se
= list_first_entry(&hist_entry__sort_list
, typeof(*se
),
543 left_margin
= se
->width
? *se
->width
: 0;
544 left_margin
-= thread__comm_len(self
->thread
);
547 hist_entry_callchain__fprintf(fp
, self
, session
->events_stats
.total
,
554 size_t perf_session__fprintf_hists(struct perf_session
*self
,
555 struct perf_session
*pair
,
556 bool show_displacement
, FILE *fp
)
558 struct sort_entry
*se
;
561 unsigned long position
= 1;
562 long displacement
= 0;
564 const char *sep
= symbol_conf
.field_sep
;
565 char *col_width
= symbol_conf
.col_width_list_str
;
569 fprintf(fp
, "# %s", pair
? "Baseline" : "Overhead");
571 if (symbol_conf
.show_nr_samples
) {
573 fprintf(fp
, "%cSamples", *sep
);
575 fputs(" Samples ", fp
);
580 ret
+= fprintf(fp
, "%cDelta", *sep
);
582 ret
+= fprintf(fp
, " Delta ");
584 if (show_displacement
) {
586 ret
+= fprintf(fp
, "%cDisplacement", *sep
);
588 ret
+= fprintf(fp
, " Displ");
592 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
596 fprintf(fp
, "%c%s", *sep
, se
->header
);
599 width
= strlen(se
->header
);
601 if (symbol_conf
.col_width_list_str
) {
603 *se
->width
= atoi(col_width
);
604 col_width
= strchr(col_width
, ',');
609 width
= *se
->width
= max(*se
->width
, width
);
611 fprintf(fp
, " %*s", width
, se
->header
);
618 fprintf(fp
, "# ........");
619 if (symbol_conf
.show_nr_samples
)
620 fprintf(fp
, " ..........");
622 fprintf(fp
, " ..........");
623 if (show_displacement
)
624 fprintf(fp
, " .....");
626 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
636 width
= strlen(se
->header
);
637 for (i
= 0; i
< width
; i
++)
641 fprintf(fp
, "\n#\n");
644 for (nd
= rb_first(&self
->hists
); nd
; nd
= rb_next(nd
)) {
645 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
647 if (show_displacement
) {
649 displacement
= ((long)h
->pair
->position
-
655 ret
+= hist_entry__fprintf(h
, self
, pair
, show_displacement
,
659 free(rem_sq_bracket
);