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