root/tools/perf/ui/stdio/hist.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. callchain__fprintf_left_margin
  2. ipchain__fprintf_graph_line
  3. ipchain__fprintf_graph
  4. init_rem_hits
  5. __callchain__fprintf_graph
  6. need_percent_display
  7. callchain__fprintf_graph
  8. __callchain__fprintf_flat
  9. callchain__fprintf_flat
  10. __callchain__fprintf_folded
  11. callchain__fprintf_folded
  12. hist_entry_callchain__fprintf
  13. __hist_entry__snprintf
  14. hist_entry__snprintf
  15. hist_entry__hierarchy_fprintf
  16. hist_entry__block_fprintf
  17. hist_entry__fprintf
  18. print_hierarchy_indent
  19. hists__fprintf_hierarchy_headers
  20. fprintf_line
  21. hists__fprintf_standard_headers
  22. hists__fprintf_headers
  23. hists__fprintf
  24. events_stats__fprintf

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

/* [<][>][^][v][top][bottom][index][help] */