]> git.karo-electronics.de Git - karo-tx-linux.git/blob - tools/perf/ui/stdio/hist.c
6128f485a3c502927d991b222913723578360580
[karo-tx-linux.git] / tools / perf / ui / stdio / hist.c
1 #include <stdio.h>
2
3 #include "../../util/util.h"
4 #include "../../util/hist.h"
5 #include "../../util/sort.h"
6 #include "../../util/evsel.h"
7
8
9 static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
10 {
11         int i;
12         int ret = fprintf(fp, "            ");
13
14         for (i = 0; i < left_margin; i++)
15                 ret += fprintf(fp, " ");
16
17         return ret;
18 }
19
20 static size_t inline__fprintf(struct map *map, u64 ip, int left_margin,
21                               int depth, int depth_mask, FILE *fp)
22 {
23         struct dso *dso;
24         struct inline_node *node;
25         struct inline_list *ilist;
26         int ret = 0, i;
27
28         if (map == NULL)
29                 return 0;
30
31         dso = map->dso;
32         if (dso == NULL)
33                 return 0;
34
35         if (dso->kernel != DSO_TYPE_USER)
36                 return 0;
37
38         node = dso__parse_addr_inlines(dso,
39                                        map__rip_2objdump(map, ip));
40         if (node == NULL)
41                 return 0;
42
43         list_for_each_entry(ilist, &node->val, list) {
44                 if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
45                         ret += callchain__fprintf_left_margin(fp, left_margin);
46
47                         for (i = 0; i < depth; i++) {
48                                 if (depth_mask & (1 << i))
49                                         ret += fprintf(fp, "|");
50                                 else
51                                         ret += fprintf(fp, " ");
52                                 ret += fprintf(fp, "          ");
53                         }
54
55                         if (callchain_param.key == CCKEY_ADDRESS) {
56                                 if (ilist->filename != NULL)
57                                         ret += fprintf(fp, "%s:%d (inline)",
58                                                        ilist->filename,
59                                                        ilist->line_nr);
60                                 else
61                                         ret += fprintf(fp, "??");
62                         } else if (ilist->funcname != NULL)
63                                 ret += fprintf(fp, "%s (inline)",
64                                                ilist->funcname);
65                         else if (ilist->filename != NULL)
66                                 ret += fprintf(fp, "%s:%d (inline)",
67                                                ilist->filename,
68                                                ilist->line_nr);
69                         else
70                                 ret += fprintf(fp, "??");
71
72                         ret += fprintf(fp, "\n");
73                 }
74         }
75
76         inline_node__delete(node);
77         return ret;
78 }
79
80 static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
81                                           int left_margin)
82 {
83         int i;
84         size_t ret = callchain__fprintf_left_margin(fp, left_margin);
85
86         for (i = 0; i < depth; i++)
87                 if (depth_mask & (1 << i))
88                         ret += fprintf(fp, "|          ");
89                 else
90                         ret += fprintf(fp, "           ");
91
92         ret += fprintf(fp, "\n");
93
94         return ret;
95 }
96
97 static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node,
98                                      struct callchain_list *chain,
99                                      int depth, int depth_mask, int period,
100                                      u64 total_samples, int left_margin)
101 {
102         int i;
103         size_t ret = 0;
104         char bf[1024], *alloc_str = NULL;
105         char buf[64];
106         const char *str;
107
108         ret += callchain__fprintf_left_margin(fp, left_margin);
109         for (i = 0; i < depth; i++) {
110                 if (depth_mask & (1 << i))
111                         ret += fprintf(fp, "|");
112                 else
113                         ret += fprintf(fp, " ");
114                 if (!period && i == depth - 1) {
115                         ret += fprintf(fp, "--");
116                         ret += callchain_node__fprintf_value(node, fp, total_samples);
117                         ret += fprintf(fp, "--");
118                 } else
119                         ret += fprintf(fp, "%s", "          ");
120         }
121
122         str = callchain_list__sym_name(chain, bf, sizeof(bf), false);
123
124         if (symbol_conf.show_branchflag_count) {
125                 if (!period)
126                         callchain_list_counts__printf_value(node, chain, NULL,
127                                                             buf, sizeof(buf));
128                 else
129                         callchain_list_counts__printf_value(NULL, chain, NULL,
130                                                             buf, sizeof(buf));
131
132                 if (asprintf(&alloc_str, "%s%s", str, buf) < 0)
133                         str = "Not enough memory!";
134                 else
135                         str = alloc_str;
136         }
137
138         fputs(str, fp);
139         fputc('\n', fp);
140         free(alloc_str);
141
142         if (symbol_conf.inline_name)
143                 ret += inline__fprintf(chain->ms.map, chain->ip,
144                                        left_margin, depth, depth_mask, fp);
145         return ret;
146 }
147
148 static struct symbol *rem_sq_bracket;
149 static struct callchain_list rem_hits;
150
151 static void init_rem_hits(void)
152 {
153         rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
154         if (!rem_sq_bracket) {
155                 fprintf(stderr, "Not enough memory to display remaining hits\n");
156                 return;
157         }
158
159         strcpy(rem_sq_bracket->name, "[...]");
160         rem_hits.ms.sym = rem_sq_bracket;
161 }
162
163 static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
164                                          u64 total_samples, int depth,
165                                          int depth_mask, int left_margin)
166 {
167         struct rb_node *node, *next;
168         struct callchain_node *child = NULL;
169         struct callchain_list *chain;
170         int new_depth_mask = depth_mask;
171         u64 remaining;
172         size_t ret = 0;
173         int i;
174         uint entries_printed = 0;
175         int cumul_count = 0;
176
177         remaining = total_samples;
178
179         node = rb_first(root);
180         while (node) {
181                 u64 new_total;
182                 u64 cumul;
183
184                 child = rb_entry(node, struct callchain_node, rb_node);
185                 cumul = callchain_cumul_hits(child);
186                 remaining -= cumul;
187                 cumul_count += callchain_cumul_counts(child);
188
189                 /*
190                  * The depth mask manages the output of pipes that show
191                  * the depth. We don't want to keep the pipes of the current
192                  * level for the last child of this depth.
193                  * Except if we have remaining filtered hits. They will
194                  * supersede the last child
195                  */
196                 next = rb_next(node);
197                 if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
198                         new_depth_mask &= ~(1 << (depth - 1));
199
200                 /*
201                  * But we keep the older depth mask for the line separator
202                  * to keep the level link until we reach the last child
203                  */
204                 ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
205                                                    left_margin);
206                 i = 0;
207                 list_for_each_entry(chain, &child->val, list) {
208                         ret += ipchain__fprintf_graph(fp, child, chain, depth,
209                                                       new_depth_mask, i++,
210                                                       total_samples,
211                                                       left_margin);
212                 }
213
214                 if (callchain_param.mode == CHAIN_GRAPH_REL)
215                         new_total = child->children_hit;
216                 else
217                         new_total = total_samples;
218
219                 ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
220                                                   depth + 1,
221                                                   new_depth_mask | (1 << depth),
222                                                   left_margin);
223                 node = next;
224                 if (++entries_printed == callchain_param.print_limit)
225                         break;
226         }
227
228         if (callchain_param.mode == CHAIN_GRAPH_REL &&
229                 remaining && remaining != total_samples) {
230                 struct callchain_node rem_node = {
231                         .hit = remaining,
232                 };
233
234                 if (!rem_sq_bracket)
235                         return ret;
236
237                 if (callchain_param.value == CCVAL_COUNT && child && child->parent) {
238                         rem_node.count = child->parent->children_count - cumul_count;
239                         if (rem_node.count <= 0)
240                                 return ret;
241                 }
242
243                 new_depth_mask &= ~(1 << (depth - 1));
244                 ret += ipchain__fprintf_graph(fp, &rem_node, &rem_hits, depth,
245                                               new_depth_mask, 0, total_samples,
246                                               left_margin);
247         }
248
249         return ret;
250 }
251
252 /*
253  * If have one single callchain root, don't bother printing
254  * its percentage (100 % in fractal mode and the same percentage
255  * than the hist in graph mode). This also avoid one level of column.
256  *
257  * However when percent-limit applied, it's possible that single callchain
258  * node have different (non-100% in fractal mode) percentage.
259  */
260 static bool need_percent_display(struct rb_node *node, u64 parent_samples)
261 {
262         struct callchain_node *cnode;
263
264         if (rb_next(node))
265                 return true;
266
267         cnode = rb_entry(node, struct callchain_node, rb_node);
268         return callchain_cumul_hits(cnode) != parent_samples;
269 }
270
271 static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
272                                        u64 total_samples, u64 parent_samples,
273                                        int left_margin)
274 {
275         struct callchain_node *cnode;
276         struct callchain_list *chain;
277         u32 entries_printed = 0;
278         bool printed = false;
279         struct rb_node *node;
280         int i = 0;
281         int ret = 0;
282         char bf[1024];
283
284         node = rb_first(root);
285         if (node && !need_percent_display(node, parent_samples)) {
286                 cnode = rb_entry(node, struct callchain_node, rb_node);
287                 list_for_each_entry(chain, &cnode->val, list) {
288                         /*
289                          * If we sort by symbol, the first entry is the same than
290                          * the symbol. No need to print it otherwise it appears as
291                          * displayed twice.
292                          */
293                         if (!i++ && field_order == NULL &&
294                             sort_order && !prefixcmp(sort_order, "sym"))
295                                 continue;
296
297                         if (!printed) {
298                                 ret += callchain__fprintf_left_margin(fp, left_margin);
299                                 ret += fprintf(fp, "|\n");
300                                 ret += callchain__fprintf_left_margin(fp, left_margin);
301                                 ret += fprintf(fp, "---");
302                                 left_margin += 3;
303                                 printed = true;
304                         } else
305                                 ret += callchain__fprintf_left_margin(fp, left_margin);
306
307                         ret += fprintf(fp, "%s",
308                                        callchain_list__sym_name(chain, bf,
309                                                                 sizeof(bf),
310                                                                 false));
311
312                         if (symbol_conf.show_branchflag_count)
313                                 ret += callchain_list_counts__printf_value(
314                                                 NULL, chain, fp, NULL, 0);
315                         ret += fprintf(fp, "\n");
316
317                         if (++entries_printed == callchain_param.print_limit)
318                                 break;
319
320                         if (symbol_conf.inline_name)
321                                 ret += inline__fprintf(chain->ms.map,
322                                                        chain->ip,
323                                                        left_margin,
324                                                        0, 0,
325                                                        fp);
326                 }
327                 root = &cnode->rb_root;
328         }
329
330         if (callchain_param.mode == CHAIN_GRAPH_REL)
331                 total_samples = parent_samples;
332
333         ret += __callchain__fprintf_graph(fp, root, total_samples,
334                                           1, 1, left_margin);
335         if (ret) {
336                 /* do not add a blank line if it printed nothing */
337                 ret += fprintf(fp, "\n");
338         }
339
340         return ret;
341 }
342
343 static size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node,
344                                         u64 total_samples)
345 {
346         struct callchain_list *chain;
347         size_t ret = 0;
348         char bf[1024];
349
350         if (!node)
351                 return 0;
352
353         ret += __callchain__fprintf_flat(fp, node->parent, total_samples);
354
355
356         list_for_each_entry(chain, &node->val, list) {
357                 if (chain->ip >= PERF_CONTEXT_MAX)
358                         continue;
359                 ret += fprintf(fp, "                %s\n", callchain_list__sym_name(chain,
360                                         bf, sizeof(bf), false));
361         }
362
363         return ret;
364 }
365
366 static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *tree,
367                                       u64 total_samples)
368 {
369         size_t ret = 0;
370         u32 entries_printed = 0;
371         struct callchain_node *chain;
372         struct rb_node *rb_node = rb_first(tree);
373
374         while (rb_node) {
375                 chain = rb_entry(rb_node, struct callchain_node, rb_node);
376
377                 ret += fprintf(fp, "           ");
378                 ret += callchain_node__fprintf_value(chain, fp, total_samples);
379                 ret += fprintf(fp, "\n");
380                 ret += __callchain__fprintf_flat(fp, chain, total_samples);
381                 ret += fprintf(fp, "\n");
382                 if (++entries_printed == callchain_param.print_limit)
383                         break;
384
385                 rb_node = rb_next(rb_node);
386         }
387
388         return ret;
389 }
390
391 static size_t __callchain__fprintf_folded(FILE *fp, struct callchain_node *node)
392 {
393         const char *sep = symbol_conf.field_sep ?: ";";
394         struct callchain_list *chain;
395         size_t ret = 0;
396         char bf[1024];
397         bool first;
398
399         if (!node)
400                 return 0;
401
402         ret += __callchain__fprintf_folded(fp, node->parent);
403
404         first = (ret == 0);
405         list_for_each_entry(chain, &node->val, list) {
406                 if (chain->ip >= PERF_CONTEXT_MAX)
407                         continue;
408                 ret += fprintf(fp, "%s%s", first ? "" : sep,
409                                callchain_list__sym_name(chain,
410                                                 bf, sizeof(bf), false));
411                 first = false;
412         }
413
414         return ret;
415 }
416
417 static size_t callchain__fprintf_folded(FILE *fp, struct rb_root *tree,
418                                         u64 total_samples)
419 {
420         size_t ret = 0;
421         u32 entries_printed = 0;
422         struct callchain_node *chain;
423         struct rb_node *rb_node = rb_first(tree);
424
425         while (rb_node) {
426
427                 chain = rb_entry(rb_node, struct callchain_node, rb_node);
428
429                 ret += callchain_node__fprintf_value(chain, fp, total_samples);
430                 ret += fprintf(fp, " ");
431                 ret += __callchain__fprintf_folded(fp, chain);
432                 ret += fprintf(fp, "\n");
433                 if (++entries_printed == callchain_param.print_limit)
434                         break;
435
436                 rb_node = rb_next(rb_node);
437         }
438
439         return ret;
440 }
441
442 static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
443                                             u64 total_samples, int left_margin,
444                                             FILE *fp)
445 {
446         u64 parent_samples = he->stat.period;
447
448         if (symbol_conf.cumulate_callchain)
449                 parent_samples = he->stat_acc->period;
450
451         switch (callchain_param.mode) {
452         case CHAIN_GRAPH_REL:
453                 return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
454                                                 parent_samples, left_margin);
455                 break;
456         case CHAIN_GRAPH_ABS:
457                 return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
458                                                 parent_samples, left_margin);
459                 break;
460         case CHAIN_FLAT:
461                 return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
462                 break;
463         case CHAIN_FOLDED:
464                 return callchain__fprintf_folded(fp, &he->sorted_chain, total_samples);
465                 break;
466         case CHAIN_NONE:
467                 break;
468         default:
469                 pr_err("Bad callchain mode\n");
470         }
471
472         return 0;
473 }
474
475 int __hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp,
476                            struct perf_hpp_list *hpp_list)
477 {
478         const char *sep = symbol_conf.field_sep;
479         struct perf_hpp_fmt *fmt;
480         char *start = hpp->buf;
481         int ret;
482         bool first = true;
483
484         if (symbol_conf.exclude_other && !he->parent)
485                 return 0;
486
487         perf_hpp_list__for_each_format(hpp_list, fmt) {
488                 if (perf_hpp__should_skip(fmt, he->hists))
489                         continue;
490
491                 /*
492                  * If there's no field_sep, we still need
493                  * to display initial '  '.
494                  */
495                 if (!sep || !first) {
496                         ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: "  ");
497                         advance_hpp(hpp, ret);
498                 } else
499                         first = false;
500
501                 if (perf_hpp__use_color() && fmt->color)
502                         ret = fmt->color(fmt, hpp, he);
503                 else
504                         ret = fmt->entry(fmt, hpp, he);
505
506                 ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret);
507                 advance_hpp(hpp, ret);
508         }
509
510         return hpp->buf - start;
511 }
512
513 static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)
514 {
515         return __hist_entry__snprintf(he, hpp, he->hists->hpp_list);
516 }
517
518 static int hist_entry__hierarchy_fprintf(struct hist_entry *he,
519                                          struct perf_hpp *hpp,
520                                          struct hists *hists,
521                                          FILE *fp)
522 {
523         const char *sep = symbol_conf.field_sep;
524         struct perf_hpp_fmt *fmt;
525         struct perf_hpp_list_node *fmt_node;
526         char *buf = hpp->buf;
527         size_t size = hpp->size;
528         int ret, printed = 0;
529         bool first = true;
530
531         if (symbol_conf.exclude_other && !he->parent)
532                 return 0;
533
534         ret = scnprintf(hpp->buf, hpp->size, "%*s", he->depth * HIERARCHY_INDENT, "");
535         advance_hpp(hpp, ret);
536
537         /* the first hpp_list_node is for overhead columns */
538         fmt_node = list_first_entry(&hists->hpp_formats,
539                                     struct perf_hpp_list_node, list);
540         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
541                 /*
542                  * If there's no field_sep, we still need
543                  * to display initial '  '.
544                  */
545                 if (!sep || !first) {
546                         ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: "  ");
547                         advance_hpp(hpp, ret);
548                 } else
549                         first = false;
550
551                 if (perf_hpp__use_color() && fmt->color)
552                         ret = fmt->color(fmt, hpp, he);
553                 else
554                         ret = fmt->entry(fmt, hpp, he);
555
556                 ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret);
557                 advance_hpp(hpp, ret);
558         }
559
560         if (!sep)
561                 ret = scnprintf(hpp->buf, hpp->size, "%*s",
562                                 (hists->nr_hpp_node - 2) * HIERARCHY_INDENT, "");
563         advance_hpp(hpp, ret);
564
565         printed += fprintf(fp, "%s", buf);
566
567         perf_hpp_list__for_each_format(he->hpp_list, fmt) {
568                 hpp->buf  = buf;
569                 hpp->size = size;
570
571                 /*
572                  * No need to call hist_entry__snprintf_alignment() since this
573                  * fmt is always the last column in the hierarchy mode.
574                  */
575                 if (perf_hpp__use_color() && fmt->color)
576                         fmt->color(fmt, hpp, he);
577                 else
578                         fmt->entry(fmt, hpp, he);
579
580                 /*
581                  * dynamic entries are right-aligned but we want left-aligned
582                  * in the hierarchy mode
583                  */
584                 printed += fprintf(fp, "%s%s", sep ?: "  ", ltrim(buf));
585         }
586         printed += putc('\n', fp);
587
588         if (symbol_conf.use_callchain && he->leaf) {
589                 u64 total = hists__total_period(hists);
590
591                 printed += hist_entry_callchain__fprintf(he, total, 0, fp);
592                 goto out;
593         }
594
595 out:
596         return printed;
597 }
598
599 static int hist_entry__fprintf(struct hist_entry *he, size_t size,
600                                char *bf, size_t bfsz, FILE *fp,
601                                bool use_callchain)
602 {
603         int ret;
604         int callchain_ret = 0;
605         int inline_ret = 0;
606         struct perf_hpp hpp = {
607                 .buf            = bf,
608                 .size           = size,
609         };
610         struct hists *hists = he->hists;
611         u64 total_period = hists->stats.total_period;
612
613         if (size == 0 || size > bfsz)
614                 size = hpp.size = bfsz;
615
616         if (symbol_conf.report_hierarchy)
617                 return hist_entry__hierarchy_fprintf(he, &hpp, hists, fp);
618
619         hist_entry__snprintf(he, &hpp);
620
621         ret = fprintf(fp, "%s\n", bf);
622
623         if (use_callchain)
624                 callchain_ret = hist_entry_callchain__fprintf(he, total_period,
625                                                               0, fp);
626
627         if (callchain_ret == 0 && symbol_conf.inline_name) {
628                 inline_ret = inline__fprintf(he->ms.map, he->ip, 0, 0, 0, fp);
629                 ret += inline_ret;
630                 if (inline_ret > 0)
631                         ret += fprintf(fp, "\n");
632         } else
633                 ret += callchain_ret;
634
635         return ret;
636 }
637
638 static int print_hierarchy_indent(const char *sep, int indent,
639                                   const char *line, FILE *fp)
640 {
641         if (sep != NULL || indent < 2)
642                 return 0;
643
644         return fprintf(fp, "%-.*s", (indent - 2) * HIERARCHY_INDENT, line);
645 }
646
647 static int hists__fprintf_hierarchy_headers(struct hists *hists,
648                                             struct perf_hpp *hpp, FILE *fp)
649 {
650         bool first_node, first_col;
651         int indent;
652         int depth;
653         unsigned width = 0;
654         unsigned header_width = 0;
655         struct perf_hpp_fmt *fmt;
656         struct perf_hpp_list_node *fmt_node;
657         const char *sep = symbol_conf.field_sep;
658
659         indent = hists->nr_hpp_node;
660
661         /* preserve max indent depth for column headers */
662         print_hierarchy_indent(sep, indent, spaces, fp);
663
664         /* the first hpp_list_node is for overhead columns */
665         fmt_node = list_first_entry(&hists->hpp_formats,
666                                     struct perf_hpp_list_node, list);
667
668         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
669                 fmt->header(fmt, hpp, hists, 0, NULL);
670                 fprintf(fp, "%s%s", hpp->buf, sep ?: "  ");
671         }
672
673         /* combine sort headers with ' / ' */
674         first_node = true;
675         list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
676                 if (!first_node)
677                         header_width += fprintf(fp, " / ");
678                 first_node = false;
679
680                 first_col = true;
681                 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
682                         if (perf_hpp__should_skip(fmt, hists))
683                                 continue;
684
685                         if (!first_col)
686                                 header_width += fprintf(fp, "+");
687                         first_col = false;
688
689                         fmt->header(fmt, hpp, hists, 0, NULL);
690
691                         header_width += fprintf(fp, "%s", trim(hpp->buf));
692                 }
693         }
694
695         fprintf(fp, "\n# ");
696
697         /* preserve max indent depth for initial dots */
698         print_hierarchy_indent(sep, indent, dots, fp);
699
700         /* the first hpp_list_node is for overhead columns */
701         fmt_node = list_first_entry(&hists->hpp_formats,
702                                     struct perf_hpp_list_node, list);
703
704         first_col = true;
705         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
706                 if (!first_col)
707                         fprintf(fp, "%s", sep ?: "..");
708                 first_col = false;
709
710                 width = fmt->width(fmt, hpp, hists);
711                 fprintf(fp, "%.*s", width, dots);
712         }
713
714         depth = 0;
715         list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
716                 first_col = true;
717                 width = depth * HIERARCHY_INDENT;
718
719                 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
720                         if (perf_hpp__should_skip(fmt, hists))
721                                 continue;
722
723                         if (!first_col)
724                                 width++;  /* for '+' sign between column header */
725                         first_col = false;
726
727                         width += fmt->width(fmt, hpp, hists);
728                 }
729
730                 if (width > header_width)
731                         header_width = width;
732
733                 depth++;
734         }
735
736         fprintf(fp, "%s%-.*s", sep ?: "  ", header_width, dots);
737
738         fprintf(fp, "\n#\n");
739
740         return 2;
741 }
742
743 static void fprintf_line(struct hists *hists, struct perf_hpp *hpp,
744                          int line, FILE *fp)
745 {
746         struct perf_hpp_fmt *fmt;
747         const char *sep = symbol_conf.field_sep;
748         bool first = true;
749         int span = 0;
750
751         hists__for_each_format(hists, fmt) {
752                 if (perf_hpp__should_skip(fmt, hists))
753                         continue;
754
755                 if (!first && !span)
756                         fprintf(fp, "%s", sep ?: "  ");
757                 else
758                         first = false;
759
760                 fmt->header(fmt, hpp, hists, line, &span);
761
762                 if (!span)
763                         fprintf(fp, "%s", hpp->buf);
764         }
765 }
766
767 static int
768 hists__fprintf_standard_headers(struct hists *hists,
769                                 struct perf_hpp *hpp,
770                                 FILE *fp)
771 {
772         struct perf_hpp_list *hpp_list = hists->hpp_list;
773         struct perf_hpp_fmt *fmt;
774         unsigned int width;
775         const char *sep = symbol_conf.field_sep;
776         bool first = true;
777         int line;
778
779         for (line = 0; line < hpp_list->nr_header_lines; line++) {
780                 /* first # is displayed one level up */
781                 if (line)
782                         fprintf(fp, "# ");
783                 fprintf_line(hists, hpp, line, fp);
784                 fprintf(fp, "\n");
785         }
786
787         if (sep)
788                 return hpp_list->nr_header_lines;
789
790         first = true;
791
792         fprintf(fp, "# ");
793
794         hists__for_each_format(hists, fmt) {
795                 unsigned int i;
796
797                 if (perf_hpp__should_skip(fmt, hists))
798                         continue;
799
800                 if (!first)
801                         fprintf(fp, "%s", sep ?: "  ");
802                 else
803                         first = false;
804
805                 width = fmt->width(fmt, hpp, hists);
806                 for (i = 0; i < width; i++)
807                         fprintf(fp, ".");
808         }
809
810         fprintf(fp, "\n");
811         fprintf(fp, "#\n");
812         return hpp_list->nr_header_lines + 2;
813 }
814
815 int hists__fprintf_headers(struct hists *hists, FILE *fp)
816 {
817         char bf[1024];
818         struct perf_hpp dummy_hpp = {
819                 .buf    = bf,
820                 .size   = sizeof(bf),
821         };
822
823         fprintf(fp, "# ");
824
825         if (symbol_conf.report_hierarchy)
826                 return hists__fprintf_hierarchy_headers(hists, &dummy_hpp, fp);
827         else
828                 return hists__fprintf_standard_headers(hists, &dummy_hpp, fp);
829
830 }
831
832 size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
833                       int max_cols, float min_pcnt, FILE *fp,
834                       bool use_callchain)
835 {
836         struct rb_node *nd;
837         size_t ret = 0;
838         const char *sep = symbol_conf.field_sep;
839         int nr_rows = 0;
840         size_t linesz;
841         char *line = NULL;
842         unsigned indent;
843
844         init_rem_hits();
845
846         hists__reset_column_width(hists);
847
848         if (symbol_conf.col_width_list_str)
849                 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
850
851         if (show_header)
852                 nr_rows += hists__fprintf_headers(hists, fp);
853
854         if (max_rows && nr_rows >= max_rows)
855                 goto out;
856
857         linesz = hists__sort_list_width(hists) + 3 + 1;
858         linesz += perf_hpp__color_overhead();
859         line = malloc(linesz);
860         if (line == NULL) {
861                 ret = -1;
862                 goto out;
863         }
864
865         indent = hists__overhead_width(hists) + 4;
866
867         for (nd = rb_first(&hists->entries); nd; nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD)) {
868                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
869                 float percent;
870
871                 if (h->filtered)
872                         continue;
873
874                 percent = hist_entry__get_percent_limit(h);
875                 if (percent < min_pcnt)
876                         continue;
877
878                 ret += hist_entry__fprintf(h, max_cols, line, linesz, fp, use_callchain);
879
880                 if (max_rows && ++nr_rows >= max_rows)
881                         break;
882
883                 /*
884                  * If all children are filtered out or percent-limited,
885                  * display "no entry >= x.xx%" message.
886                  */
887                 if (!h->leaf && !hist_entry__has_hierarchy_children(h, min_pcnt)) {
888                         int depth = hists->nr_hpp_node + h->depth + 1;
889
890                         print_hierarchy_indent(sep, depth, spaces, fp);
891                         fprintf(fp, "%*sno entry >= %.2f%%\n", indent, "", min_pcnt);
892
893                         if (max_rows && ++nr_rows >= max_rows)
894                                 break;
895                 }
896
897                 if (h->ms.map == NULL && verbose > 1) {
898                         __map_groups__fprintf_maps(h->thread->mg,
899                                                    MAP__FUNCTION, fp);
900                         fprintf(fp, "%.10s end\n", graph_dotted_line);
901                 }
902         }
903
904         free(line);
905 out:
906         zfree(&rem_sq_bracket);
907
908         return ret;
909 }
910
911 size_t events_stats__fprintf(struct events_stats *stats, FILE *fp)
912 {
913         int i;
914         size_t ret = 0;
915
916         for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
917                 const char *name;
918
919                 if (stats->nr_events[i] == 0)
920                         continue;
921
922                 name = perf_event__name(i);
923                 if (!strcmp(name, "UNKNOWN"))
924                         continue;
925
926                 ret += fprintf(fp, "%16s events: %10d\n", name,
927                                stats->nr_events[i]);
928         }
929
930         return ret;
931 }