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