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