6d06fbb365b6a9f896ff4f251fb36a2586009758
3 #include "../../util/util.h"
4 #include "../../util/hist.h"
5 #include "../../util/sort.h"
6 #include "../../util/evsel.h"
9 static size_t callchain__fprintf_left_margin(FILE *fp
, int left_margin
)
12 int ret
= fprintf(fp
, " ");
14 for (i
= 0; i
< left_margin
; i
++)
15 ret
+= fprintf(fp
, " ");
20 static size_t ipchain__fprintf_graph_line(FILE *fp
, int depth
, int depth_mask
,
24 size_t ret
= callchain__fprintf_left_margin(fp
, left_margin
);
26 for (i
= 0; i
< depth
; i
++)
27 if (depth_mask
& (1 << i
))
28 ret
+= fprintf(fp
, "| ");
30 ret
+= fprintf(fp
, " ");
32 ret
+= fprintf(fp
, "\n");
37 static size_t ipchain__fprintf_graph(FILE *fp
, struct callchain_node
*node
,
38 struct callchain_list
*chain
,
39 int depth
, int depth_mask
, int period
,
40 u64 total_samples
, int left_margin
)
46 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
47 for (i
= 0; i
< depth
; i
++) {
48 if (depth_mask
& (1 << i
))
49 ret
+= fprintf(fp
, "|");
51 ret
+= fprintf(fp
, " ");
52 if (!period
&& i
== depth
- 1) {
53 ret
+= fprintf(fp
, "--");
54 ret
+= callchain_node__fprintf_value(node
, fp
, total_samples
);
55 ret
+= fprintf(fp
, "--");
57 ret
+= fprintf(fp
, "%s", " ");
59 fputs(callchain_list__sym_name(chain
, bf
, sizeof(bf
), false), fp
);
64 static struct symbol
*rem_sq_bracket
;
65 static struct callchain_list rem_hits
;
67 static void init_rem_hits(void)
69 rem_sq_bracket
= malloc(sizeof(*rem_sq_bracket
) + 6);
70 if (!rem_sq_bracket
) {
71 fprintf(stderr
, "Not enough memory to display remaining hits\n");
75 strcpy(rem_sq_bracket
->name
, "[...]");
76 rem_hits
.ms
.sym
= rem_sq_bracket
;
79 static size_t __callchain__fprintf_graph(FILE *fp
, struct rb_root
*root
,
80 u64 total_samples
, int depth
,
81 int depth_mask
, int left_margin
)
83 struct rb_node
*node
, *next
;
84 struct callchain_node
*child
= NULL
;
85 struct callchain_list
*chain
;
86 int new_depth_mask
= depth_mask
;
90 uint entries_printed
= 0;
93 remaining
= total_samples
;
95 node
= rb_first(root
);
100 child
= rb_entry(node
, struct callchain_node
, rb_node
);
101 cumul
= callchain_cumul_hits(child
);
103 cumul_count
+= callchain_cumul_counts(child
);
106 * The depth mask manages the output of pipes that show
107 * the depth. We don't want to keep the pipes of the current
108 * level for the last child of this depth.
109 * Except if we have remaining filtered hits. They will
110 * supersede the last child
112 next
= rb_next(node
);
113 if (!next
&& (callchain_param
.mode
!= CHAIN_GRAPH_REL
|| !remaining
))
114 new_depth_mask
&= ~(1 << (depth
- 1));
117 * But we keep the older depth mask for the line separator
118 * to keep the level link until we reach the last child
120 ret
+= ipchain__fprintf_graph_line(fp
, depth
, depth_mask
,
123 list_for_each_entry(chain
, &child
->val
, list
) {
124 ret
+= ipchain__fprintf_graph(fp
, child
, chain
, depth
,
130 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
131 new_total
= child
->children_hit
;
133 new_total
= total_samples
;
135 ret
+= __callchain__fprintf_graph(fp
, &child
->rb_root
, new_total
,
137 new_depth_mask
| (1 << depth
),
140 if (++entries_printed
== callchain_param
.print_limit
)
144 if (callchain_param
.mode
== CHAIN_GRAPH_REL
&&
145 remaining
&& remaining
!= total_samples
) {
146 struct callchain_node rem_node
= {
153 if (callchain_param
.value
== CCVAL_COUNT
&& child
&& child
->parent
) {
154 rem_node
.count
= child
->parent
->children_count
- cumul_count
;
155 if (rem_node
.count
<= 0)
159 new_depth_mask
&= ~(1 << (depth
- 1));
160 ret
+= ipchain__fprintf_graph(fp
, &rem_node
, &rem_hits
, depth
,
161 new_depth_mask
, 0, total_samples
,
169 * If have one single callchain root, don't bother printing
170 * its percentage (100 % in fractal mode and the same percentage
171 * than the hist in graph mode). This also avoid one level of column.
173 * However when percent-limit applied, it's possible that single callchain
174 * node have different (non-100% in fractal mode) percentage.
176 static bool need_percent_display(struct rb_node
*node
, u64 parent_samples
)
178 struct callchain_node
*cnode
;
183 cnode
= rb_entry(node
, struct callchain_node
, rb_node
);
184 return callchain_cumul_hits(cnode
) != parent_samples
;
187 static size_t callchain__fprintf_graph(FILE *fp
, struct rb_root
*root
,
188 u64 total_samples
, u64 parent_samples
,
191 struct callchain_node
*cnode
;
192 struct callchain_list
*chain
;
193 u32 entries_printed
= 0;
194 bool printed
= false;
195 struct rb_node
*node
;
200 node
= rb_first(root
);
201 if (node
&& !need_percent_display(node
, parent_samples
)) {
202 cnode
= rb_entry(node
, struct callchain_node
, rb_node
);
203 list_for_each_entry(chain
, &cnode
->val
, list
) {
205 * If we sort by symbol, the first entry is the same than
206 * the symbol. No need to print it otherwise it appears as
209 if (!i
++ && field_order
== NULL
&&
210 sort_order
&& !prefixcmp(sort_order
, "sym"))
213 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
214 ret
+= fprintf(fp
, "|\n");
215 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
216 ret
+= fprintf(fp
, "---");
220 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
222 ret
+= fprintf(fp
, "%s\n", callchain_list__sym_name(chain
, bf
, sizeof(bf
),
225 if (++entries_printed
== callchain_param
.print_limit
)
228 root
= &cnode
->rb_root
;
231 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
232 total_samples
= parent_samples
;
234 ret
+= __callchain__fprintf_graph(fp
, root
, total_samples
,
237 /* do not add a blank line if it printed nothing */
238 ret
+= fprintf(fp
, "\n");
244 static size_t __callchain__fprintf_flat(FILE *fp
, struct callchain_node
*node
,
247 struct callchain_list
*chain
;
254 ret
+= __callchain__fprintf_flat(fp
, node
->parent
, total_samples
);
257 list_for_each_entry(chain
, &node
->val
, list
) {
258 if (chain
->ip
>= PERF_CONTEXT_MAX
)
260 ret
+= fprintf(fp
, " %s\n", callchain_list__sym_name(chain
,
261 bf
, sizeof(bf
), false));
267 static size_t callchain__fprintf_flat(FILE *fp
, struct rb_root
*tree
,
271 u32 entries_printed
= 0;
272 struct callchain_node
*chain
;
273 struct rb_node
*rb_node
= rb_first(tree
);
276 chain
= rb_entry(rb_node
, struct callchain_node
, rb_node
);
278 ret
+= fprintf(fp
, " ");
279 ret
+= callchain_node__fprintf_value(chain
, fp
, total_samples
);
280 ret
+= fprintf(fp
, "\n");
281 ret
+= __callchain__fprintf_flat(fp
, chain
, total_samples
);
282 ret
+= fprintf(fp
, "\n");
283 if (++entries_printed
== callchain_param
.print_limit
)
286 rb_node
= rb_next(rb_node
);
292 static size_t __callchain__fprintf_folded(FILE *fp
, struct callchain_node
*node
)
294 const char *sep
= symbol_conf
.field_sep
?: ";";
295 struct callchain_list
*chain
;
303 ret
+= __callchain__fprintf_folded(fp
, node
->parent
);
306 list_for_each_entry(chain
, &node
->val
, list
) {
307 if (chain
->ip
>= PERF_CONTEXT_MAX
)
309 ret
+= fprintf(fp
, "%s%s", first
? "" : sep
,
310 callchain_list__sym_name(chain
,
311 bf
, sizeof(bf
), false));
318 static size_t callchain__fprintf_folded(FILE *fp
, struct rb_root
*tree
,
322 u32 entries_printed
= 0;
323 struct callchain_node
*chain
;
324 struct rb_node
*rb_node
= rb_first(tree
);
328 chain
= rb_entry(rb_node
, struct callchain_node
, rb_node
);
330 ret
+= callchain_node__fprintf_value(chain
, fp
, total_samples
);
331 ret
+= fprintf(fp
, " ");
332 ret
+= __callchain__fprintf_folded(fp
, chain
);
333 ret
+= fprintf(fp
, "\n");
334 if (++entries_printed
== callchain_param
.print_limit
)
337 rb_node
= rb_next(rb_node
);
343 static size_t hist_entry_callchain__fprintf(struct hist_entry
*he
,
344 u64 total_samples
, int left_margin
,
347 u64 parent_samples
= he
->stat
.period
;
349 if (symbol_conf
.cumulate_callchain
)
350 parent_samples
= he
->stat_acc
->period
;
352 switch (callchain_param
.mode
) {
353 case CHAIN_GRAPH_REL
:
354 return callchain__fprintf_graph(fp
, &he
->sorted_chain
, total_samples
,
355 parent_samples
, left_margin
);
357 case CHAIN_GRAPH_ABS
:
358 return callchain__fprintf_graph(fp
, &he
->sorted_chain
, total_samples
,
359 parent_samples
, left_margin
);
362 return callchain__fprintf_flat(fp
, &he
->sorted_chain
, total_samples
);
365 return callchain__fprintf_folded(fp
, &he
->sorted_chain
, total_samples
);
370 pr_err("Bad callchain mode\n");
376 static int hist_entry__snprintf(struct hist_entry
*he
, struct perf_hpp
*hpp
)
378 const char *sep
= symbol_conf
.field_sep
;
379 struct perf_hpp_fmt
*fmt
;
380 char *start
= hpp
->buf
;
384 if (symbol_conf
.exclude_other
&& !he
->parent
)
387 hists__for_each_format(he
->hists
, fmt
) {
388 if (perf_hpp__should_skip(fmt
, he
->hists
))
392 * If there's no field_sep, we still need
393 * to display initial ' '.
395 if (!sep
|| !first
) {
396 ret
= scnprintf(hpp
->buf
, hpp
->size
, "%s", sep
?: " ");
397 advance_hpp(hpp
, ret
);
401 if (perf_hpp__use_color() && fmt
->color
)
402 ret
= fmt
->color(fmt
, hpp
, he
);
404 ret
= fmt
->entry(fmt
, hpp
, he
);
406 ret
= hist_entry__snprintf_alignment(he
, hpp
, fmt
, ret
);
407 advance_hpp(hpp
, ret
);
410 return hpp
->buf
- start
;
413 static int hist_entry__hierarchy_fprintf(struct hist_entry
*he
,
414 struct perf_hpp
*hpp
,
415 int nr_sort_key
, struct hists
*hists
,
418 const char *sep
= symbol_conf
.field_sep
;
419 struct perf_hpp_fmt
*fmt
;
420 char *buf
= hpp
->buf
;
421 size_t size
= hpp
->size
;
422 int ret
, printed
= 0;
425 if (symbol_conf
.exclude_other
&& !he
->parent
)
428 ret
= scnprintf(hpp
->buf
, hpp
->size
, "%*s", he
->depth
* HIERARCHY_INDENT
, "");
429 advance_hpp(hpp
, ret
);
431 hists__for_each_format(he
->hists
, fmt
) {
432 if (perf_hpp__is_sort_entry(fmt
) || perf_hpp__is_dynamic_entry(fmt
))
436 * If there's no field_sep, we still need
437 * to display initial ' '.
439 if (!sep
|| !first
) {
440 ret
= scnprintf(hpp
->buf
, hpp
->size
, "%s", sep
?: " ");
441 advance_hpp(hpp
, ret
);
445 if (perf_hpp__use_color() && fmt
->color
)
446 ret
= fmt
->color(fmt
, hpp
, he
);
448 ret
= fmt
->entry(fmt
, hpp
, he
);
450 ret
= hist_entry__snprintf_alignment(he
, hpp
, fmt
, ret
);
451 advance_hpp(hpp
, ret
);
455 ret
= scnprintf(hpp
->buf
, hpp
->size
, "%s", sep
);
457 ret
= scnprintf(hpp
->buf
, hpp
->size
, "%*s",
458 (nr_sort_key
- 1) * HIERARCHY_INDENT
+ 2, "");
459 advance_hpp(hpp
, ret
);
461 printed
+= fprintf(fp
, "%s", buf
);
467 * No need to call hist_entry__snprintf_alignment() since this
468 * fmt is always the last column in the hierarchy mode.
471 if (perf_hpp__use_color() && fmt
->color
)
472 fmt
->color(fmt
, hpp
, he
);
474 fmt
->entry(fmt
, hpp
, he
);
477 * dynamic entries are right-aligned but we want left-aligned
478 * in the hierarchy mode
480 printed
+= fprintf(fp
, "%s\n", ltrim(buf
));
482 if (symbol_conf
.use_callchain
&& he
->leaf
) {
483 u64 total
= hists__total_period(hists
);
485 printed
+= hist_entry_callchain__fprintf(he
, total
, 0, fp
);
493 static int hist_entry__fprintf(struct hist_entry
*he
, size_t size
,
495 char *bf
, size_t bfsz
, FILE *fp
)
498 struct perf_hpp hpp
= {
502 u64 total_period
= hists
->stats
.total_period
;
504 if (size
== 0 || size
> bfsz
)
505 size
= hpp
.size
= bfsz
;
507 if (symbol_conf
.report_hierarchy
) {
508 int nr_sort
= hists
->nr_sort_keys
;
510 return hist_entry__hierarchy_fprintf(he
, &hpp
, nr_sort
,
514 hist_entry__snprintf(he
, &hpp
);
516 ret
= fprintf(fp
, "%s\n", bf
);
518 if (symbol_conf
.use_callchain
)
519 ret
+= hist_entry_callchain__fprintf(he
, total_period
, 0, fp
);
524 static int print_hierarchy_indent(const char *sep
, int nr_sort
,
525 const char *line
, FILE *fp
)
527 if (sep
!= NULL
|| nr_sort
< 1)
530 return fprintf(fp
, "%-.*s", (nr_sort
- 1) * HIERARCHY_INDENT
, line
);
533 static int print_hierarchy_header(struct hists
*hists
, struct perf_hpp
*hpp
,
534 const char *sep
, FILE *fp
)
540 unsigned header_width
= 0;
541 struct perf_hpp_fmt
*fmt
;
543 nr_sort
= hists
->nr_sort_keys
;
545 /* preserve max indent depth for column headers */
546 print_hierarchy_indent(sep
, nr_sort
, spaces
, fp
);
548 hists__for_each_format(hists
, fmt
) {
549 if (perf_hpp__is_sort_entry(fmt
) || perf_hpp__is_dynamic_entry(fmt
))
553 fprintf(fp
, "%s", sep
?: " ");
557 fmt
->header(fmt
, hpp
, hists_to_evsel(hists
));
558 fprintf(fp
, "%s", hpp
->buf
);
561 /* combine sort headers with ' / ' */
563 hists__for_each_format(hists
, fmt
) {
564 if (!perf_hpp__is_sort_entry(fmt
) && !perf_hpp__is_dynamic_entry(fmt
))
566 if (perf_hpp__should_skip(fmt
, hists
))
570 header_width
+= fprintf(fp
, " / ");
572 fprintf(fp
, "%s", sep
?: " ");
576 fmt
->header(fmt
, hpp
, hists_to_evsel(hists
));
579 header_width
+= fprintf(fp
, "%s", ltrim(hpp
->buf
));
584 /* preserve max indent depth for initial dots */
585 print_hierarchy_indent(sep
, nr_sort
, dots
, fp
);
588 hists__for_each_format(hists
, fmt
) {
589 if (perf_hpp__is_sort_entry(fmt
) || perf_hpp__is_dynamic_entry(fmt
))
593 fprintf(fp
, "%s", sep
?: " ");
597 width
= fmt
->width(fmt
, hpp
, hists_to_evsel(hists
));
598 fprintf(fp
, "%.*s", width
, dots
);
602 hists__for_each_format(hists
, fmt
) {
603 if (!perf_hpp__is_sort_entry(fmt
) && !perf_hpp__is_dynamic_entry(fmt
))
605 if (perf_hpp__should_skip(fmt
, hists
))
608 width
= fmt
->width(fmt
, hpp
, hists_to_evsel(hists
));
609 width
+= depth
* HIERARCHY_INDENT
;
611 if (width
> header_width
)
612 header_width
= width
;
617 fprintf(fp
, "%s%-.*s", sep
?: " ", header_width
, dots
);
619 fprintf(fp
, "\n#\n");
624 size_t hists__fprintf(struct hists
*hists
, bool show_header
, int max_rows
,
625 int max_cols
, float min_pcnt
, FILE *fp
)
627 struct perf_hpp_fmt
*fmt
;
631 const char *sep
= symbol_conf
.field_sep
;
634 struct perf_hpp dummy_hpp
= {
645 hists__for_each_format(hists
, fmt
)
646 perf_hpp__reset_width(fmt
, hists
);
648 if (symbol_conf
.col_width_list_str
)
649 perf_hpp__set_user_width(symbol_conf
.col_width_list_str
);
656 if (symbol_conf
.report_hierarchy
) {
657 nr_rows
+= print_hierarchy_header(hists
, &dummy_hpp
, sep
, fp
);
661 hists__for_each_format(hists
, fmt
) {
662 if (perf_hpp__should_skip(fmt
, hists
))
666 fprintf(fp
, "%s", sep
?: " ");
670 fmt
->header(fmt
, &dummy_hpp
, hists_to_evsel(hists
));
671 fprintf(fp
, "%s", bf
);
675 if (max_rows
&& ++nr_rows
>= max_rows
)
685 hists__for_each_format(hists
, fmt
) {
688 if (perf_hpp__should_skip(fmt
, hists
))
692 fprintf(fp
, "%s", sep
?: " ");
696 width
= fmt
->width(fmt
, &dummy_hpp
, hists_to_evsel(hists
));
697 for (i
= 0; i
< width
; i
++)
702 if (max_rows
&& ++nr_rows
>= max_rows
)
706 if (max_rows
&& ++nr_rows
>= max_rows
)
710 linesz
= hists__sort_list_width(hists
) + 3 + 1;
711 linesz
+= perf_hpp__color_overhead();
712 line
= malloc(linesz
);
718 indent
= hists__overhead_width(hists
) + 4;
720 for (nd
= rb_first(&hists
->entries
); nd
; nd
= __rb_hierarchy_next(nd
, HMD_FORCE_CHILD
)) {
721 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
727 percent
= hist_entry__get_percent_limit(h
);
728 if (percent
< min_pcnt
)
731 ret
+= hist_entry__fprintf(h
, max_cols
, hists
, line
, linesz
, fp
);
733 if (max_rows
&& ++nr_rows
>= max_rows
)
737 * If all children are filtered out or percent-limited,
738 * display "no entry >= x.xx%" message.
740 if (!h
->leaf
&& !hist_entry__has_hierarchy_children(h
, min_pcnt
)) {
741 int nr_sort
= hists
->nr_sort_keys
;
743 print_hierarchy_indent(sep
, nr_sort
+ h
->depth
+ 1, spaces
, fp
);
744 fprintf(fp
, "%*sno entry >= %.2f%%\n", indent
, "", min_pcnt
);
746 if (max_rows
&& ++nr_rows
>= max_rows
)
750 if (h
->ms
.map
== NULL
&& verbose
> 1) {
751 __map_groups__fprintf_maps(h
->thread
->mg
,
753 fprintf(fp
, "%.10s end\n", graph_dotted_line
);
759 zfree(&rem_sq_bracket
);
764 size_t events_stats__fprintf(struct events_stats
*stats
, FILE *fp
)
769 for (i
= 0; i
< PERF_RECORD_HEADER_MAX
; ++i
) {
772 if (stats
->nr_events
[i
] == 0)
775 name
= perf_event__name(i
);
776 if (!strcmp(name
, "UNKNOWN"))
779 ret
+= fprintf(fp
, "%16s events: %10d\n", name
,
780 stats
->nr_events
[i
]);
This page took 0.046785 seconds and 4 git commands to generate.