5 struct callchain_param callchain_param
= {
6 .mode
= CHAIN_GRAPH_REL
,
11 * histogram, sorted on item, collects counts
14 struct hist_entry
*__perf_session__add_hist_entry(struct perf_session
*self
,
15 struct addr_location
*al
,
16 struct symbol
*sym_parent
,
19 struct rb_node
**p
= &self
->hists
.rb_node
;
20 struct rb_node
*parent
= NULL
;
21 struct hist_entry
*he
;
22 struct hist_entry entry
= {
35 he
= rb_entry(parent
, struct hist_entry
, rb_node
);
37 cmp
= hist_entry__cmp(&entry
, he
);
50 he
= malloc(sizeof(*he
));
54 rb_link_node(&he
->rb_node
, parent
, p
);
55 rb_insert_color(&he
->rb_node
, &self
->hists
);
61 hist_entry__cmp(struct hist_entry
*left
, struct hist_entry
*right
)
63 struct sort_entry
*se
;
66 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
67 cmp
= se
->cmp(left
, right
);
76 hist_entry__collapse(struct hist_entry
*left
, struct hist_entry
*right
)
78 struct sort_entry
*se
;
81 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
82 int64_t (*f
)(struct hist_entry
*, struct hist_entry
*);
84 f
= se
->collapse
?: se
->cmp
;
94 void hist_entry__free(struct hist_entry
*he
)
100 * collapse the histogram
103 static void collapse__insert_entry(struct rb_root
*root
, struct hist_entry
*he
)
105 struct rb_node
**p
= &root
->rb_node
;
106 struct rb_node
*parent
= NULL
;
107 struct hist_entry
*iter
;
112 iter
= rb_entry(parent
, struct hist_entry
, rb_node
);
114 cmp
= hist_entry__collapse(iter
, he
);
117 iter
->count
+= he
->count
;
118 hist_entry__free(he
);
128 rb_link_node(&he
->rb_node
, parent
, p
);
129 rb_insert_color(&he
->rb_node
, root
);
132 void perf_session__collapse_resort(struct perf_session
*self
)
135 struct rb_node
*next
;
136 struct hist_entry
*n
;
138 if (!sort__need_collapse
)
142 next
= rb_first(&self
->hists
);
145 n
= rb_entry(next
, struct hist_entry
, rb_node
);
146 next
= rb_next(&n
->rb_node
);
148 rb_erase(&n
->rb_node
, &self
->hists
);
149 collapse__insert_entry(&tmp
, n
);
156 * reverse the map, sort on count.
159 static void perf_session__insert_output_hist_entry(struct rb_root
*root
,
160 struct hist_entry
*he
,
161 u64 min_callchain_hits
)
163 struct rb_node
**p
= &root
->rb_node
;
164 struct rb_node
*parent
= NULL
;
165 struct hist_entry
*iter
;
167 if (symbol_conf
.use_callchain
)
168 callchain_param
.sort(&he
->sorted_chain
, &he
->callchain
,
169 min_callchain_hits
, &callchain_param
);
173 iter
= rb_entry(parent
, struct hist_entry
, rb_node
);
175 if (he
->count
> iter
->count
)
181 rb_link_node(&he
->rb_node
, parent
, p
);
182 rb_insert_color(&he
->rb_node
, root
);
185 void perf_session__output_resort(struct perf_session
*self
, u64 total_samples
)
188 struct rb_node
*next
;
189 struct hist_entry
*n
;
190 u64 min_callchain_hits
;
193 total_samples
* (callchain_param
.min_percent
/ 100);
196 next
= rb_first(&self
->hists
);
199 n
= rb_entry(next
, struct hist_entry
, rb_node
);
200 next
= rb_next(&n
->rb_node
);
202 rb_erase(&n
->rb_node
, &self
->hists
);
203 perf_session__insert_output_hist_entry(&tmp
, n
,
210 static size_t callchain__fprintf_left_margin(FILE *fp
, int left_margin
)
213 int ret
= fprintf(fp
, " ");
215 for (i
= 0; i
< left_margin
; i
++)
216 ret
+= fprintf(fp
, " ");
221 static size_t ipchain__fprintf_graph_line(FILE *fp
, int depth
, int depth_mask
,
225 size_t ret
= callchain__fprintf_left_margin(fp
, left_margin
);
227 for (i
= 0; i
< depth
; i
++)
228 if (depth_mask
& (1 << i
))
229 ret
+= fprintf(fp
, "| ");
231 ret
+= fprintf(fp
, " ");
233 ret
+= fprintf(fp
, "\n");
238 static size_t ipchain__fprintf_graph(FILE *fp
, struct callchain_list
*chain
,
239 int depth
, int depth_mask
, int count
,
240 u64 total_samples
, int hits
,
246 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
247 for (i
= 0; i
< depth
; i
++) {
248 if (depth_mask
& (1 << i
))
249 ret
+= fprintf(fp
, "|");
251 ret
+= fprintf(fp
, " ");
252 if (!count
&& i
== depth
- 1) {
255 percent
= hits
* 100.0 / total_samples
;
256 ret
+= percent_color_fprintf(fp
, "--%2.2f%%-- ", percent
);
258 ret
+= fprintf(fp
, "%s", " ");
261 ret
+= fprintf(fp
, "%s\n", chain
->sym
->name
);
263 ret
+= fprintf(fp
, "%p\n", (void *)(long)chain
->ip
);
268 static struct symbol
*rem_sq_bracket
;
269 static struct callchain_list rem_hits
;
271 static void init_rem_hits(void)
273 rem_sq_bracket
= malloc(sizeof(*rem_sq_bracket
) + 6);
274 if (!rem_sq_bracket
) {
275 fprintf(stderr
, "Not enough memory to display remaining hits\n");
279 strcpy(rem_sq_bracket
->name
, "[...]");
280 rem_hits
.sym
= rem_sq_bracket
;
283 static size_t __callchain__fprintf_graph(FILE *fp
, struct callchain_node
*self
,
284 u64 total_samples
, int depth
,
285 int depth_mask
, int left_margin
)
287 struct rb_node
*node
, *next
;
288 struct callchain_node
*child
;
289 struct callchain_list
*chain
;
290 int new_depth_mask
= depth_mask
;
296 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
297 new_total
= self
->children_hit
;
299 new_total
= total_samples
;
301 remaining
= new_total
;
303 node
= rb_first(&self
->rb_root
);
307 child
= rb_entry(node
, struct callchain_node
, rb_node
);
308 cumul
= cumul_hits(child
);
312 * The depth mask manages the output of pipes that show
313 * the depth. We don't want to keep the pipes of the current
314 * level for the last child of this depth.
315 * Except if we have remaining filtered hits. They will
316 * supersede the last child
318 next
= rb_next(node
);
319 if (!next
&& (callchain_param
.mode
!= CHAIN_GRAPH_REL
|| !remaining
))
320 new_depth_mask
&= ~(1 << (depth
- 1));
323 * But we keep the older depth mask for the line seperator
324 * to keep the level link until we reach the last child
326 ret
+= ipchain__fprintf_graph_line(fp
, depth
, depth_mask
,
329 list_for_each_entry(chain
, &child
->val
, list
) {
330 if (chain
->ip
>= PERF_CONTEXT_MAX
)
332 ret
+= ipchain__fprintf_graph(fp
, chain
, depth
,
338 ret
+= __callchain__fprintf_graph(fp
, child
, new_total
,
340 new_depth_mask
| (1 << depth
),
345 if (callchain_param
.mode
== CHAIN_GRAPH_REL
&&
346 remaining
&& remaining
!= new_total
) {
351 new_depth_mask
&= ~(1 << (depth
- 1));
353 ret
+= ipchain__fprintf_graph(fp
, &rem_hits
, depth
,
354 new_depth_mask
, 0, new_total
,
355 remaining
, left_margin
);
361 static size_t callchain__fprintf_graph(FILE *fp
, struct callchain_node
*self
,
362 u64 total_samples
, int left_margin
)
364 struct callchain_list
*chain
;
365 bool printed
= false;
369 list_for_each_entry(chain
, &self
->val
, list
) {
370 if (chain
->ip
>= PERF_CONTEXT_MAX
)
373 if (!i
++ && sort__first_dimension
== SORT_SYM
)
377 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
378 ret
+= fprintf(fp
, "|\n");
379 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
380 ret
+= fprintf(fp
, "---");
385 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
388 ret
+= fprintf(fp
, " %s\n", chain
->sym
->name
);
390 ret
+= fprintf(fp
, " %p\n", (void *)(long)chain
->ip
);
393 ret
+= __callchain__fprintf_graph(fp
, self
, total_samples
, 1, 1, left_margin
);
398 static size_t callchain__fprintf_flat(FILE *fp
, struct callchain_node
*self
,
401 struct callchain_list
*chain
;
407 ret
+= callchain__fprintf_flat(fp
, self
->parent
, total_samples
);
410 list_for_each_entry(chain
, &self
->val
, list
) {
411 if (chain
->ip
>= PERF_CONTEXT_MAX
)
414 ret
+= fprintf(fp
, " %s\n", chain
->sym
->name
);
416 ret
+= fprintf(fp
, " %p\n",
417 (void *)(long)chain
->ip
);
423 static size_t hist_entry_callchain__fprintf(FILE *fp
, struct hist_entry
*self
,
424 u64 total_samples
, int left_margin
)
426 struct rb_node
*rb_node
;
427 struct callchain_node
*chain
;
430 rb_node
= rb_first(&self
->sorted_chain
);
434 chain
= rb_entry(rb_node
, struct callchain_node
, rb_node
);
435 percent
= chain
->hit
* 100.0 / total_samples
;
436 switch (callchain_param
.mode
) {
438 ret
+= percent_color_fprintf(fp
, " %6.2f%%\n",
440 ret
+= callchain__fprintf_flat(fp
, chain
, total_samples
);
442 case CHAIN_GRAPH_ABS
: /* Falldown */
443 case CHAIN_GRAPH_REL
:
444 ret
+= callchain__fprintf_graph(fp
, chain
, total_samples
,
450 ret
+= fprintf(fp
, "\n");
451 rb_node
= rb_next(rb_node
);
457 static size_t hist_entry__fprintf(struct hist_entry
*self
,
458 struct perf_session
*session
,
459 struct perf_session
*pair_session
,
460 bool show_displacement
,
461 long displacement
, FILE *fp
)
463 struct sort_entry
*se
;
465 const char *sep
= symbol_conf
.field_sep
;
468 if (symbol_conf
.exclude_other
&& !self
->parent
)
472 count
= self
->pair
? self
->pair
->count
: 0;
473 total
= pair_session
->events_stats
.total
;
476 total
= session
->events_stats
.total
;
480 ret
= percent_color_fprintf(fp
, sep
? "%.2f" : " %6.2f%%",
481 (count
* 100.0) / total
);
483 ret
= fprintf(fp
, sep
? "%lld" : "%12lld ", count
);
485 if (symbol_conf
.show_nr_samples
) {
487 fprintf(fp
, "%c%lld", *sep
, count
);
489 fprintf(fp
, "%11lld", count
);
494 double old_percent
= 0, new_percent
= 0, diff
;
497 old_percent
= (count
* 100) / total
;
498 if (session
->events_stats
.total
> 0)
499 new_percent
= (self
->count
* 100) / session
->events_stats
.total
;
501 diff
= old_percent
- new_percent
;
504 snprintf(bf
, sizeof(bf
), "%+4.2F%%", diff
);
506 snprintf(bf
, sizeof(bf
), " ");
509 ret
+= fprintf(fp
, "%c%s", *sep
, bf
);
511 ret
+= fprintf(fp
, "%11.11s", bf
);
513 if (show_displacement
) {
515 snprintf(bf
, sizeof(bf
), "%+4ld", displacement
);
517 snprintf(bf
, sizeof(bf
), " ");
520 fprintf(fp
, "%c%s", *sep
, bf
);
522 fprintf(fp
, "%6.6s", bf
);
526 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
530 fprintf(fp
, "%s", sep
?: " ");
531 ret
+= se
->print(fp
, self
, se
->width
? *se
->width
: 0);
534 ret
+= fprintf(fp
, "\n");
536 if (symbol_conf
.use_callchain
) {
539 if (sort__first_dimension
== SORT_COMM
) {
540 se
= list_first_entry(&hist_entry__sort_list
, typeof(*se
),
542 left_margin
= se
->width
? *se
->width
: 0;
543 left_margin
-= thread__comm_len(self
->thread
);
546 hist_entry_callchain__fprintf(fp
, self
, session
->events_stats
.total
,
553 size_t perf_session__fprintf_hists(struct perf_session
*self
,
554 struct perf_session
*pair
,
555 bool show_displacement
, FILE *fp
)
557 struct sort_entry
*se
;
560 unsigned long position
= 1;
561 long displacement
= 0;
563 const char *sep
= symbol_conf
.field_sep
;
564 char *col_width
= symbol_conf
.col_width_list_str
;
568 fprintf(fp
, "# %s", pair
? "Baseline" : "Overhead");
570 if (symbol_conf
.show_nr_samples
) {
572 fprintf(fp
, "%cSamples", *sep
);
574 fputs(" Samples ", fp
);
579 ret
+= fprintf(fp
, "%cDelta", *sep
);
581 ret
+= fprintf(fp
, " Delta ");
583 if (show_displacement
) {
585 ret
+= fprintf(fp
, "%cDisplacement", *sep
);
587 ret
+= fprintf(fp
, " Displ");
591 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
595 fprintf(fp
, "%c%s", *sep
, se
->header
);
598 width
= strlen(se
->header
);
600 if (symbol_conf
.col_width_list_str
) {
602 *se
->width
= atoi(col_width
);
603 col_width
= strchr(col_width
, ',');
608 width
= *se
->width
= max(*se
->width
, width
);
610 fprintf(fp
, " %*s", width
, se
->header
);
617 fprintf(fp
, "# ........");
618 if (symbol_conf
.show_nr_samples
)
619 fprintf(fp
, " ..........");
621 fprintf(fp
, " ..........");
622 if (show_displacement
)
623 fprintf(fp
, " .....");
625 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
635 width
= strlen(se
->header
);
636 for (i
= 0; i
< width
; i
++)
640 fprintf(fp
, "\n#\n");
643 for (nd
= rb_first(&self
->hists
); nd
; nd
= rb_next(nd
)) {
644 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
646 if (show_displacement
) {
648 displacement
= ((long)h
->pair
->position
-
654 ret
+= hist_entry__fprintf(h
, self
, pair
, show_displacement
,
658 free(rem_sq_bracket
);