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/perf/builtin-report.c | |
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/perf/builtin-report.c')
-rw-r--r-- | tools/perf/builtin-report.c | 397 |
1 files changed, 0 insertions, 397 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 26f4de6d9a51..24d20e7d125a 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; |