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