linux/tools/perf/ui/stdio/hist.c
<<
>>
Prefs
   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
  18static 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
  29static 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
  46static 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
  90static struct symbol *rem_sq_bracket;
  91static struct callchain_list rem_hits;
  92
  93static 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
 105static 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 */
 202static 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
 213static 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
 278static 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
 301static 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
 326static 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
 352static 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
 377static 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
 410int __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
 448static 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
 453static 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
 530out:
 531        return printed;
 532}
 533
 534static 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
 558static 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
 593static 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
 606static 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
 702static 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
 726static int
 727hists__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
 774int 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
 791size_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);
 864out:
 865        zfree(&rem_sq_bracket);
 866
 867        return ret;
 868}
 869
 870size_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}
 887