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