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_list
*chain
,
38 int depth
, int depth_mask
, int period
,
39 u64 total_samples
, u64 hits
,
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) {
55 percent
= hits
* 100.0 / total_samples
;
56 ret
+= percent_color_fprintf(fp
, "--%2.2f%%-- ", percent
);
58 ret
+= fprintf(fp
, "%s", " ");
60 fputs(callchain_list__sym_name(chain
, bf
, sizeof(bf
), false), fp
);
65 static struct symbol
*rem_sq_bracket
;
66 static struct callchain_list rem_hits
;
68 static void init_rem_hits(void)
70 rem_sq_bracket
= malloc(sizeof(*rem_sq_bracket
) + 6);
71 if (!rem_sq_bracket
) {
72 fprintf(stderr
, "Not enough memory to display remaining hits\n");
76 strcpy(rem_sq_bracket
->name
, "[...]");
77 rem_hits
.ms
.sym
= rem_sq_bracket
;
80 static size_t __callchain__fprintf_graph(FILE *fp
, struct rb_root
*root
,
81 u64 total_samples
, int depth
,
82 int depth_mask
, int left_margin
)
84 struct rb_node
*node
, *next
;
85 struct callchain_node
*child
;
86 struct callchain_list
*chain
;
87 int new_depth_mask
= depth_mask
;
91 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
);
105 * The depth mask manages the output of pipes that show
106 * the depth. We don't want to keep the pipes of the current
107 * level for the last child of this depth.
108 * Except if we have remaining filtered hits. They will
109 * supersede the last child
111 next
= rb_next(node
);
112 if (!next
&& (callchain_param
.mode
!= CHAIN_GRAPH_REL
|| !remaining
))
113 new_depth_mask
&= ~(1 << (depth
- 1));
116 * But we keep the older depth mask for the line separator
117 * to keep the level link until we reach the last child
119 ret
+= ipchain__fprintf_graph_line(fp
, depth
, depth_mask
,
122 list_for_each_entry(chain
, &child
->val
, list
) {
123 ret
+= ipchain__fprintf_graph(fp
, 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
) {
150 new_depth_mask
&= ~(1 << (depth
- 1));
151 ret
+= ipchain__fprintf_graph(fp
, &rem_hits
, depth
,
152 new_depth_mask
, 0, total_samples
,
153 remaining
, left_margin
);
159 static size_t callchain__fprintf_graph(FILE *fp
, struct rb_root
*root
,
160 u64 total_samples
, int left_margin
)
162 struct callchain_node
*cnode
;
163 struct callchain_list
*chain
;
164 u32 entries_printed
= 0;
165 bool printed
= false;
166 struct rb_node
*node
;
172 * If have one single callchain root, don't bother printing
173 * its percentage (100 % in fractal mode and the same percentage
174 * than the hist in graph mode). This also avoid one level of column.
176 node
= rb_first(root
);
177 if (node
&& !rb_next(node
)) {
178 cnode
= rb_entry(node
, struct callchain_node
, rb_node
);
179 list_for_each_entry(chain
, &cnode
->val
, list
) {
181 * If we sort by symbol, the first entry is the same than
182 * the symbol. No need to print it otherwise it appears as
185 if (!i
++ && field_order
== NULL
&&
186 sort_order
&& !prefixcmp(sort_order
, "sym"))
189 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
190 ret
+= fprintf(fp
, "|\n");
191 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
192 ret
+= fprintf(fp
, "---");
196 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
198 ret
+= fprintf(fp
, "%s\n", callchain_list__sym_name(chain
, bf
, sizeof(bf
),
201 if (++entries_printed
== callchain_param
.print_limit
)
204 root
= &cnode
->rb_root
;
207 ret
+= __callchain__fprintf_graph(fp
, root
, total_samples
,
209 ret
+= fprintf(fp
, "\n");
214 static size_t __callchain__fprintf_flat(FILE *fp
, struct callchain_node
*node
,
217 struct callchain_list
*chain
;
224 ret
+= __callchain__fprintf_flat(fp
, node
->parent
, total_samples
);
227 list_for_each_entry(chain
, &node
->val
, list
) {
228 if (chain
->ip
>= PERF_CONTEXT_MAX
)
230 ret
+= fprintf(fp
, " %s\n", callchain_list__sym_name(chain
,
231 bf
, sizeof(bf
), false));
237 static size_t callchain__fprintf_flat(FILE *fp
, struct rb_root
*tree
,
241 u32 entries_printed
= 0;
242 struct callchain_node
*chain
;
243 struct rb_node
*rb_node
= rb_first(tree
);
248 chain
= rb_entry(rb_node
, struct callchain_node
, rb_node
);
249 percent
= chain
->hit
* 100.0 / total_samples
;
251 ret
= percent_color_fprintf(fp
, " %6.2f%%\n", percent
);
252 ret
+= __callchain__fprintf_flat(fp
, chain
, total_samples
);
253 ret
+= fprintf(fp
, "\n");
254 if (++entries_printed
== callchain_param
.print_limit
)
257 rb_node
= rb_next(rb_node
);
263 static size_t hist_entry_callchain__fprintf(struct hist_entry
*he
,
264 u64 total_samples
, int left_margin
,
267 switch (callchain_param
.mode
) {
268 case CHAIN_GRAPH_REL
:
269 return callchain__fprintf_graph(fp
, &he
->sorted_chain
,
270 symbol_conf
.cumulate_callchain
?
271 he
->stat_acc
->period
: he
->stat
.period
,
274 case CHAIN_GRAPH_ABS
:
275 return callchain__fprintf_graph(fp
, &he
->sorted_chain
, total_samples
,
279 return callchain__fprintf_flat(fp
, &he
->sorted_chain
, total_samples
);
284 pr_err("Bad callchain mode\n");
290 static size_t hist_entry__callchain_fprintf(struct hist_entry
*he
,
295 u64 total_period
= hists
->stats
.total_period
;
297 if (field_order
== NULL
&& (sort_order
== NULL
||
298 !prefixcmp(sort_order
, "comm"))) {
299 struct perf_hpp_fmt
*fmt
;
301 perf_hpp__for_each_format(fmt
) {
302 if (!perf_hpp__is_sort_entry(fmt
))
305 /* must be 'comm' sort entry */
306 left_margin
= fmt
->width(fmt
, NULL
, hists_to_evsel(hists
));
307 left_margin
-= thread__comm_len(he
->thread
);
311 return hist_entry_callchain__fprintf(he
, total_period
, left_margin
, fp
);
314 static int hist_entry__snprintf(struct hist_entry
*he
, struct perf_hpp
*hpp
)
316 const char *sep
= symbol_conf
.field_sep
;
317 struct perf_hpp_fmt
*fmt
;
318 char *start
= hpp
->buf
;
322 if (symbol_conf
.exclude_other
&& !he
->parent
)
325 perf_hpp__for_each_format(fmt
) {
326 if (perf_hpp__should_skip(fmt
))
330 * If there's no field_sep, we still need
331 * to display initial ' '.
333 if (!sep
|| !first
) {
334 ret
= scnprintf(hpp
->buf
, hpp
->size
, "%s", sep
?: " ");
335 advance_hpp(hpp
, ret
);
339 if (perf_hpp__use_color() && fmt
->color
)
340 ret
= fmt
->color(fmt
, hpp
, he
);
342 ret
= fmt
->entry(fmt
, hpp
, he
);
344 advance_hpp(hpp
, ret
);
347 return hpp
->buf
- start
;
350 static int hist_entry__fprintf(struct hist_entry
*he
, size_t size
,
352 char *bf
, size_t bfsz
, FILE *fp
)
355 struct perf_hpp hpp
= {
360 if (size
== 0 || size
> bfsz
)
361 size
= hpp
.size
= bfsz
;
363 hist_entry__snprintf(he
, &hpp
);
365 ret
= fprintf(fp
, "%s\n", bf
);
367 if (symbol_conf
.use_callchain
)
368 ret
+= hist_entry__callchain_fprintf(he
, hists
, fp
);
373 size_t hists__fprintf(struct hists
*hists
, bool show_header
, int max_rows
,
374 int max_cols
, float min_pcnt
, FILE *fp
)
376 struct perf_hpp_fmt
*fmt
;
380 const char *sep
= symbol_conf
.field_sep
;
383 struct perf_hpp dummy_hpp
= {
393 perf_hpp__for_each_format(fmt
)
394 perf_hpp__reset_width(fmt
, hists
);
396 if (symbol_conf
.col_width_list_str
)
397 perf_hpp__set_user_width(symbol_conf
.col_width_list_str
);
404 perf_hpp__for_each_format(fmt
) {
405 if (perf_hpp__should_skip(fmt
))
409 fprintf(fp
, "%s", sep
?: " ");
413 fmt
->header(fmt
, &dummy_hpp
, hists_to_evsel(hists
));
414 fprintf(fp
, "%s", bf
);
418 if (max_rows
&& ++nr_rows
>= max_rows
)
428 perf_hpp__for_each_format(fmt
) {
431 if (perf_hpp__should_skip(fmt
))
435 fprintf(fp
, "%s", sep
?: " ");
439 width
= fmt
->width(fmt
, &dummy_hpp
, hists_to_evsel(hists
));
440 for (i
= 0; i
< width
; i
++)
445 if (max_rows
&& ++nr_rows
>= max_rows
)
449 if (max_rows
&& ++nr_rows
>= max_rows
)
453 linesz
= hists__sort_list_width(hists
) + 3 + 1;
454 linesz
+= perf_hpp__color_overhead();
455 line
= malloc(linesz
);
461 for (nd
= rb_first(&hists
->entries
); nd
; nd
= rb_next(nd
)) {
462 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
468 percent
= hist_entry__get_percent_limit(h
);
469 if (percent
< min_pcnt
)
472 ret
+= hist_entry__fprintf(h
, max_cols
, hists
, line
, linesz
, fp
);
474 if (max_rows
&& ++nr_rows
>= max_rows
)
477 if (h
->ms
.map
== NULL
&& verbose
> 1) {
478 __map_groups__fprintf_maps(h
->thread
->mg
,
480 fprintf(fp
, "%.10s end\n", graph_dotted_line
);
486 zfree(&rem_sq_bracket
);
491 size_t events_stats__fprintf(struct events_stats
*stats
, FILE *fp
)
496 for (i
= 0; i
< PERF_RECORD_HEADER_MAX
; ++i
) {
499 if (stats
->nr_events
[i
] == 0)
502 name
= perf_event__name(i
);
503 if (!strcmp(name
, "UNKNOWN"))
506 ret
+= fprintf(fp
, "%16s events: %10d\n", name
,
507 stats
->nr_events
[i
]);