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
= {
38 he
= rb_entry(parent
, struct hist_entry
, rb_node
);
40 cmp
= hist_entry__cmp(&entry
, he
);
53 he
= malloc(sizeof(*he
) + (symbol_conf
.use_callchain
?
54 sizeof(struct callchain_node
) : 0));
58 rb_link_node(&he
->rb_node
, parent
, p
);
59 rb_insert_color(&he
->rb_node
, hists
);
65 hist_entry__cmp(struct hist_entry
*left
, struct hist_entry
*right
)
67 struct sort_entry
*se
;
70 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
71 cmp
= se
->cmp(left
, right
);
80 hist_entry__collapse(struct hist_entry
*left
, struct hist_entry
*right
)
82 struct sort_entry
*se
;
85 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
86 int64_t (*f
)(struct hist_entry
*, struct hist_entry
*);
88 f
= se
->collapse
?: se
->cmp
;
98 void hist_entry__free(struct hist_entry
*he
)
104 * collapse the histogram
107 static void collapse__insert_entry(struct rb_root
*root
, struct hist_entry
*he
)
109 struct rb_node
**p
= &root
->rb_node
;
110 struct rb_node
*parent
= NULL
;
111 struct hist_entry
*iter
;
116 iter
= rb_entry(parent
, struct hist_entry
, rb_node
);
118 cmp
= hist_entry__collapse(iter
, he
);
121 iter
->count
+= he
->count
;
122 hist_entry__free(he
);
132 rb_link_node(&he
->rb_node
, parent
, p
);
133 rb_insert_color(&he
->rb_node
, root
);
136 void perf_session__collapse_resort(struct rb_root
*hists
)
139 struct rb_node
*next
;
140 struct hist_entry
*n
;
142 if (!sort__need_collapse
)
146 next
= rb_first(hists
);
149 n
= rb_entry(next
, struct hist_entry
, rb_node
);
150 next
= rb_next(&n
->rb_node
);
152 rb_erase(&n
->rb_node
, hists
);
153 collapse__insert_entry(&tmp
, n
);
160 * reverse the map, sort on count.
163 static void perf_session__insert_output_hist_entry(struct rb_root
*root
,
164 struct hist_entry
*he
,
165 u64 min_callchain_hits
)
167 struct rb_node
**p
= &root
->rb_node
;
168 struct rb_node
*parent
= NULL
;
169 struct hist_entry
*iter
;
171 if (symbol_conf
.use_callchain
)
172 callchain_param
.sort(&he
->sorted_chain
, he
->callchain
,
173 min_callchain_hits
, &callchain_param
);
177 iter
= rb_entry(parent
, struct hist_entry
, rb_node
);
179 if (he
->count
> iter
->count
)
185 rb_link_node(&he
->rb_node
, parent
, p
);
186 rb_insert_color(&he
->rb_node
, root
);
189 u64
perf_session__output_resort(struct rb_root
*hists
, u64 total_samples
)
192 struct rb_node
*next
;
193 struct hist_entry
*n
;
194 u64 min_callchain_hits
;
198 total_samples
* (callchain_param
.min_percent
/ 100);
201 next
= rb_first(hists
);
204 n
= rb_entry(next
, struct hist_entry
, rb_node
);
205 next
= rb_next(&n
->rb_node
);
207 rb_erase(&n
->rb_node
, hists
);
208 perf_session__insert_output_hist_entry(&tmp
, n
,
217 static size_t callchain__fprintf_left_margin(FILE *fp
, int left_margin
)
220 int ret
= fprintf(fp
, " ");
222 for (i
= 0; i
< left_margin
; i
++)
223 ret
+= fprintf(fp
, " ");
228 static size_t ipchain__fprintf_graph_line(FILE *fp
, int depth
, int depth_mask
,
232 size_t ret
= callchain__fprintf_left_margin(fp
, left_margin
);
234 for (i
= 0; i
< depth
; i
++)
235 if (depth_mask
& (1 << i
))
236 ret
+= fprintf(fp
, "| ");
238 ret
+= fprintf(fp
, " ");
240 ret
+= fprintf(fp
, "\n");
245 static size_t ipchain__fprintf_graph(FILE *fp
, struct callchain_list
*chain
,
246 int depth
, int depth_mask
, int count
,
247 u64 total_samples
, int hits
,
253 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
254 for (i
= 0; i
< depth
; i
++) {
255 if (depth_mask
& (1 << i
))
256 ret
+= fprintf(fp
, "|");
258 ret
+= fprintf(fp
, " ");
259 if (!count
&& i
== depth
- 1) {
262 percent
= hits
* 100.0 / total_samples
;
263 ret
+= percent_color_fprintf(fp
, "--%2.2f%%-- ", percent
);
265 ret
+= fprintf(fp
, "%s", " ");
268 ret
+= fprintf(fp
, "%s\n", chain
->ms
.sym
->name
);
270 ret
+= fprintf(fp
, "%p\n", (void *)(long)chain
->ip
);
275 static struct symbol
*rem_sq_bracket
;
276 static struct callchain_list rem_hits
;
278 static void init_rem_hits(void)
280 rem_sq_bracket
= malloc(sizeof(*rem_sq_bracket
) + 6);
281 if (!rem_sq_bracket
) {
282 fprintf(stderr
, "Not enough memory to display remaining hits\n");
286 strcpy(rem_sq_bracket
->name
, "[...]");
287 rem_hits
.ms
.sym
= rem_sq_bracket
;
290 static size_t __callchain__fprintf_graph(FILE *fp
, struct callchain_node
*self
,
291 u64 total_samples
, int depth
,
292 int depth_mask
, int left_margin
)
294 struct rb_node
*node
, *next
;
295 struct callchain_node
*child
;
296 struct callchain_list
*chain
;
297 int new_depth_mask
= depth_mask
;
303 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
304 new_total
= self
->children_hit
;
306 new_total
= total_samples
;
308 remaining
= new_total
;
310 node
= rb_first(&self
->rb_root
);
314 child
= rb_entry(node
, struct callchain_node
, rb_node
);
315 cumul
= cumul_hits(child
);
319 * The depth mask manages the output of pipes that show
320 * the depth. We don't want to keep the pipes of the current
321 * level for the last child of this depth.
322 * Except if we have remaining filtered hits. They will
323 * supersede the last child
325 next
= rb_next(node
);
326 if (!next
&& (callchain_param
.mode
!= CHAIN_GRAPH_REL
|| !remaining
))
327 new_depth_mask
&= ~(1 << (depth
- 1));
330 * But we keep the older depth mask for the line separator
331 * to keep the level link until we reach the last child
333 ret
+= ipchain__fprintf_graph_line(fp
, depth
, depth_mask
,
336 list_for_each_entry(chain
, &child
->val
, list
) {
337 ret
+= ipchain__fprintf_graph(fp
, chain
, depth
,
343 ret
+= __callchain__fprintf_graph(fp
, child
, new_total
,
345 new_depth_mask
| (1 << depth
),
350 if (callchain_param
.mode
== CHAIN_GRAPH_REL
&&
351 remaining
&& remaining
!= new_total
) {
356 new_depth_mask
&= ~(1 << (depth
- 1));
358 ret
+= ipchain__fprintf_graph(fp
, &rem_hits
, depth
,
359 new_depth_mask
, 0, new_total
,
360 remaining
, left_margin
);
366 static size_t callchain__fprintf_graph(FILE *fp
, struct callchain_node
*self
,
367 u64 total_samples
, int left_margin
)
369 struct callchain_list
*chain
;
370 bool printed
= false;
374 list_for_each_entry(chain
, &self
->val
, list
) {
375 if (!i
++ && sort__first_dimension
== SORT_SYM
)
379 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
380 ret
+= fprintf(fp
, "|\n");
381 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
382 ret
+= fprintf(fp
, "---");
387 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
390 ret
+= fprintf(fp
, " %s\n", chain
->ms
.sym
->name
);
392 ret
+= fprintf(fp
, " %p\n", (void *)(long)chain
->ip
);
395 ret
+= __callchain__fprintf_graph(fp
, self
, total_samples
, 1, 1, left_margin
);
400 static size_t callchain__fprintf_flat(FILE *fp
, struct callchain_node
*self
,
403 struct callchain_list
*chain
;
409 ret
+= callchain__fprintf_flat(fp
, self
->parent
, total_samples
);
412 list_for_each_entry(chain
, &self
->val
, list
) {
413 if (chain
->ip
>= PERF_CONTEXT_MAX
)
416 ret
+= fprintf(fp
, " %s\n", chain
->ms
.sym
->name
);
418 ret
+= fprintf(fp
, " %p\n",
419 (void *)(long)chain
->ip
);
425 static size_t hist_entry_callchain__fprintf(FILE *fp
, struct hist_entry
*self
,
426 u64 total_samples
, int left_margin
)
428 struct rb_node
*rb_node
;
429 struct callchain_node
*chain
;
432 rb_node
= rb_first(&self
->sorted_chain
);
436 chain
= rb_entry(rb_node
, struct callchain_node
, rb_node
);
437 percent
= chain
->hit
* 100.0 / total_samples
;
438 switch (callchain_param
.mode
) {
440 ret
+= percent_color_fprintf(fp
, " %6.2f%%\n",
442 ret
+= callchain__fprintf_flat(fp
, chain
, total_samples
);
444 case CHAIN_GRAPH_ABS
: /* Falldown */
445 case CHAIN_GRAPH_REL
:
446 ret
+= callchain__fprintf_graph(fp
, chain
, total_samples
,
452 ret
+= fprintf(fp
, "\n");
453 rb_node
= rb_next(rb_node
);
459 int hist_entry__snprintf(struct hist_entry
*self
,
460 char *s
, size_t size
,
461 struct perf_session
*pair_session
,
462 bool show_displacement
,
463 long displacement
, bool color
,
466 struct sort_entry
*se
;
468 const char *sep
= symbol_conf
.field_sep
;
471 if (symbol_conf
.exclude_other
&& !self
->parent
)
475 count
= self
->pair
? self
->pair
->count
: 0;
476 total
= pair_session
->events_stats
.total
;
479 total
= session_total
;
484 ret
= percent_color_snprintf(s
, size
,
485 sep
? "%.2f" : " %6.2f%%",
486 (count
* 100.0) / total
);
488 ret
= snprintf(s
, size
, sep
? "%.2f" : " %6.2f%%",
489 (count
* 100.0) / total
);
491 ret
= snprintf(s
, size
, sep
? "%lld" : "%12lld ", count
);
493 if (symbol_conf
.show_nr_samples
) {
495 ret
+= snprintf(s
+ ret
, size
- ret
, "%c%lld", *sep
, count
);
497 ret
+= snprintf(s
+ ret
, size
- ret
, "%11lld", count
);
502 double old_percent
= 0, new_percent
= 0, diff
;
505 old_percent
= (count
* 100.0) / total
;
506 if (session_total
> 0)
507 new_percent
= (self
->count
* 100.0) / session_total
;
509 diff
= new_percent
- old_percent
;
511 if (fabs(diff
) >= 0.01)
512 snprintf(bf
, sizeof(bf
), "%+4.2F%%", diff
);
514 snprintf(bf
, sizeof(bf
), " ");
517 ret
+= snprintf(s
+ ret
, size
- ret
, "%c%s", *sep
, bf
);
519 ret
+= snprintf(s
+ ret
, size
- ret
, "%11.11s", bf
);
521 if (show_displacement
) {
523 snprintf(bf
, sizeof(bf
), "%+4ld", displacement
);
525 snprintf(bf
, sizeof(bf
), " ");
528 ret
+= snprintf(s
+ ret
, size
- ret
, "%c%s", *sep
, bf
);
530 ret
+= snprintf(s
+ ret
, size
- ret
, "%6.6s", bf
);
534 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
538 ret
+= snprintf(s
+ ret
, size
- ret
, "%s", sep
?: " ");
539 ret
+= se
->snprintf(self
, s
+ ret
, size
- ret
,
540 se
->width
? *se
->width
: 0);
546 int hist_entry__fprintf(struct hist_entry
*self
,
547 struct perf_session
*pair_session
,
548 bool show_displacement
,
549 long displacement
, FILE *fp
,
553 hist_entry__snprintf(self
, bf
, sizeof(bf
), pair_session
,
554 show_displacement
, displacement
,
555 true, session_total
);
556 return fprintf(fp
, "%s\n", bf
);
559 static size_t hist_entry__fprintf_callchain(struct hist_entry
*self
, FILE *fp
,
564 if (sort__first_dimension
== SORT_COMM
) {
565 struct sort_entry
*se
= list_first_entry(&hist_entry__sort_list
,
567 left_margin
= se
->width
? *se
->width
: 0;
568 left_margin
-= thread__comm_len(self
->thread
);
571 return hist_entry_callchain__fprintf(fp
, self
, session_total
,
575 size_t perf_session__fprintf_hists(struct rb_root
*hists
,
576 struct perf_session
*pair
,
577 bool show_displacement
, FILE *fp
,
580 struct sort_entry
*se
;
583 unsigned long position
= 1;
584 long displacement
= 0;
586 const char *sep
= symbol_conf
.field_sep
;
587 char *col_width
= symbol_conf
.col_width_list_str
;
591 fprintf(fp
, "# %s", pair
? "Baseline" : "Overhead");
593 if (symbol_conf
.show_nr_samples
) {
595 fprintf(fp
, "%cSamples", *sep
);
597 fputs(" Samples ", fp
);
602 ret
+= fprintf(fp
, "%cDelta", *sep
);
604 ret
+= fprintf(fp
, " Delta ");
606 if (show_displacement
) {
608 ret
+= fprintf(fp
, "%cDisplacement", *sep
);
610 ret
+= fprintf(fp
, " Displ");
614 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
618 fprintf(fp
, "%c%s", *sep
, se
->header
);
621 width
= strlen(se
->header
);
623 if (symbol_conf
.col_width_list_str
) {
625 *se
->width
= atoi(col_width
);
626 col_width
= strchr(col_width
, ',');
631 width
= *se
->width
= max(*se
->width
, width
);
633 fprintf(fp
, " %*s", width
, se
->header
);
640 fprintf(fp
, "# ........");
641 if (symbol_conf
.show_nr_samples
)
642 fprintf(fp
, " ..........");
644 fprintf(fp
, " ..........");
645 if (show_displacement
)
646 fprintf(fp
, " .....");
648 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
658 width
= strlen(se
->header
);
659 for (i
= 0; i
< width
; i
++)
663 fprintf(fp
, "\n#\n");
666 for (nd
= rb_first(hists
); nd
; nd
= rb_next(nd
)) {
667 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
669 if (show_displacement
) {
671 displacement
= ((long)h
->pair
->position
-
677 ret
+= hist_entry__fprintf(h
, pair
, show_displacement
,
678 displacement
, fp
, session_total
);
680 if (symbol_conf
.use_callchain
)
681 ret
+= hist_entry__fprintf_callchain(h
, fp
, session_total
);
683 if (h
->ms
.map
== NULL
&& verbose
> 1) {
684 __map_groups__fprintf_maps(&h
->thread
->mg
,
685 MAP__FUNCTION
, verbose
, fp
);
686 fprintf(fp
, "%.10s end\n", graph_dotted_line
);
690 free(rem_sq_bracket
);