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
,
168 static size_t callchain__fprintf_graph(FILE *fp
, struct rb_root
*root
,
169 u64 total_samples
, int left_margin
)
171 struct callchain_node
*cnode
;
172 struct callchain_list
*chain
;
173 u32 entries_printed
= 0;
174 bool printed
= false;
175 struct rb_node
*node
;
181 * If have one single callchain root, don't bother printing
182 * its percentage (100 % in fractal mode and the same percentage
183 * than the hist in graph mode). This also avoid one level of column.
185 node
= rb_first(root
);
186 if (node
&& !rb_next(node
)) {
187 cnode
= rb_entry(node
, struct callchain_node
, rb_node
);
188 list_for_each_entry(chain
, &cnode
->val
, list
) {
190 * If we sort by symbol, the first entry is the same than
191 * the symbol. No need to print it otherwise it appears as
194 if (!i
++ && field_order
== NULL
&&
195 sort_order
&& !prefixcmp(sort_order
, "sym"))
198 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
199 ret
+= fprintf(fp
, "|\n");
200 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
201 ret
+= fprintf(fp
, "---");
205 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
207 ret
+= fprintf(fp
, "%s\n", callchain_list__sym_name(chain
, bf
, sizeof(bf
),
210 if (++entries_printed
== callchain_param
.print_limit
)
213 root
= &cnode
->rb_root
;
216 ret
+= __callchain__fprintf_graph(fp
, root
, total_samples
,
218 ret
+= fprintf(fp
, "\n");
223 static size_t __callchain__fprintf_flat(FILE *fp
, struct callchain_node
*node
,
226 struct callchain_list
*chain
;
233 ret
+= __callchain__fprintf_flat(fp
, node
->parent
, total_samples
);
236 list_for_each_entry(chain
, &node
->val
, list
) {
237 if (chain
->ip
>= PERF_CONTEXT_MAX
)
239 ret
+= fprintf(fp
, " %s\n", callchain_list__sym_name(chain
,
240 bf
, sizeof(bf
), false));
246 static size_t callchain__fprintf_flat(FILE *fp
, struct rb_root
*tree
,
250 u32 entries_printed
= 0;
251 struct callchain_node
*chain
;
252 struct rb_node
*rb_node
= rb_first(tree
);
255 chain
= rb_entry(rb_node
, struct callchain_node
, rb_node
);
257 ret
+= fprintf(fp
, " ");
258 ret
+= callchain_node__fprintf_value(chain
, fp
, total_samples
);
259 ret
+= fprintf(fp
, "\n");
260 ret
+= __callchain__fprintf_flat(fp
, chain
, total_samples
);
261 ret
+= fprintf(fp
, "\n");
262 if (++entries_printed
== callchain_param
.print_limit
)
265 rb_node
= rb_next(rb_node
);
271 static size_t __callchain__fprintf_folded(FILE *fp
, struct callchain_node
*node
)
273 const char *sep
= symbol_conf
.field_sep
?: ";";
274 struct callchain_list
*chain
;
282 ret
+= __callchain__fprintf_folded(fp
, node
->parent
);
285 list_for_each_entry(chain
, &node
->val
, list
) {
286 if (chain
->ip
>= PERF_CONTEXT_MAX
)
288 ret
+= fprintf(fp
, "%s%s", first
? "" : sep
,
289 callchain_list__sym_name(chain
,
290 bf
, sizeof(bf
), false));
297 static size_t callchain__fprintf_folded(FILE *fp
, struct rb_root
*tree
,
301 u32 entries_printed
= 0;
302 struct callchain_node
*chain
;
303 struct rb_node
*rb_node
= rb_first(tree
);
307 chain
= rb_entry(rb_node
, struct callchain_node
, rb_node
);
309 ret
+= callchain_node__fprintf_value(chain
, fp
, total_samples
);
310 ret
+= fprintf(fp
, " ");
311 ret
+= __callchain__fprintf_folded(fp
, chain
);
312 ret
+= fprintf(fp
, "\n");
313 if (++entries_printed
== callchain_param
.print_limit
)
316 rb_node
= rb_next(rb_node
);
322 static size_t hist_entry_callchain__fprintf(struct hist_entry
*he
,
323 u64 total_samples
, int left_margin
,
326 switch (callchain_param
.mode
) {
327 case CHAIN_GRAPH_REL
:
328 return callchain__fprintf_graph(fp
, &he
->sorted_chain
,
329 symbol_conf
.cumulate_callchain
?
330 he
->stat_acc
->period
: he
->stat
.period
,
333 case CHAIN_GRAPH_ABS
:
334 return callchain__fprintf_graph(fp
, &he
->sorted_chain
, total_samples
,
338 return callchain__fprintf_flat(fp
, &he
->sorted_chain
, total_samples
);
341 return callchain__fprintf_folded(fp
, &he
->sorted_chain
, total_samples
);
346 pr_err("Bad callchain mode\n");
352 static size_t hist_entry__callchain_fprintf(struct hist_entry
*he
,
357 u64 total_period
= hists
->stats
.total_period
;
359 if (field_order
== NULL
&& (sort_order
== NULL
||
360 !prefixcmp(sort_order
, "comm"))) {
361 struct perf_hpp_fmt
*fmt
;
363 perf_hpp__for_each_format(fmt
) {
364 if (!perf_hpp__is_sort_entry(fmt
))
367 /* must be 'comm' sort entry */
368 left_margin
= fmt
->width(fmt
, NULL
, hists_to_evsel(hists
));
369 left_margin
-= thread__comm_len(he
->thread
);
373 return hist_entry_callchain__fprintf(he
, total_period
, left_margin
, fp
);
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 perf_hpp__for_each_format(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 advance_hpp(hpp
, ret
);
409 return hpp
->buf
- start
;
412 static int hist_entry__fprintf(struct hist_entry
*he
, size_t size
,
414 char *bf
, size_t bfsz
, FILE *fp
)
417 struct perf_hpp hpp
= {
422 if (size
== 0 || size
> bfsz
)
423 size
= hpp
.size
= bfsz
;
425 hist_entry__snprintf(he
, &hpp
);
427 ret
= fprintf(fp
, "%s\n", bf
);
429 if (symbol_conf
.use_callchain
)
430 ret
+= hist_entry__callchain_fprintf(he
, hists
, fp
);
435 size_t hists__fprintf(struct hists
*hists
, bool show_header
, int max_rows
,
436 int max_cols
, float min_pcnt
, FILE *fp
)
438 struct perf_hpp_fmt
*fmt
;
442 const char *sep
= symbol_conf
.field_sep
;
445 struct perf_hpp dummy_hpp
= {
455 perf_hpp__for_each_format(fmt
)
456 perf_hpp__reset_width(fmt
, hists
);
458 if (symbol_conf
.col_width_list_str
)
459 perf_hpp__set_user_width(symbol_conf
.col_width_list_str
);
466 perf_hpp__for_each_format(fmt
) {
467 if (perf_hpp__should_skip(fmt
, hists
))
471 fprintf(fp
, "%s", sep
?: " ");
475 fmt
->header(fmt
, &dummy_hpp
, hists_to_evsel(hists
));
476 fprintf(fp
, "%s", bf
);
480 if (max_rows
&& ++nr_rows
>= max_rows
)
490 perf_hpp__for_each_format(fmt
) {
493 if (perf_hpp__should_skip(fmt
, hists
))
497 fprintf(fp
, "%s", sep
?: " ");
501 width
= fmt
->width(fmt
, &dummy_hpp
, hists_to_evsel(hists
));
502 for (i
= 0; i
< width
; i
++)
507 if (max_rows
&& ++nr_rows
>= max_rows
)
511 if (max_rows
&& ++nr_rows
>= max_rows
)
515 linesz
= hists__sort_list_width(hists
) + 3 + 1;
516 linesz
+= perf_hpp__color_overhead();
517 line
= malloc(linesz
);
523 for (nd
= rb_first(&hists
->entries
); nd
; nd
= rb_next(nd
)) {
524 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
530 percent
= hist_entry__get_percent_limit(h
);
531 if (percent
< min_pcnt
)
534 ret
+= hist_entry__fprintf(h
, max_cols
, hists
, line
, linesz
, fp
);
536 if (max_rows
&& ++nr_rows
>= max_rows
)
539 if (h
->ms
.map
== NULL
&& verbose
> 1) {
540 __map_groups__fprintf_maps(h
->thread
->mg
,
542 fprintf(fp
, "%.10s end\n", graph_dotted_line
);
548 zfree(&rem_sq_bracket
);
553 size_t events_stats__fprintf(struct events_stats
*stats
, FILE *fp
)
558 for (i
= 0; i
< PERF_RECORD_HEADER_MAX
; ++i
) {
561 if (stats
->nr_events
[i
] == 0)
564 name
= perf_event__name(i
);
565 if (!strcmp(name
, "UNKNOWN"))
568 ret
+= fprintf(fp
, "%16s events: %10d\n", name
,
569 stats
->nr_events
[i
]);