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 } 890