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