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
,
45 ret
+= callchain__fprintf_left_margin(fp
, left_margin
);
46 for (i
= 0; i
< depth
; i
++) {
47 if (depth_mask
& (1 << i
))
48 ret
+= fprintf(fp
, "|");
50 ret
+= fprintf(fp
, " ");
51 if (!period
&& i
== depth
- 1) {
54 percent
= hits
* 100.0 / total_samples
;
55 ret
+= percent_color_fprintf(fp
, "--%2.2f%%-- ", percent
);
57 ret
+= fprintf(fp
, "%s", " ");
60 ret
+= fprintf(fp
, "%s\n", chain
->ms
.sym
->name
);
62 ret
+= fprintf(fp
, "0x%0" PRIx64
"\n", chain
->ip
);
67 static struct symbol
*rem_sq_bracket
;
68 static struct callchain_list rem_hits
;
70 static void init_rem_hits(void)
72 rem_sq_bracket
= malloc(sizeof(*rem_sq_bracket
) + 6);
73 if (!rem_sq_bracket
) {
74 fprintf(stderr
, "Not enough memory to display remaining hits\n");
78 strcpy(rem_sq_bracket
->name
, "[...]");
79 rem_hits
.ms
.sym
= rem_sq_bracket
;
82 static size_t __callchain__fprintf_graph(FILE *fp
, struct rb_root
*root
,
83 u64 total_samples
, int depth
,
84 int depth_mask
, int left_margin
)
86 struct rb_node
*node
, *next
;
87 struct callchain_node
*child
;
88 struct callchain_list
*chain
;
89 int new_depth_mask
= depth_mask
;
93 uint entries_printed
= 0;
95 remaining
= total_samples
;
97 node
= rb_first(root
);
102 child
= rb_entry(node
, struct callchain_node
, rb_node
);
103 cumul
= callchain_cumul_hits(child
);
107 * The depth mask manages the output of pipes that show
108 * the depth. We don't want to keep the pipes of the current
109 * level for the last child of this depth.
110 * Except if we have remaining filtered hits. They will
111 * supersede the last child
113 next
= rb_next(node
);
114 if (!next
&& (callchain_param
.mode
!= CHAIN_GRAPH_REL
|| !remaining
))
115 new_depth_mask
&= ~(1 << (depth
- 1));
118 * But we keep the older depth mask for the line separator
119 * to keep the level link until we reach the last child
121 ret
+= ipchain__fprintf_graph_line(fp
, depth
, depth_mask
,
124 list_for_each_entry(chain
, &child
->val
, list
) {
125 ret
+= ipchain__fprintf_graph(fp
, chain
, depth
,
132 if (callchain_param
.mode
== CHAIN_GRAPH_REL
)
133 new_total
= child
->children_hit
;
135 new_total
= total_samples
;
137 ret
+= __callchain__fprintf_graph(fp
, &child
->rb_root
, new_total
,
139 new_depth_mask
| (1 << depth
),
142 if (++entries_printed
== callchain_param
.print_limit
)
146 if (callchain_param
.mode
== CHAIN_GRAPH_REL
&&
147 remaining
&& remaining
!= total_samples
) {
152 new_depth_mask
&= ~(1 << (depth
- 1));
153 ret
+= ipchain__fprintf_graph(fp
, &rem_hits
, depth
,
154 new_depth_mask
, 0, total_samples
,
155 remaining
, left_margin
);
161 static size_t callchain__fprintf_graph(FILE *fp
, struct rb_root
*root
,
162 u64 total_samples
, int left_margin
)
164 struct callchain_node
*cnode
;
165 struct callchain_list
*chain
;
166 u32 entries_printed
= 0;
167 bool printed
= false;
168 struct rb_node
*node
;
173 * If have one single callchain root, don't bother printing
174 * its percentage (100 % in fractal mode and the same percentage
175 * than the hist in graph mode). This also avoid one level of column.
177 node
= rb_first(root
);
178 if (node
&& !rb_next(node
)) {
179 cnode
= rb_entry(node
, struct callchain_node
, rb_node
);
180 list_for_each_entry(chain
, &cnode
->val
, list
) {
182 * If we sort by symbol, the first entry is the same than
183 * the symbol. No need to print it otherwise it appears as
186 if (!i
++ && sort__first_dimension
== SORT_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
);
199 ret
+= fprintf(fp
, " %s\n", chain
->ms
.sym
->name
);
201 ret
+= fprintf(fp
, " %p\n", (void *)(long)chain
->ip
);
203 if (++entries_printed
== callchain_param
.print_limit
)
206 root
= &cnode
->rb_root
;
209 ret
+= __callchain__fprintf_graph(fp
, root
, total_samples
,
211 ret
+= fprintf(fp
, "\n");
216 static size_t __callchain__fprintf_flat(FILE *fp
, struct callchain_node
*node
,
219 struct callchain_list
*chain
;
225 ret
+= __callchain__fprintf_flat(fp
, node
->parent
, total_samples
);
228 list_for_each_entry(chain
, &node
->val
, list
) {
229 if (chain
->ip
>= PERF_CONTEXT_MAX
)
232 ret
+= fprintf(fp
, " %s\n", chain
->ms
.sym
->name
);
234 ret
+= fprintf(fp
, " %p\n",
235 (void *)(long)chain
->ip
);
241 static size_t callchain__fprintf_flat(FILE *fp
, struct rb_root
*tree
,
245 u32 entries_printed
= 0;
246 struct callchain_node
*chain
;
247 struct rb_node
*rb_node
= rb_first(tree
);
252 chain
= rb_entry(rb_node
, struct callchain_node
, rb_node
);
253 percent
= chain
->hit
* 100.0 / total_samples
;
255 ret
= percent_color_fprintf(fp
, " %6.2f%%\n", percent
);
256 ret
+= __callchain__fprintf_flat(fp
, chain
, total_samples
);
257 ret
+= fprintf(fp
, "\n");
258 if (++entries_printed
== callchain_param
.print_limit
)
261 rb_node
= rb_next(rb_node
);
267 static size_t hist_entry_callchain__fprintf(struct hist_entry
*he
,
268 u64 total_samples
, int left_margin
,
271 switch (callchain_param
.mode
) {
272 case CHAIN_GRAPH_REL
:
273 return callchain__fprintf_graph(fp
, &he
->sorted_chain
, he
->stat
.period
,
276 case CHAIN_GRAPH_ABS
:
277 return callchain__fprintf_graph(fp
, &he
->sorted_chain
, total_samples
,
281 return callchain__fprintf_flat(fp
, &he
->sorted_chain
, total_samples
);
286 pr_err("Bad callchain mode\n");
292 static size_t hist_entry__callchain_fprintf(struct hist_entry
*he
,
297 u64 total_period
= hists
->stats
.total_period
;
299 if (sort__first_dimension
== SORT_COMM
) {
300 struct sort_entry
*se
= list_first_entry(&hist_entry__sort_list
,
302 left_margin
= hists__col_len(hists
, se
->se_width_idx
);
303 left_margin
-= thread__comm_len(he
->thread
);
306 return hist_entry_callchain__fprintf(he
, total_period
, left_margin
, fp
);
309 static inline void advance_hpp(struct perf_hpp
*hpp
, int inc
)
315 static int hist_entry__period_snprintf(struct perf_hpp
*hpp
,
316 struct hist_entry
*he
)
318 const char *sep
= symbol_conf
.field_sep
;
319 struct perf_hpp_fmt
*fmt
;
320 char *start
= hpp
->buf
;
324 if (symbol_conf
.exclude_other
&& !he
->parent
)
327 perf_hpp__for_each_format(fmt
) {
329 * If there's no field_sep, we still need
330 * to display initial ' '.
332 if (!sep
|| !first
) {
333 ret
= scnprintf(hpp
->buf
, hpp
->size
, "%s", sep
?: " ");
334 advance_hpp(hpp
, ret
);
338 if (perf_hpp__use_color() && fmt
->color
)
339 ret
= fmt
->color(fmt
, hpp
, he
);
341 ret
= fmt
->entry(fmt
, hpp
, he
);
343 advance_hpp(hpp
, ret
);
346 return hpp
->buf
- start
;
349 static int hist_entry__fprintf(struct hist_entry
*he
, size_t size
,
351 char *bf
, size_t bfsz
, FILE *fp
)
354 struct perf_hpp hpp
= {
359 if (size
== 0 || size
> bfsz
)
360 size
= hpp
.size
= bfsz
;
362 ret
= hist_entry__period_snprintf(&hpp
, he
);
363 hist_entry__sort_snprintf(he
, bf
+ ret
, size
- ret
, hists
);
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
;
377 struct sort_entry
*se
;
381 const char *sep
= symbol_conf
.field_sep
;
382 const char *col_width
= symbol_conf
.col_width_list_str
;
385 struct perf_hpp dummy_hpp
= {
388 .ptr
= hists_to_evsel(hists
),
401 perf_hpp__for_each_format(fmt
) {
403 fprintf(fp
, "%s", sep
?: " ");
407 fmt
->header(fmt
, &dummy_hpp
);
408 fprintf(fp
, "%s", bf
);
411 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
415 fprintf(fp
, "%c%s", *sep
, se
->se_header
);
418 width
= strlen(se
->se_header
);
419 if (symbol_conf
.col_width_list_str
) {
421 hists__set_col_len(hists
, se
->se_width_idx
,
423 col_width
= strchr(col_width
, ',');
428 if (!hists__new_col_len(hists
, se
->se_width_idx
, width
))
429 width
= hists__col_len(hists
, se
->se_width_idx
);
430 fprintf(fp
, " %*s", width
, se
->se_header
);
434 if (max_rows
&& ++nr_rows
>= max_rows
)
444 perf_hpp__for_each_format(fmt
) {
448 fprintf(fp
, "%s", sep
?: " ");
452 width
= fmt
->width(fmt
, &dummy_hpp
);
453 for (i
= 0; i
< width
; i
++)
457 list_for_each_entry(se
, &hist_entry__sort_list
, list
) {
464 width
= hists__col_len(hists
, se
->se_width_idx
);
466 width
= strlen(se
->se_header
);
467 for (i
= 0; i
< width
; i
++)
472 if (max_rows
&& ++nr_rows
>= max_rows
)
476 if (max_rows
&& ++nr_rows
>= max_rows
)
480 linesz
= hists__sort_list_width(hists
) + 3 + 1;
481 linesz
+= perf_hpp__color_overhead();
482 line
= malloc(linesz
);
488 for (nd
= rb_first(&hists
->entries
); nd
; nd
= rb_next(nd
)) {
489 struct hist_entry
*h
= rb_entry(nd
, struct hist_entry
, rb_node
);
490 float percent
= h
->stat
.period
* 100.0 /
491 hists
->stats
.total_period
;
496 if (percent
< min_pcnt
)
499 ret
+= hist_entry__fprintf(h
, max_cols
, hists
, line
, linesz
, fp
);
501 if (max_rows
&& ++nr_rows
>= max_rows
)
504 if (h
->ms
.map
== NULL
&& verbose
> 1) {
505 __map_groups__fprintf_maps(&h
->thread
->mg
,
506 MAP__FUNCTION
, verbose
, fp
);
507 fprintf(fp
, "%.10s end\n", graph_dotted_line
);
513 zfree(&rem_sq_bracket
);
518 size_t events_stats__fprintf(struct events_stats
*stats
, FILE *fp
)
523 for (i
= 0; i
< PERF_RECORD_HEADER_MAX
; ++i
) {
526 if (stats
->nr_events
[i
] == 0)
529 name
= perf_event__name(i
);
530 if (!strcmp(name
, "UNKNOWN"))
533 ret
+= fprintf(fp
, "%16s events: %10d\n", name
,
534 stats
->nr_events
[i
]);