aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2009-12-16 09:27:09 -0500
committerIngo Molnar <mingo@elte.hu>2009-12-16 10:51:50 -0500
commit4ecf84d086fbeca5a622e971fff013b291dbde86 (patch)
treeea482bdd8006c0318d2236737c5868af859c2179 /tools/perf
parentd599db3fc5dd4f1e8432fdbc6d899584b25f4dff (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')
-rw-r--r--tools/perf/builtin-report.c397
-rw-r--r--tools/perf/util/hist.c384
-rw-r--r--tools/perf/util/hist.h1
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
44static char callchain_default_opt[] = "fractal,0.5"; 44static char callchain_default_opt[] = "fractal,0.5";
45 45
46static size_t
47callchain__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
60static 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}
78static size_t
79ipchain__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
108static struct symbol *rem_sq_bracket;
109static struct callchain_list rem_hits;
110
111static 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
123static 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
203static size_t
204callchain__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
241static size_t
242callchain__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
267static size_t
268hist_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
302static 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
356static int perf_session__add_hist_entry(struct perf_session *self, 46static 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
384static 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
452print_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
471static int validate_chain(struct ip_callchain *chain, event_t *event) 74static 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
210static 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
221static 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
238static 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
268static struct symbol *rem_sq_bracket;
269static struct callchain_list rem_hits;
270
271static 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
283static 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
361static 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
398static 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
423static 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
457static 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
507size_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
575print_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
22void perf_session__output_resort(struct perf_session *self, u64 total_samples); 22void perf_session__output_resort(struct perf_session *self, u64 total_samples);
23void perf_session__collapse_resort(struct perf_session *self); 23void perf_session__collapse_resort(struct perf_session *self);
24size_t perf_session__fprintf_hists(struct perf_session *self, FILE *fp);
24 25
25#endif /* __PERF_HIST_H */ 26#endif /* __PERF_HIST_H */