Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <stdio.h>
0003 #include <stdlib.h>
0004 #include <linux/string.h>
0005 
0006 #include "../../util/callchain.h"
0007 #include "../../util/debug.h"
0008 #include "../../util/event.h"
0009 #include "../../util/hist.h"
0010 #include "../../util/map.h"
0011 #include "../../util/maps.h"
0012 #include "../../util/symbol.h"
0013 #include "../../util/sort.h"
0014 #include "../../util/evsel.h"
0015 #include "../../util/srcline.h"
0016 #include "../../util/string2.h"
0017 #include "../../util/thread.h"
0018 #include "../../util/block-info.h"
0019 #include <linux/ctype.h>
0020 #include <linux/zalloc.h>
0021 
0022 static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
0023 {
0024     int i;
0025     int ret = fprintf(fp, "            ");
0026 
0027     for (i = 0; i < left_margin; i++)
0028         ret += fprintf(fp, " ");
0029 
0030     return ret;
0031 }
0032 
0033 static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
0034                       int left_margin)
0035 {
0036     int i;
0037     size_t ret = callchain__fprintf_left_margin(fp, left_margin);
0038 
0039     for (i = 0; i < depth; i++)
0040         if (depth_mask & (1 << i))
0041             ret += fprintf(fp, "|          ");
0042         else
0043             ret += fprintf(fp, "           ");
0044 
0045     ret += fprintf(fp, "\n");
0046 
0047     return ret;
0048 }
0049 
0050 static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node,
0051                      struct callchain_list *chain,
0052                      int depth, int depth_mask, int period,
0053                      u64 total_samples, int left_margin)
0054 {
0055     int i;
0056     size_t ret = 0;
0057     char bf[1024], *alloc_str = NULL;
0058     char buf[64];
0059     const char *str;
0060 
0061     ret += callchain__fprintf_left_margin(fp, left_margin);
0062     for (i = 0; i < depth; i++) {
0063         if (depth_mask & (1 << i))
0064             ret += fprintf(fp, "|");
0065         else
0066             ret += fprintf(fp, " ");
0067         if (!period && i == depth - 1) {
0068             ret += fprintf(fp, "--");
0069             ret += callchain_node__fprintf_value(node, fp, total_samples);
0070             ret += fprintf(fp, "--");
0071         } else
0072             ret += fprintf(fp, "%s", "          ");
0073     }
0074 
0075     str = callchain_list__sym_name(chain, bf, sizeof(bf), false);
0076 
0077     if (symbol_conf.show_branchflag_count) {
0078         callchain_list_counts__printf_value(chain, NULL,
0079                             buf, sizeof(buf));
0080 
0081         if (asprintf(&alloc_str, "%s%s", str, buf) < 0)
0082             str = "Not enough memory!";
0083         else
0084             str = alloc_str;
0085     }
0086 
0087     fputs(str, fp);
0088     fputc('\n', fp);
0089     free(alloc_str);
0090 
0091     return ret;
0092 }
0093 
0094 static struct symbol *rem_sq_bracket;
0095 static struct callchain_list rem_hits;
0096 
0097 static void init_rem_hits(void)
0098 {
0099     rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
0100     if (!rem_sq_bracket) {
0101         fprintf(stderr, "Not enough memory to display remaining hits\n");
0102         return;
0103     }
0104 
0105     strcpy(rem_sq_bracket->name, "[...]");
0106     rem_hits.ms.sym = rem_sq_bracket;
0107 }
0108 
0109 static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
0110                      u64 total_samples, int depth,
0111                      int depth_mask, int left_margin)
0112 {
0113     struct rb_node *node, *next;
0114     struct callchain_node *child = NULL;
0115     struct callchain_list *chain;
0116     int new_depth_mask = depth_mask;
0117     u64 remaining;
0118     size_t ret = 0;
0119     int i;
0120     uint entries_printed = 0;
0121     int cumul_count = 0;
0122 
0123     remaining = total_samples;
0124 
0125     node = rb_first(root);
0126     while (node) {
0127         u64 new_total;
0128         u64 cumul;
0129 
0130         child = rb_entry(node, struct callchain_node, rb_node);
0131         cumul = callchain_cumul_hits(child);
0132         remaining -= cumul;
0133         cumul_count += callchain_cumul_counts(child);
0134 
0135         /*
0136          * The depth mask manages the output of pipes that show
0137          * the depth. We don't want to keep the pipes of the current
0138          * level for the last child of this depth.
0139          * Except if we have remaining filtered hits. They will
0140          * supersede the last child
0141          */
0142         next = rb_next(node);
0143         if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
0144             new_depth_mask &= ~(1 << (depth - 1));
0145 
0146         /*
0147          * But we keep the older depth mask for the line separator
0148          * to keep the level link until we reach the last child
0149          */
0150         ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
0151                            left_margin);
0152         i = 0;
0153         list_for_each_entry(chain, &child->val, list) {
0154             ret += ipchain__fprintf_graph(fp, child, chain, depth,
0155                               new_depth_mask, i++,
0156                               total_samples,
0157                               left_margin);
0158         }
0159 
0160         if (callchain_param.mode == CHAIN_GRAPH_REL)
0161             new_total = child->children_hit;
0162         else
0163             new_total = total_samples;
0164 
0165         ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
0166                           depth + 1,
0167                           new_depth_mask | (1 << depth),
0168                           left_margin);
0169         node = next;
0170         if (++entries_printed == callchain_param.print_limit)
0171             break;
0172     }
0173 
0174     if (callchain_param.mode == CHAIN_GRAPH_REL &&
0175         remaining && remaining != total_samples) {
0176         struct callchain_node rem_node = {
0177             .hit = remaining,
0178         };
0179 
0180         if (!rem_sq_bracket)
0181             return ret;
0182 
0183         if (callchain_param.value == CCVAL_COUNT && child && child->parent) {
0184             rem_node.count = child->parent->children_count - cumul_count;
0185             if (rem_node.count <= 0)
0186                 return ret;
0187         }
0188 
0189         new_depth_mask &= ~(1 << (depth - 1));
0190         ret += ipchain__fprintf_graph(fp, &rem_node, &rem_hits, depth,
0191                           new_depth_mask, 0, total_samples,
0192                           left_margin);
0193     }
0194 
0195     return ret;
0196 }
0197 
0198 /*
0199  * If have one single callchain root, don't bother printing
0200  * its percentage (100 % in fractal mode and the same percentage
0201  * than the hist in graph mode). This also avoid one level of column.
0202  *
0203  * However when percent-limit applied, it's possible that single callchain
0204  * node have different (non-100% in fractal mode) percentage.
0205  */
0206 static bool need_percent_display(struct rb_node *node, u64 parent_samples)
0207 {
0208     struct callchain_node *cnode;
0209 
0210     if (rb_next(node))
0211         return true;
0212 
0213     cnode = rb_entry(node, struct callchain_node, rb_node);
0214     return callchain_cumul_hits(cnode) != parent_samples;
0215 }
0216 
0217 static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
0218                        u64 total_samples, u64 parent_samples,
0219                        int left_margin)
0220 {
0221     struct callchain_node *cnode;
0222     struct callchain_list *chain;
0223     u32 entries_printed = 0;
0224     bool printed = false;
0225     struct rb_node *node;
0226     int i = 0;
0227     int ret = 0;
0228     char bf[1024];
0229 
0230     node = rb_first(root);
0231     if (node && !need_percent_display(node, parent_samples)) {
0232         cnode = rb_entry(node, struct callchain_node, rb_node);
0233         list_for_each_entry(chain, &cnode->val, list) {
0234             /*
0235              * If we sort by symbol, the first entry is the same than
0236              * the symbol. No need to print it otherwise it appears as
0237              * displayed twice.
0238              */
0239             if (!i++ && field_order == NULL &&
0240                 sort_order && strstarts(sort_order, "sym"))
0241                 continue;
0242 
0243             if (!printed) {
0244                 ret += callchain__fprintf_left_margin(fp, left_margin);
0245                 ret += fprintf(fp, "|\n");
0246                 ret += callchain__fprintf_left_margin(fp, left_margin);
0247                 ret += fprintf(fp, "---");
0248                 left_margin += 3;
0249                 printed = true;
0250             } else
0251                 ret += callchain__fprintf_left_margin(fp, left_margin);
0252 
0253             ret += fprintf(fp, "%s",
0254                        callchain_list__sym_name(chain, bf,
0255                                 sizeof(bf),
0256                                 false));
0257 
0258             if (symbol_conf.show_branchflag_count)
0259                 ret += callchain_list_counts__printf_value(
0260                         chain, fp, NULL, 0);
0261             ret += fprintf(fp, "\n");
0262 
0263             if (++entries_printed == callchain_param.print_limit)
0264                 break;
0265         }
0266         root = &cnode->rb_root;
0267     }
0268 
0269     if (callchain_param.mode == CHAIN_GRAPH_REL)
0270         total_samples = parent_samples;
0271 
0272     ret += __callchain__fprintf_graph(fp, root, total_samples,
0273                       1, 1, left_margin);
0274     if (ret) {
0275         /* do not add a blank line if it printed nothing */
0276         ret += fprintf(fp, "\n");
0277     }
0278 
0279     return ret;
0280 }
0281 
0282 static size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node,
0283                     u64 total_samples)
0284 {
0285     struct callchain_list *chain;
0286     size_t ret = 0;
0287     char bf[1024];
0288 
0289     if (!node)
0290         return 0;
0291 
0292     ret += __callchain__fprintf_flat(fp, node->parent, total_samples);
0293 
0294 
0295     list_for_each_entry(chain, &node->val, list) {
0296         if (chain->ip >= PERF_CONTEXT_MAX)
0297             continue;
0298         ret += fprintf(fp, "                %s\n", callchain_list__sym_name(chain,
0299                     bf, sizeof(bf), false));
0300     }
0301 
0302     return ret;
0303 }
0304 
0305 static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *tree,
0306                       u64 total_samples)
0307 {
0308     size_t ret = 0;
0309     u32 entries_printed = 0;
0310     struct callchain_node *chain;
0311     struct rb_node *rb_node = rb_first(tree);
0312 
0313     while (rb_node) {
0314         chain = rb_entry(rb_node, struct callchain_node, rb_node);
0315 
0316         ret += fprintf(fp, "           ");
0317         ret += callchain_node__fprintf_value(chain, fp, total_samples);
0318         ret += fprintf(fp, "\n");
0319         ret += __callchain__fprintf_flat(fp, chain, total_samples);
0320         ret += fprintf(fp, "\n");
0321         if (++entries_printed == callchain_param.print_limit)
0322             break;
0323 
0324         rb_node = rb_next(rb_node);
0325     }
0326 
0327     return ret;
0328 }
0329 
0330 static size_t __callchain__fprintf_folded(FILE *fp, struct callchain_node *node)
0331 {
0332     const char *sep = symbol_conf.field_sep ?: ";";
0333     struct callchain_list *chain;
0334     size_t ret = 0;
0335     char bf[1024];
0336     bool first;
0337 
0338     if (!node)
0339         return 0;
0340 
0341     ret += __callchain__fprintf_folded(fp, node->parent);
0342 
0343     first = (ret == 0);
0344     list_for_each_entry(chain, &node->val, list) {
0345         if (chain->ip >= PERF_CONTEXT_MAX)
0346             continue;
0347         ret += fprintf(fp, "%s%s", first ? "" : sep,
0348                    callchain_list__sym_name(chain,
0349                         bf, sizeof(bf), false));
0350         first = false;
0351     }
0352 
0353     return ret;
0354 }
0355 
0356 static size_t callchain__fprintf_folded(FILE *fp, struct rb_root *tree,
0357                     u64 total_samples)
0358 {
0359     size_t ret = 0;
0360     u32 entries_printed = 0;
0361     struct callchain_node *chain;
0362     struct rb_node *rb_node = rb_first(tree);
0363 
0364     while (rb_node) {
0365 
0366         chain = rb_entry(rb_node, struct callchain_node, rb_node);
0367 
0368         ret += callchain_node__fprintf_value(chain, fp, total_samples);
0369         ret += fprintf(fp, " ");
0370         ret += __callchain__fprintf_folded(fp, chain);
0371         ret += fprintf(fp, "\n");
0372         if (++entries_printed == callchain_param.print_limit)
0373             break;
0374 
0375         rb_node = rb_next(rb_node);
0376     }
0377 
0378     return ret;
0379 }
0380 
0381 static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
0382                         u64 total_samples, int left_margin,
0383                         FILE *fp)
0384 {
0385     u64 parent_samples = he->stat.period;
0386 
0387     if (symbol_conf.cumulate_callchain)
0388         parent_samples = he->stat_acc->period;
0389 
0390     switch (callchain_param.mode) {
0391     case CHAIN_GRAPH_REL:
0392         return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
0393                         parent_samples, left_margin);
0394         break;
0395     case CHAIN_GRAPH_ABS:
0396         return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
0397                         parent_samples, left_margin);
0398         break;
0399     case CHAIN_FLAT:
0400         return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
0401         break;
0402     case CHAIN_FOLDED:
0403         return callchain__fprintf_folded(fp, &he->sorted_chain, total_samples);
0404         break;
0405     case CHAIN_NONE:
0406         break;
0407     default:
0408         pr_err("Bad callchain mode\n");
0409     }
0410 
0411     return 0;
0412 }
0413 
0414 int __hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp,
0415                struct perf_hpp_list *hpp_list)
0416 {
0417     const char *sep = symbol_conf.field_sep;
0418     struct perf_hpp_fmt *fmt;
0419     char *start = hpp->buf;
0420     int ret;
0421     bool first = true;
0422 
0423     if (symbol_conf.exclude_other && !he->parent)
0424         return 0;
0425 
0426     perf_hpp_list__for_each_format(hpp_list, fmt) {
0427         if (perf_hpp__should_skip(fmt, he->hists))
0428             continue;
0429 
0430         /*
0431          * If there's no field_sep, we still need
0432          * to display initial '  '.
0433          */
0434         if (!sep || !first) {
0435             ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: "  ");
0436             advance_hpp(hpp, ret);
0437         } else
0438             first = false;
0439 
0440         if (perf_hpp__use_color() && fmt->color)
0441             ret = fmt->color(fmt, hpp, he);
0442         else
0443             ret = fmt->entry(fmt, hpp, he);
0444 
0445         ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret);
0446         advance_hpp(hpp, ret);
0447     }
0448 
0449     return hpp->buf - start;
0450 }
0451 
0452 static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)
0453 {
0454     return __hist_entry__snprintf(he, hpp, he->hists->hpp_list);
0455 }
0456 
0457 static int hist_entry__hierarchy_fprintf(struct hist_entry *he,
0458                      struct perf_hpp *hpp,
0459                      struct hists *hists,
0460                      FILE *fp)
0461 {
0462     const char *sep = symbol_conf.field_sep;
0463     struct perf_hpp_fmt *fmt;
0464     struct perf_hpp_list_node *fmt_node;
0465     char *buf = hpp->buf;
0466     size_t size = hpp->size;
0467     int ret, printed = 0;
0468     bool first = true;
0469 
0470     if (symbol_conf.exclude_other && !he->parent)
0471         return 0;
0472 
0473     ret = scnprintf(hpp->buf, hpp->size, "%*s", he->depth * HIERARCHY_INDENT, "");
0474     advance_hpp(hpp, ret);
0475 
0476     /* the first hpp_list_node is for overhead columns */
0477     fmt_node = list_first_entry(&hists->hpp_formats,
0478                     struct perf_hpp_list_node, list);
0479     perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
0480         /*
0481          * If there's no field_sep, we still need
0482          * to display initial '  '.
0483          */
0484         if (!sep || !first) {
0485             ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: "  ");
0486             advance_hpp(hpp, ret);
0487         } else
0488             first = false;
0489 
0490         if (perf_hpp__use_color() && fmt->color)
0491             ret = fmt->color(fmt, hpp, he);
0492         else
0493             ret = fmt->entry(fmt, hpp, he);
0494 
0495         ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret);
0496         advance_hpp(hpp, ret);
0497     }
0498 
0499     if (!sep)
0500         ret = scnprintf(hpp->buf, hpp->size, "%*s",
0501                 (hists->nr_hpp_node - 2) * HIERARCHY_INDENT, "");
0502     advance_hpp(hpp, ret);
0503 
0504     printed += fprintf(fp, "%s", buf);
0505 
0506     perf_hpp_list__for_each_format(he->hpp_list, fmt) {
0507         hpp->buf  = buf;
0508         hpp->size = size;
0509 
0510         /*
0511          * No need to call hist_entry__snprintf_alignment() since this
0512          * fmt is always the last column in the hierarchy mode.
0513          */
0514         if (perf_hpp__use_color() && fmt->color)
0515             fmt->color(fmt, hpp, he);
0516         else
0517             fmt->entry(fmt, hpp, he);
0518 
0519         /*
0520          * dynamic entries are right-aligned but we want left-aligned
0521          * in the hierarchy mode
0522          */
0523         printed += fprintf(fp, "%s%s", sep ?: "  ", skip_spaces(buf));
0524     }
0525     printed += putc('\n', fp);
0526 
0527     if (he->leaf && hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
0528         u64 total = hists__total_period(hists);
0529 
0530         printed += hist_entry_callchain__fprintf(he, total, 0, fp);
0531         goto out;
0532     }
0533 
0534 out:
0535     return printed;
0536 }
0537 
0538 static int hist_entry__block_fprintf(struct hist_entry *he,
0539                      char *bf, size_t size,
0540                      FILE *fp)
0541 {
0542     struct block_hist *bh = container_of(he, struct block_hist, he);
0543     int ret = 0;
0544 
0545     for (unsigned int i = 0; i < bh->block_hists.nr_entries; i++) {
0546         struct perf_hpp hpp = {
0547             .buf        = bf,
0548             .size       = size,
0549             .skip       = false,
0550         };
0551 
0552         bh->block_idx = i;
0553         hist_entry__snprintf(he, &hpp);
0554 
0555         if (!hpp.skip)
0556             ret += fprintf(fp, "%s\n", bf);
0557     }
0558 
0559     return ret;
0560 }
0561 
0562 static int hist_entry__individual_block_fprintf(struct hist_entry *he,
0563                         char *bf, size_t size,
0564                         FILE *fp)
0565 {
0566     int ret = 0;
0567 
0568     struct perf_hpp hpp = {
0569         .buf        = bf,
0570         .size       = size,
0571         .skip       = false,
0572     };
0573 
0574     hist_entry__snprintf(he, &hpp);
0575     if (!hpp.skip)
0576         ret += fprintf(fp, "%s\n", bf);
0577 
0578     return ret;
0579 }
0580 
0581 static int hist_entry__fprintf(struct hist_entry *he, size_t size,
0582                    char *bf, size_t bfsz, FILE *fp,
0583                    bool ignore_callchains)
0584 {
0585     int ret;
0586     int callchain_ret = 0;
0587     struct perf_hpp hpp = {
0588         .buf        = bf,
0589         .size       = size,
0590     };
0591     struct hists *hists = he->hists;
0592     u64 total_period = hists->stats.total_period;
0593 
0594     if (size == 0 || size > bfsz)
0595         size = hpp.size = bfsz;
0596 
0597     if (symbol_conf.report_hierarchy)
0598         return hist_entry__hierarchy_fprintf(he, &hpp, hists, fp);
0599 
0600     if (symbol_conf.report_block)
0601         return hist_entry__block_fprintf(he, bf, size, fp);
0602 
0603     if (symbol_conf.report_individual_block)
0604         return hist_entry__individual_block_fprintf(he, bf, size, fp);
0605 
0606     hist_entry__snprintf(he, &hpp);
0607 
0608     ret = fprintf(fp, "%s\n", bf);
0609 
0610     if (hist_entry__has_callchains(he) && !ignore_callchains)
0611         callchain_ret = hist_entry_callchain__fprintf(he, total_period,
0612                                   0, fp);
0613 
0614     ret += callchain_ret;
0615 
0616     return ret;
0617 }
0618 
0619 static int print_hierarchy_indent(const char *sep, int indent,
0620                   const char *line, FILE *fp)
0621 {
0622     int width;
0623 
0624     if (sep != NULL || indent < 2)
0625         return 0;
0626 
0627     width = (indent - 2) * HIERARCHY_INDENT;
0628 
0629     return fprintf(fp, "%-*.*s", width, width, line);
0630 }
0631 
0632 static int hists__fprintf_hierarchy_headers(struct hists *hists,
0633                         struct perf_hpp *hpp, FILE *fp)
0634 {
0635     bool first_node, first_col;
0636     int indent;
0637     int depth;
0638     unsigned width = 0;
0639     unsigned header_width = 0;
0640     struct perf_hpp_fmt *fmt;
0641     struct perf_hpp_list_node *fmt_node;
0642     const char *sep = symbol_conf.field_sep;
0643 
0644     indent = hists->nr_hpp_node;
0645 
0646     /* preserve max indent depth for column headers */
0647     print_hierarchy_indent(sep, indent, " ", fp);
0648 
0649     /* the first hpp_list_node is for overhead columns */
0650     fmt_node = list_first_entry(&hists->hpp_formats,
0651                     struct perf_hpp_list_node, list);
0652 
0653     perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
0654         fmt->header(fmt, hpp, hists, 0, NULL);
0655         fprintf(fp, "%s%s", hpp->buf, sep ?: "  ");
0656     }
0657 
0658     /* combine sort headers with ' / ' */
0659     first_node = true;
0660     list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
0661         if (!first_node)
0662             header_width += fprintf(fp, " / ");
0663         first_node = false;
0664 
0665         first_col = true;
0666         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
0667             if (perf_hpp__should_skip(fmt, hists))
0668                 continue;
0669 
0670             if (!first_col)
0671                 header_width += fprintf(fp, "+");
0672             first_col = false;
0673 
0674             fmt->header(fmt, hpp, hists, 0, NULL);
0675 
0676             header_width += fprintf(fp, "%s", strim(hpp->buf));
0677         }
0678     }
0679 
0680     fprintf(fp, "\n# ");
0681 
0682     /* preserve max indent depth for initial dots */
0683     print_hierarchy_indent(sep, indent, dots, fp);
0684 
0685     /* the first hpp_list_node is for overhead columns */
0686     fmt_node = list_first_entry(&hists->hpp_formats,
0687                     struct perf_hpp_list_node, list);
0688 
0689     first_col = true;
0690     perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
0691         if (!first_col)
0692             fprintf(fp, "%s", sep ?: "..");
0693         first_col = false;
0694 
0695         width = fmt->width(fmt, hpp, hists);
0696         fprintf(fp, "%.*s", width, dots);
0697     }
0698 
0699     depth = 0;
0700     list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
0701         first_col = true;
0702         width = depth * HIERARCHY_INDENT;
0703 
0704         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
0705             if (perf_hpp__should_skip(fmt, hists))
0706                 continue;
0707 
0708             if (!first_col)
0709                 width++;  /* for '+' sign between column header */
0710             first_col = false;
0711 
0712             width += fmt->width(fmt, hpp, hists);
0713         }
0714 
0715         if (width > header_width)
0716             header_width = width;
0717 
0718         depth++;
0719     }
0720 
0721     fprintf(fp, "%s%-.*s", sep ?: "  ", header_width, dots);
0722 
0723     fprintf(fp, "\n#\n");
0724 
0725     return 2;
0726 }
0727 
0728 static void fprintf_line(struct hists *hists, struct perf_hpp *hpp,
0729              int line, FILE *fp)
0730 {
0731     struct perf_hpp_fmt *fmt;
0732     const char *sep = symbol_conf.field_sep;
0733     bool first = true;
0734     int span = 0;
0735 
0736     hists__for_each_format(hists, fmt) {
0737         if (perf_hpp__should_skip(fmt, hists))
0738             continue;
0739 
0740         if (!first && !span)
0741             fprintf(fp, "%s", sep ?: "  ");
0742         else
0743             first = false;
0744 
0745         fmt->header(fmt, hpp, hists, line, &span);
0746 
0747         if (!span)
0748             fprintf(fp, "%s", hpp->buf);
0749     }
0750 }
0751 
0752 static int
0753 hists__fprintf_standard_headers(struct hists *hists,
0754                 struct perf_hpp *hpp,
0755                 FILE *fp)
0756 {
0757     struct perf_hpp_list *hpp_list = hists->hpp_list;
0758     struct perf_hpp_fmt *fmt;
0759     unsigned int width;
0760     const char *sep = symbol_conf.field_sep;
0761     bool first = true;
0762     int line;
0763 
0764     for (line = 0; line < hpp_list->nr_header_lines; line++) {
0765         /* first # is displayed one level up */
0766         if (line)
0767             fprintf(fp, "# ");
0768         fprintf_line(hists, hpp, line, fp);
0769         fprintf(fp, "\n");
0770     }
0771 
0772     if (sep)
0773         return hpp_list->nr_header_lines;
0774 
0775     first = true;
0776 
0777     fprintf(fp, "# ");
0778 
0779     hists__for_each_format(hists, fmt) {
0780         unsigned int i;
0781 
0782         if (perf_hpp__should_skip(fmt, hists))
0783             continue;
0784 
0785         if (!first)
0786             fprintf(fp, "%s", sep ?: "  ");
0787         else
0788             first = false;
0789 
0790         width = fmt->width(fmt, hpp, hists);
0791         for (i = 0; i < width; i++)
0792             fprintf(fp, ".");
0793     }
0794 
0795     fprintf(fp, "\n");
0796     fprintf(fp, "#\n");
0797     return hpp_list->nr_header_lines + 2;
0798 }
0799 
0800 int hists__fprintf_headers(struct hists *hists, FILE *fp)
0801 {
0802     char bf[1024];
0803     struct perf_hpp dummy_hpp = {
0804         .buf    = bf,
0805         .size   = sizeof(bf),
0806     };
0807 
0808     fprintf(fp, "# ");
0809 
0810     if (symbol_conf.report_hierarchy)
0811         return hists__fprintf_hierarchy_headers(hists, &dummy_hpp, fp);
0812     else
0813         return hists__fprintf_standard_headers(hists, &dummy_hpp, fp);
0814 
0815 }
0816 
0817 size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
0818               int max_cols, float min_pcnt, FILE *fp,
0819               bool ignore_callchains)
0820 {
0821     struct rb_node *nd;
0822     size_t ret = 0;
0823     const char *sep = symbol_conf.field_sep;
0824     int nr_rows = 0;
0825     size_t linesz;
0826     char *line = NULL;
0827     unsigned indent;
0828 
0829     init_rem_hits();
0830 
0831     hists__reset_column_width(hists);
0832 
0833     if (symbol_conf.col_width_list_str)
0834         perf_hpp__set_user_width(symbol_conf.col_width_list_str);
0835 
0836     if (show_header)
0837         nr_rows += hists__fprintf_headers(hists, fp);
0838 
0839     if (max_rows && nr_rows >= max_rows)
0840         goto out;
0841 
0842     linesz = hists__sort_list_width(hists) + 3 + 1;
0843     linesz += perf_hpp__color_overhead();
0844     line = malloc(linesz);
0845     if (line == NULL) {
0846         ret = -1;
0847         goto out;
0848     }
0849 
0850     indent = hists__overhead_width(hists) + 4;
0851 
0852     for (nd = rb_first_cached(&hists->entries); nd;
0853          nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD)) {
0854         struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
0855         float percent;
0856 
0857         if (h->filtered)
0858             continue;
0859 
0860         if (symbol_conf.report_individual_block)
0861             percent = block_info__total_cycles_percent(h);
0862         else
0863             percent = hist_entry__get_percent_limit(h);
0864 
0865         if (percent < min_pcnt)
0866             continue;
0867 
0868         ret += hist_entry__fprintf(h, max_cols, line, linesz, fp, ignore_callchains);
0869 
0870         if (max_rows && ++nr_rows >= max_rows)
0871             break;
0872 
0873         /*
0874          * If all children are filtered out or percent-limited,
0875          * display "no entry >= x.xx%" message.
0876          */
0877         if (!h->leaf && !hist_entry__has_hierarchy_children(h, min_pcnt)) {
0878             int depth = hists->nr_hpp_node + h->depth + 1;
0879 
0880             print_hierarchy_indent(sep, depth, " ", fp);
0881             fprintf(fp, "%*sno entry >= %.2f%%\n", indent, "", min_pcnt);
0882 
0883             if (max_rows && ++nr_rows >= max_rows)
0884                 break;
0885         }
0886 
0887         if (h->ms.map == NULL && verbose > 1) {
0888             maps__fprintf(h->thread->maps, fp);
0889             fprintf(fp, "%.10s end\n", graph_dotted_line);
0890         }
0891     }
0892 
0893     free(line);
0894 out:
0895     zfree(&rem_sq_bracket);
0896 
0897     return ret;
0898 }
0899 
0900 size_t events_stats__fprintf(struct events_stats *stats, FILE *fp,
0901                  bool skip_empty)
0902 {
0903     int i;
0904     size_t ret = 0;
0905     u32 total = stats->nr_events[0];
0906 
0907     for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
0908         const char *name;
0909 
0910         name = perf_event__name(i);
0911         if (!strcmp(name, "UNKNOWN"))
0912             continue;
0913         if (skip_empty && !stats->nr_events[i])
0914             continue;
0915 
0916         if (i && total) {
0917             ret += fprintf(fp, "%16s events: %10d  (%4.1f%%)\n",
0918                        name, stats->nr_events[i],
0919                        100.0 * stats->nr_events[i] / total);
0920         } else {
0921             ret += fprintf(fp, "%16s events: %10d\n",
0922                        name, stats->nr_events[i]);
0923         }
0924     }
0925 
0926     return ret;
0927 }