diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2009-12-16 09:27:09 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-12-16 10:51:50 -0500 |
commit | 4ecf84d086fbeca5a622e971fff013b291dbde86 (patch) | |
tree | ea482bdd8006c0318d2236737c5868af859c2179 /tools | |
parent | d599db3fc5dd4f1e8432fdbc6d899584b25f4dff (diff) |
perf tools: Move hist entries printing routines from perf report
Will be used in other tools such as 'perf diff'.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <1260973631-28035-1-git-send-email-acme@infradead.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/perf/builtin-report.c | 397 | ||||
-rw-r--r-- | tools/perf/util/hist.c | 384 | ||||
-rw-r--r-- | tools/perf/util/hist.h | 1 |
3 files changed, 385 insertions, 397 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 26f4de6d9a5..24d20e7d125 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -43,316 +43,6 @@ static char *pretty_printing_style = default_pretty_printing_style; | |||
43 | 43 | ||
44 | static char callchain_default_opt[] = "fractal,0.5"; | 44 | static char callchain_default_opt[] = "fractal,0.5"; |
45 | 45 | ||
46 | static size_t | ||
47 | callchain__fprintf_left_margin(FILE *fp, int left_margin) | ||
48 | { | ||
49 | int i; | ||
50 | int ret; | ||
51 | |||
52 | ret = fprintf(fp, " "); | ||
53 | |||
54 | for (i = 0; i < left_margin; i++) | ||
55 | ret += fprintf(fp, " "); | ||
56 | |||
57 | return ret; | ||
58 | } | ||
59 | |||
60 | static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, | ||
61 | int left_margin) | ||
62 | { | ||
63 | int i; | ||
64 | size_t ret = 0; | ||
65 | |||
66 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
67 | |||
68 | for (i = 0; i < depth; i++) | ||
69 | if (depth_mask & (1 << i)) | ||
70 | ret += fprintf(fp, "| "); | ||
71 | else | ||
72 | ret += fprintf(fp, " "); | ||
73 | |||
74 | ret += fprintf(fp, "\n"); | ||
75 | |||
76 | return ret; | ||
77 | } | ||
78 | static size_t | ||
79 | ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth, | ||
80 | int depth_mask, int count, u64 total_samples, | ||
81 | int hits, int left_margin) | ||
82 | { | ||
83 | int i; | ||
84 | size_t ret = 0; | ||
85 | |||
86 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
87 | for (i = 0; i < depth; i++) { | ||
88 | if (depth_mask & (1 << i)) | ||
89 | ret += fprintf(fp, "|"); | ||
90 | else | ||
91 | ret += fprintf(fp, " "); | ||
92 | if (!count && i == depth - 1) { | ||
93 | double percent; | ||
94 | |||
95 | percent = hits * 100.0 / total_samples; | ||
96 | ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); | ||
97 | } else | ||
98 | ret += fprintf(fp, "%s", " "); | ||
99 | } | ||
100 | if (chain->sym) | ||
101 | ret += fprintf(fp, "%s\n", chain->sym->name); | ||
102 | else | ||
103 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); | ||
104 | |||
105 | return ret; | ||
106 | } | ||
107 | |||
108 | static struct symbol *rem_sq_bracket; | ||
109 | static struct callchain_list rem_hits; | ||
110 | |||
111 | static void init_rem_hits(void) | ||
112 | { | ||
113 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | ||
114 | if (!rem_sq_bracket) { | ||
115 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | ||
116 | return; | ||
117 | } | ||
118 | |||
119 | strcpy(rem_sq_bracket->name, "[...]"); | ||
120 | rem_hits.sym = rem_sq_bracket; | ||
121 | } | ||
122 | |||
123 | static size_t | ||
124 | __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
125 | u64 total_samples, int depth, int depth_mask, | ||
126 | int left_margin) | ||
127 | { | ||
128 | struct rb_node *node, *next; | ||
129 | struct callchain_node *child; | ||
130 | struct callchain_list *chain; | ||
131 | int new_depth_mask = depth_mask; | ||
132 | u64 new_total; | ||
133 | u64 remaining; | ||
134 | size_t ret = 0; | ||
135 | int i; | ||
136 | |||
137 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
138 | new_total = self->children_hit; | ||
139 | else | ||
140 | new_total = total_samples; | ||
141 | |||
142 | remaining = new_total; | ||
143 | |||
144 | node = rb_first(&self->rb_root); | ||
145 | while (node) { | ||
146 | u64 cumul; | ||
147 | |||
148 | child = rb_entry(node, struct callchain_node, rb_node); | ||
149 | cumul = cumul_hits(child); | ||
150 | remaining -= cumul; | ||
151 | |||
152 | /* | ||
153 | * The depth mask manages the output of pipes that show | ||
154 | * the depth. We don't want to keep the pipes of the current | ||
155 | * level for the last child of this depth. | ||
156 | * Except if we have remaining filtered hits. They will | ||
157 | * supersede the last child | ||
158 | */ | ||
159 | next = rb_next(node); | ||
160 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) | ||
161 | new_depth_mask &= ~(1 << (depth - 1)); | ||
162 | |||
163 | /* | ||
164 | * But we keep the older depth mask for the line seperator | ||
165 | * to keep the level link until we reach the last child | ||
166 | */ | ||
167 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, | ||
168 | left_margin); | ||
169 | i = 0; | ||
170 | list_for_each_entry(chain, &child->val, list) { | ||
171 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
172 | continue; | ||
173 | ret += ipchain__fprintf_graph(fp, chain, depth, | ||
174 | new_depth_mask, i++, | ||
175 | new_total, | ||
176 | cumul, | ||
177 | left_margin); | ||
178 | } | ||
179 | ret += __callchain__fprintf_graph(fp, child, new_total, | ||
180 | depth + 1, | ||
181 | new_depth_mask | (1 << depth), | ||
182 | left_margin); | ||
183 | node = next; | ||
184 | } | ||
185 | |||
186 | if (callchain_param.mode == CHAIN_GRAPH_REL && | ||
187 | remaining && remaining != new_total) { | ||
188 | |||
189 | if (!rem_sq_bracket) | ||
190 | return ret; | ||
191 | |||
192 | new_depth_mask &= ~(1 << (depth - 1)); | ||
193 | |||
194 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | ||
195 | new_depth_mask, 0, new_total, | ||
196 | remaining, left_margin); | ||
197 | } | ||
198 | |||
199 | return ret; | ||
200 | } | ||
201 | |||
202 | |||
203 | static size_t | ||
204 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
205 | u64 total_samples, int left_margin) | ||
206 | { | ||
207 | struct callchain_list *chain; | ||
208 | bool printed = false; | ||
209 | int i = 0; | ||
210 | int ret = 0; | ||
211 | |||
212 | list_for_each_entry(chain, &self->val, list) { | ||
213 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
214 | continue; | ||
215 | |||
216 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
217 | continue; | ||
218 | |||
219 | if (!printed) { | ||
220 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
221 | ret += fprintf(fp, "|\n"); | ||
222 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
223 | ret += fprintf(fp, "---"); | ||
224 | |||
225 | left_margin += 3; | ||
226 | printed = true; | ||
227 | } else | ||
228 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
229 | |||
230 | if (chain->sym) | ||
231 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
232 | else | ||
233 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | ||
234 | } | ||
235 | |||
236 | ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); | ||
237 | |||
238 | return ret; | ||
239 | } | ||
240 | |||
241 | static size_t | ||
242 | callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | ||
243 | u64 total_samples) | ||
244 | { | ||
245 | struct callchain_list *chain; | ||
246 | size_t ret = 0; | ||
247 | |||
248 | if (!self) | ||
249 | return 0; | ||
250 | |||
251 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); | ||
252 | |||
253 | |||
254 | list_for_each_entry(chain, &self->val, list) { | ||
255 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
256 | continue; | ||
257 | if (chain->sym) | ||
258 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
259 | else | ||
260 | ret += fprintf(fp, " %p\n", | ||
261 | (void *)(long)chain->ip); | ||
262 | } | ||
263 | |||
264 | return ret; | ||
265 | } | ||
266 | |||
267 | static size_t | ||
268 | hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | ||
269 | u64 total_samples, int left_margin) | ||
270 | { | ||
271 | struct rb_node *rb_node; | ||
272 | struct callchain_node *chain; | ||
273 | size_t ret = 0; | ||
274 | |||
275 | rb_node = rb_first(&self->sorted_chain); | ||
276 | while (rb_node) { | ||
277 | double percent; | ||
278 | |||
279 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | ||
280 | percent = chain->hit * 100.0 / total_samples; | ||
281 | switch (callchain_param.mode) { | ||
282 | case CHAIN_FLAT: | ||
283 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | ||
284 | percent); | ||
285 | ret += callchain__fprintf_flat(fp, chain, total_samples); | ||
286 | break; | ||
287 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
288 | case CHAIN_GRAPH_REL: | ||
289 | ret += callchain__fprintf_graph(fp, chain, total_samples, | ||
290 | left_margin); | ||
291 | case CHAIN_NONE: | ||
292 | default: | ||
293 | break; | ||
294 | } | ||
295 | ret += fprintf(fp, "\n"); | ||
296 | rb_node = rb_next(rb_node); | ||
297 | } | ||
298 | |||
299 | return ret; | ||
300 | } | ||
301 | |||
302 | static size_t hist_entry__fprintf(FILE *fp, struct hist_entry *self, | ||
303 | struct perf_session *session) | ||
304 | { | ||
305 | struct sort_entry *se; | ||
306 | size_t ret; | ||
307 | |||
308 | if (symbol_conf.exclude_other && !self->parent) | ||
309 | return 0; | ||
310 | |||
311 | if (session->events_stats.total) | ||
312 | ret = percent_color_fprintf(fp, | ||
313 | symbol_conf.field_sep ? "%.2f" : " %6.2f%%", | ||
314 | (self->count * 100.0) / session->events_stats.total); | ||
315 | else | ||
316 | ret = fprintf(fp, symbol_conf.field_sep ? "%lld" : "%12lld ", self->count); | ||
317 | |||
318 | if (symbol_conf.show_nr_samples) { | ||
319 | if (symbol_conf.field_sep) | ||
320 | fprintf(fp, "%c%lld", *symbol_conf.field_sep, self->count); | ||
321 | else | ||
322 | fprintf(fp, "%11lld", self->count); | ||
323 | } | ||
324 | |||
325 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
326 | if (se->elide) | ||
327 | continue; | ||
328 | |||
329 | fprintf(fp, "%s", symbol_conf.field_sep ?: " "); | ||
330 | ret += se->print(fp, self, se->width ? *se->width : 0); | ||
331 | } | ||
332 | |||
333 | ret += fprintf(fp, "\n"); | ||
334 | |||
335 | if (symbol_conf.use_callchain) { | ||
336 | int left_margin = 0; | ||
337 | |||
338 | if (sort__first_dimension == SORT_COMM) { | ||
339 | se = list_first_entry(&hist_entry__sort_list, typeof(*se), | ||
340 | list); | ||
341 | left_margin = se->width ? *se->width : 0; | ||
342 | left_margin -= thread__comm_len(self->thread); | ||
343 | } | ||
344 | |||
345 | hist_entry_callchain__fprintf(fp, self, session->events_stats.total, | ||
346 | left_margin); | ||
347 | } | ||
348 | |||
349 | return ret; | ||
350 | } | ||
351 | |||
352 | /* | ||
353 | * collect histogram counts | ||
354 | */ | ||
355 | |||
356 | static int perf_session__add_hist_entry(struct perf_session *self, | 46 | static int perf_session__add_hist_entry(struct perf_session *self, |
357 | struct addr_location *al, | 47 | struct addr_location *al, |
358 | struct ip_callchain *chain, u64 count) | 48 | struct ip_callchain *chain, u64 count) |
@@ -381,93 +71,6 @@ static int perf_session__add_hist_entry(struct perf_session *self, | |||
381 | return 0; | 71 | return 0; |
382 | } | 72 | } |
383 | 73 | ||
384 | static size_t perf_session__fprintf_hists(struct perf_session *self, FILE *fp) | ||
385 | { | ||
386 | struct hist_entry *pos; | ||
387 | struct sort_entry *se; | ||
388 | struct rb_node *nd; | ||
389 | size_t ret = 0; | ||
390 | unsigned int width; | ||
391 | char *col_width = symbol_conf.col_width_list_str; | ||
392 | |||
393 | init_rem_hits(); | ||
394 | |||
395 | fprintf(fp, "# Samples: %ld\n", self->events_stats.total); | ||
396 | fprintf(fp, "#\n"); | ||
397 | |||
398 | fprintf(fp, "# Overhead"); | ||
399 | if (symbol_conf.show_nr_samples) { | ||
400 | if (symbol_conf.field_sep) | ||
401 | fprintf(fp, "%cSamples", *symbol_conf.field_sep); | ||
402 | else | ||
403 | fputs(" Samples ", fp); | ||
404 | } | ||
405 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
406 | if (se->elide) | ||
407 | continue; | ||
408 | if (symbol_conf.field_sep) { | ||
409 | fprintf(fp, "%c%s", *symbol_conf.field_sep, se->header); | ||
410 | continue; | ||
411 | } | ||
412 | width = strlen(se->header); | ||
413 | if (se->width) { | ||
414 | if (symbol_conf.col_width_list_str) { | ||
415 | if (col_width) { | ||
416 | *se->width = atoi(col_width); | ||
417 | col_width = strchr(col_width, ','); | ||
418 | if (col_width) | ||
419 | ++col_width; | ||
420 | } | ||
421 | } | ||
422 | width = *se->width = max(*se->width, width); | ||
423 | } | ||
424 | fprintf(fp, " %*s", width, se->header); | ||
425 | } | ||
426 | fprintf(fp, "\n"); | ||
427 | |||
428 | if (symbol_conf.field_sep) | ||
429 | goto print_entries; | ||
430 | |||
431 | fprintf(fp, "# ........"); | ||
432 | if (symbol_conf.show_nr_samples) | ||
433 | fprintf(fp, " .........."); | ||
434 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
435 | unsigned int i; | ||
436 | |||
437 | if (se->elide) | ||
438 | continue; | ||
439 | |||
440 | fprintf(fp, " "); | ||
441 | if (se->width) | ||
442 | width = *se->width; | ||
443 | else | ||
444 | width = strlen(se->header); | ||
445 | for (i = 0; i < width; i++) | ||
446 | fprintf(fp, "."); | ||
447 | } | ||
448 | fprintf(fp, "\n"); | ||
449 | |||
450 | fprintf(fp, "#\n"); | ||
451 | |||
452 | print_entries: | ||
453 | for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { | ||
454 | pos = rb_entry(nd, struct hist_entry, rb_node); | ||
455 | ret += hist_entry__fprintf(fp, pos, self); | ||
456 | } | ||
457 | |||
458 | if (sort_order == default_sort_order && | ||
459 | parent_pattern == default_parent_pattern) { | ||
460 | fprintf(fp, "#\n"); | ||
461 | fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n"); | ||
462 | fprintf(fp, "#\n"); | ||
463 | } | ||
464 | fprintf(fp, "\n"); | ||
465 | |||
466 | free(rem_sq_bracket); | ||
467 | |||
468 | return ret; | ||
469 | } | ||
470 | |||
471 | static int validate_chain(struct ip_callchain *chain, event_t *event) | 74 | static int validate_chain(struct ip_callchain *chain, event_t *event) |
472 | { | 75 | { |
473 | unsigned int chain_size; | 76 | unsigned int chain_size; |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index d9a5a19391d..270eb8f3dce 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -206,3 +206,387 @@ void perf_session__output_resort(struct perf_session *self, u64 total_samples) | |||
206 | 206 | ||
207 | self->hists = tmp; | 207 | self->hists = tmp; |
208 | } | 208 | } |
209 | |||
210 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) | ||
211 | { | ||
212 | int i; | ||
213 | int ret = fprintf(fp, " "); | ||
214 | |||
215 | for (i = 0; i < left_margin; i++) | ||
216 | ret += fprintf(fp, " "); | ||
217 | |||
218 | return ret; | ||
219 | } | ||
220 | |||
221 | static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, | ||
222 | int left_margin) | ||
223 | { | ||
224 | int i; | ||
225 | size_t ret = callchain__fprintf_left_margin(fp, left_margin); | ||
226 | |||
227 | for (i = 0; i < depth; i++) | ||
228 | if (depth_mask & (1 << i)) | ||
229 | ret += fprintf(fp, "| "); | ||
230 | else | ||
231 | ret += fprintf(fp, " "); | ||
232 | |||
233 | ret += fprintf(fp, "\n"); | ||
234 | |||
235 | return ret; | ||
236 | } | ||
237 | |||
238 | static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, | ||
239 | int depth, int depth_mask, int count, | ||
240 | u64 total_samples, int hits, | ||
241 | int left_margin) | ||
242 | { | ||
243 | int i; | ||
244 | size_t ret = 0; | ||
245 | |||
246 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
247 | for (i = 0; i < depth; i++) { | ||
248 | if (depth_mask & (1 << i)) | ||
249 | ret += fprintf(fp, "|"); | ||
250 | else | ||
251 | ret += fprintf(fp, " "); | ||
252 | if (!count && i == depth - 1) { | ||
253 | double percent; | ||
254 | |||
255 | percent = hits * 100.0 / total_samples; | ||
256 | ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); | ||
257 | } else | ||
258 | ret += fprintf(fp, "%s", " "); | ||
259 | } | ||
260 | if (chain->sym) | ||
261 | ret += fprintf(fp, "%s\n", chain->sym->name); | ||
262 | else | ||
263 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); | ||
264 | |||
265 | return ret; | ||
266 | } | ||
267 | |||
268 | static struct symbol *rem_sq_bracket; | ||
269 | static struct callchain_list rem_hits; | ||
270 | |||
271 | static void init_rem_hits(void) | ||
272 | { | ||
273 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | ||
274 | if (!rem_sq_bracket) { | ||
275 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | ||
276 | return; | ||
277 | } | ||
278 | |||
279 | strcpy(rem_sq_bracket->name, "[...]"); | ||
280 | rem_hits.sym = rem_sq_bracket; | ||
281 | } | ||
282 | |||
283 | static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
284 | u64 total_samples, int depth, | ||
285 | int depth_mask, int left_margin) | ||
286 | { | ||
287 | struct rb_node *node, *next; | ||
288 | struct callchain_node *child; | ||
289 | struct callchain_list *chain; | ||
290 | int new_depth_mask = depth_mask; | ||
291 | u64 new_total; | ||
292 | u64 remaining; | ||
293 | size_t ret = 0; | ||
294 | int i; | ||
295 | |||
296 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
297 | new_total = self->children_hit; | ||
298 | else | ||
299 | new_total = total_samples; | ||
300 | |||
301 | remaining = new_total; | ||
302 | |||
303 | node = rb_first(&self->rb_root); | ||
304 | while (node) { | ||
305 | u64 cumul; | ||
306 | |||
307 | child = rb_entry(node, struct callchain_node, rb_node); | ||
308 | cumul = cumul_hits(child); | ||
309 | remaining -= cumul; | ||
310 | |||
311 | /* | ||
312 | * The depth mask manages the output of pipes that show | ||
313 | * the depth. We don't want to keep the pipes of the current | ||
314 | * level for the last child of this depth. | ||
315 | * Except if we have remaining filtered hits. They will | ||
316 | * supersede the last child | ||
317 | */ | ||
318 | next = rb_next(node); | ||
319 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) | ||
320 | new_depth_mask &= ~(1 << (depth - 1)); | ||
321 | |||
322 | /* | ||
323 | * But we keep the older depth mask for the line seperator | ||
324 | * to keep the level link until we reach the last child | ||
325 | */ | ||
326 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, | ||
327 | left_margin); | ||
328 | i = 0; | ||
329 | list_for_each_entry(chain, &child->val, list) { | ||
330 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
331 | continue; | ||
332 | ret += ipchain__fprintf_graph(fp, chain, depth, | ||
333 | new_depth_mask, i++, | ||
334 | new_total, | ||
335 | cumul, | ||
336 | left_margin); | ||
337 | } | ||
338 | ret += __callchain__fprintf_graph(fp, child, new_total, | ||
339 | depth + 1, | ||
340 | new_depth_mask | (1 << depth), | ||
341 | left_margin); | ||
342 | node = next; | ||
343 | } | ||
344 | |||
345 | if (callchain_param.mode == CHAIN_GRAPH_REL && | ||
346 | remaining && remaining != new_total) { | ||
347 | |||
348 | if (!rem_sq_bracket) | ||
349 | return ret; | ||
350 | |||
351 | new_depth_mask &= ~(1 << (depth - 1)); | ||
352 | |||
353 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | ||
354 | new_depth_mask, 0, new_total, | ||
355 | remaining, left_margin); | ||
356 | } | ||
357 | |||
358 | return ret; | ||
359 | } | ||
360 | |||
361 | static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
362 | u64 total_samples, int left_margin) | ||
363 | { | ||
364 | struct callchain_list *chain; | ||
365 | bool printed = false; | ||
366 | int i = 0; | ||
367 | int ret = 0; | ||
368 | |||
369 | list_for_each_entry(chain, &self->val, list) { | ||
370 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
371 | continue; | ||
372 | |||
373 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
374 | continue; | ||
375 | |||
376 | if (!printed) { | ||
377 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
378 | ret += fprintf(fp, "|\n"); | ||
379 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
380 | ret += fprintf(fp, "---"); | ||
381 | |||
382 | left_margin += 3; | ||
383 | printed = true; | ||
384 | } else | ||
385 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
386 | |||
387 | if (chain->sym) | ||
388 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
389 | else | ||
390 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | ||
391 | } | ||
392 | |||
393 | ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); | ||
394 | |||
395 | return ret; | ||
396 | } | ||
397 | |||
398 | static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | ||
399 | u64 total_samples) | ||
400 | { | ||
401 | struct callchain_list *chain; | ||
402 | size_t ret = 0; | ||
403 | |||
404 | if (!self) | ||
405 | return 0; | ||
406 | |||
407 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); | ||
408 | |||
409 | |||
410 | list_for_each_entry(chain, &self->val, list) { | ||
411 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
412 | continue; | ||
413 | if (chain->sym) | ||
414 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
415 | else | ||
416 | ret += fprintf(fp, " %p\n", | ||
417 | (void *)(long)chain->ip); | ||
418 | } | ||
419 | |||
420 | return ret; | ||
421 | } | ||
422 | |||
423 | static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | ||
424 | u64 total_samples, int left_margin) | ||
425 | { | ||
426 | struct rb_node *rb_node; | ||
427 | struct callchain_node *chain; | ||
428 | size_t ret = 0; | ||
429 | |||
430 | rb_node = rb_first(&self->sorted_chain); | ||
431 | while (rb_node) { | ||
432 | double percent; | ||
433 | |||
434 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | ||
435 | percent = chain->hit * 100.0 / total_samples; | ||
436 | switch (callchain_param.mode) { | ||
437 | case CHAIN_FLAT: | ||
438 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | ||
439 | percent); | ||
440 | ret += callchain__fprintf_flat(fp, chain, total_samples); | ||
441 | break; | ||
442 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
443 | case CHAIN_GRAPH_REL: | ||
444 | ret += callchain__fprintf_graph(fp, chain, total_samples, | ||
445 | left_margin); | ||
446 | case CHAIN_NONE: | ||
447 | default: | ||
448 | break; | ||
449 | } | ||
450 | ret += fprintf(fp, "\n"); | ||
451 | rb_node = rb_next(rb_node); | ||
452 | } | ||
453 | |||
454 | return ret; | ||
455 | } | ||
456 | |||
457 | static size_t hist_entry__fprintf(FILE *fp, struct hist_entry *self, | ||
458 | struct perf_session *session) | ||
459 | { | ||
460 | struct sort_entry *se; | ||
461 | size_t ret; | ||
462 | |||
463 | if (symbol_conf.exclude_other && !self->parent) | ||
464 | return 0; | ||
465 | |||
466 | if (session->events_stats.total) | ||
467 | ret = percent_color_fprintf(fp, | ||
468 | symbol_conf.field_sep ? "%.2f" : " %6.2f%%", | ||
469 | (self->count * 100.0) / session->events_stats.total); | ||
470 | else | ||
471 | ret = fprintf(fp, symbol_conf.field_sep ? "%lld" : "%12lld ", self->count); | ||
472 | |||
473 | if (symbol_conf.show_nr_samples) { | ||
474 | if (symbol_conf.field_sep) | ||
475 | fprintf(fp, "%c%lld", *symbol_conf.field_sep, self->count); | ||
476 | else | ||
477 | fprintf(fp, "%11lld", self->count); | ||
478 | } | ||
479 | |||
480 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
481 | if (se->elide) | ||
482 | continue; | ||
483 | |||
484 | fprintf(fp, "%s", symbol_conf.field_sep ?: " "); | ||
485 | ret += se->print(fp, self, se->width ? *se->width : 0); | ||
486 | } | ||
487 | |||
488 | ret += fprintf(fp, "\n"); | ||
489 | |||
490 | if (symbol_conf.use_callchain) { | ||
491 | int left_margin = 0; | ||
492 | |||
493 | if (sort__first_dimension == SORT_COMM) { | ||
494 | se = list_first_entry(&hist_entry__sort_list, typeof(*se), | ||
495 | list); | ||
496 | left_margin = se->width ? *se->width : 0; | ||
497 | left_margin -= thread__comm_len(self->thread); | ||
498 | } | ||
499 | |||
500 | hist_entry_callchain__fprintf(fp, self, session->events_stats.total, | ||
501 | left_margin); | ||
502 | } | ||
503 | |||
504 | return ret; | ||
505 | } | ||
506 | |||
507 | size_t perf_session__fprintf_hists(struct perf_session *self, FILE *fp) | ||
508 | { | ||
509 | struct hist_entry *pos; | ||
510 | struct sort_entry *se; | ||
511 | struct rb_node *nd; | ||
512 | size_t ret = 0; | ||
513 | unsigned int width; | ||
514 | char *col_width = symbol_conf.col_width_list_str; | ||
515 | |||
516 | init_rem_hits(); | ||
517 | |||
518 | fprintf(fp, "# Samples: %ld\n", self->events_stats.total); | ||
519 | fprintf(fp, "#\n"); | ||
520 | |||
521 | fprintf(fp, "# Overhead"); | ||
522 | if (symbol_conf.show_nr_samples) { | ||
523 | if (symbol_conf.field_sep) | ||
524 | fprintf(fp, "%cSamples", *symbol_conf.field_sep); | ||
525 | else | ||
526 | fputs(" Samples ", fp); | ||
527 | } | ||
528 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
529 | if (se->elide) | ||
530 | continue; | ||
531 | if (symbol_conf.field_sep) { | ||
532 | fprintf(fp, "%c%s", *symbol_conf.field_sep, se->header); | ||
533 | continue; | ||
534 | } | ||
535 | width = strlen(se->header); | ||
536 | if (se->width) { | ||
537 | if (symbol_conf.col_width_list_str) { | ||
538 | if (col_width) { | ||
539 | *se->width = atoi(col_width); | ||
540 | col_width = strchr(col_width, ','); | ||
541 | if (col_width) | ||
542 | ++col_width; | ||
543 | } | ||
544 | } | ||
545 | width = *se->width = max(*se->width, width); | ||
546 | } | ||
547 | fprintf(fp, " %*s", width, se->header); | ||
548 | } | ||
549 | fprintf(fp, "\n"); | ||
550 | |||
551 | if (symbol_conf.field_sep) | ||
552 | goto print_entries; | ||
553 | |||
554 | fprintf(fp, "# ........"); | ||
555 | if (symbol_conf.show_nr_samples) | ||
556 | fprintf(fp, " .........."); | ||
557 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
558 | unsigned int i; | ||
559 | |||
560 | if (se->elide) | ||
561 | continue; | ||
562 | |||
563 | fprintf(fp, " "); | ||
564 | if (se->width) | ||
565 | width = *se->width; | ||
566 | else | ||
567 | width = strlen(se->header); | ||
568 | for (i = 0; i < width; i++) | ||
569 | fprintf(fp, "."); | ||
570 | } | ||
571 | fprintf(fp, "\n"); | ||
572 | |||
573 | fprintf(fp, "#\n"); | ||
574 | |||
575 | print_entries: | ||
576 | for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { | ||
577 | pos = rb_entry(nd, struct hist_entry, rb_node); | ||
578 | ret += hist_entry__fprintf(fp, pos, self); | ||
579 | } | ||
580 | |||
581 | if (sort_order == default_sort_order && | ||
582 | parent_pattern == default_parent_pattern) { | ||
583 | fprintf(fp, "#\n"); | ||
584 | fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n"); | ||
585 | fprintf(fp, "#\n"); | ||
586 | } | ||
587 | fprintf(fp, "\n"); | ||
588 | |||
589 | free(rem_sq_bracket); | ||
590 | |||
591 | return ret; | ||
592 | } | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 7efdb1b6d8c..c7ac78d93b0 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -21,5 +21,6 @@ void hist_entry__free(struct hist_entry *); | |||
21 | 21 | ||
22 | void perf_session__output_resort(struct perf_session *self, u64 total_samples); | 22 | void perf_session__output_resort(struct perf_session *self, u64 total_samples); |
23 | void perf_session__collapse_resort(struct perf_session *self); | 23 | void perf_session__collapse_resort(struct perf_session *self); |
24 | size_t perf_session__fprintf_hists(struct perf_session *self, FILE *fp); | ||
24 | 25 | ||
25 | #endif /* __PERF_HIST_H */ | 26 | #endif /* __PERF_HIST_H */ |