1 #include "annotate.h" 2 #include "util.h" 3 #include "build-id.h" 4 #include "hist.h" 5 #include "session.h" 6 #include "sort.h" 7 #include <math.h> 8 9 enum hist_filter { 10 HIST_FILTER__DSO, 11 HIST_FILTER__THREAD, 12 HIST_FILTER__PARENT, 13 }; 14 15 struct callchain_param callchain_param = { 16 .mode = CHAIN_GRAPH_REL, 17 .min_percent = 0.5 18 }; 19 20 u16 hists__col_len(struct hists *self, enum hist_column col) 21 { 22 return self->col_len[col]; 23 } 24 25 void hists__set_col_len(struct hists *self, enum hist_column col, u16 len) 26 { 27 self->col_len[col] = len; 28 } 29 30 bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len) 31 { 32 if (len > hists__col_len(self, col)) { 33 hists__set_col_len(self, col, len); 34 return true; 35 } 36 return false; 37 } 38 39 static void hists__reset_col_len(struct hists *self) 40 { 41 enum hist_column col; 42 43 for (col = 0; col < HISTC_NR_COLS; ++col) 44 hists__set_col_len(self, col, 0); 45 } 46 47 static void hists__calc_col_len(struct hists *self, struct hist_entry *h) 48 { 49 u16 len; 50 51 if (h->ms.sym) 52 hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); 53 else { 54 const unsigned int unresolved_col_width = BITS_PER_LONG / 4; 55 56 if (hists__col_len(self, HISTC_DSO) < unresolved_col_width && 57 !symbol_conf.col_width_list_str && !symbol_conf.field_sep && 58 !symbol_conf.dso_list) 59 hists__set_col_len(self, HISTC_DSO, 60 unresolved_col_width); 61 } 62 63 len = thread__comm_len(h->thread); 64 if (hists__new_col_len(self, HISTC_COMM, len)) 65 hists__set_col_len(self, HISTC_THREAD, len + 6); 66 67 if (h->ms.map) { 68 len = dso__name_len(h->ms.map->dso); 69 hists__new_col_len(self, HISTC_DSO, len); 70 } 71 } 72 73 static void hist_entry__add_cpumode_period(struct hist_entry *self, 74 unsigned int cpumode, u64 period) 75 { 76 switch (cpumode) { 77 case PERF_RECORD_MISC_KERNEL: 78 self->period_sys += period; 79 break; 80 case PERF_RECORD_MISC_USER: 81 self->period_us += period; 82 break; 83 case PERF_RECORD_MISC_GUEST_KERNEL: 84 self->period_guest_sys += period; 85 break; 86 case PERF_RECORD_MISC_GUEST_USER: 87 self->period_guest_us += period; 88 break; 89 default: 90 break; 91 } 92 } 93 94 /* 95 * histogram, sorted on item, collects periods 96 */ 97 98 static struct hist_entry *hist_entry__new(struct hist_entry *template) 99 { 100 size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0; 101 struct hist_entry *self = malloc(sizeof(*self) + callchain_size); 102 103 if (self != NULL) { 104 *self = *template; 105 self->nr_events = 1; 106 if (self->ms.map) 107 self->ms.map->referenced = true; 108 if (symbol_conf.use_callchain) 109 callchain_init(self->callchain); 110 } 111 112 return self; 113 } 114 115 static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h) 116 { 117 if (!h->filtered) { 118 hists__calc_col_len(self, h); 119 ++self->nr_entries; 120 } 121 } 122 123 static u8 symbol__parent_filter(const struct symbol *parent) 124 { 125 if (symbol_conf.exclude_other && parent == NULL) 126 return 1 << HIST_FILTER__PARENT; 127 return 0; 128 } 129 130 struct hist_entry *__hists__add_entry(struct hists *self, 131 struct addr_location *al, 132 struct symbol *sym_parent, u64 period) 133 { 134 struct rb_node **p = &self->entries.rb_node; 135 struct rb_node *parent = NULL; 136 struct hist_entry *he; 137 struct hist_entry entry = { 138 .thread = al->thread, 139 .ms = { 140 .map = al->map, 141 .sym = al->sym, 142 }, 143 .cpu = al->cpu, 144 .ip = al->addr, 145 .level = al->level, 146 .period = period, 147 .parent = sym_parent, 148 .filtered = symbol__parent_filter(sym_parent), 149 }; 150 int cmp; 151 152 while (*p != NULL) { 153 parent = *p; 154 he = rb_entry(parent, struct hist_entry, rb_node); 155 156 cmp = hist_entry__cmp(&entry, he); 157 158 if (!cmp) { 159 he->period += period; 160 ++he->nr_events; 161 goto out; 162 } 163 164 if (cmp < 0) 165 p = &(*p)->rb_left; 166 else 167 p = &(*p)->rb_right; 168 } 169 170 he = hist_entry__new(&entry); 171 if (!he) 172 return NULL; 173 rb_link_node(&he->rb_node, parent, p); 174 rb_insert_color(&he->rb_node, &self->entries); 175 hists__inc_nr_entries(self, he); 176 out: 177 hist_entry__add_cpumode_period(he, al->cpumode, period); 178 return he; 179 } 180 181 int64_t 182 hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) 183 { 184 struct sort_entry *se; 185 int64_t cmp = 0; 186 187 list_for_each_entry(se, &hist_entry__sort_list, list) { 188 cmp = se->se_cmp(left, right); 189 if (cmp) 190 break; 191 } 192 193 return cmp; 194 } 195 196 int64_t 197 hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) 198 { 199 struct sort_entry *se; 200 int64_t cmp = 0; 201 202 list_for_each_entry(se, &hist_entry__sort_list, list) { 203 int64_t (*f)(struct hist_entry *, struct hist_entry *); 204 205 f = se->se_collapse ?: se->se_cmp; 206 207 cmp = f(left, right); 208 if (cmp) 209 break; 210 } 211 212 return cmp; 213 } 214 215 void hist_entry__free(struct hist_entry *he) 216 { 217 free(he); 218 } 219 220 /* 221 * collapse the histogram 222 */ 223 224 static bool hists__collapse_insert_entry(struct hists *self, 225 struct rb_root *root, 226 struct hist_entry *he) 227 { 228 struct rb_node **p = &root->rb_node; 229 struct rb_node *parent = NULL; 230 struct hist_entry *iter; 231 int64_t cmp; 232 233 while (*p != NULL) { 234 parent = *p; 235 iter = rb_entry(parent, struct hist_entry, rb_node); 236 237 cmp = hist_entry__collapse(iter, he); 238 239 if (!cmp) { 240 iter->period += he->period; 241 if (symbol_conf.use_callchain) { 242 callchain_cursor_reset(&self->callchain_cursor); 243 callchain_merge(&self->callchain_cursor, iter->callchain, 244 he->callchain); 245 } 246 hist_entry__free(he); 247 return false; 248 } 249 250 if (cmp < 0) 251 p = &(*p)->rb_left; 252 else 253 p = &(*p)->rb_right; 254 } 255 256 rb_link_node(&he->rb_node, parent, p); 257 rb_insert_color(&he->rb_node, root); 258 return true; 259 } 260 261 void hists__collapse_resort(struct hists *self) 262 { 263 struct rb_root tmp; 264 struct rb_node *next; 265 struct hist_entry *n; 266 267 if (!sort__need_collapse) 268 return; 269 270 tmp = RB_ROOT; 271 next = rb_first(&self->entries); 272 self->nr_entries = 0; 273 hists__reset_col_len(self); 274 275 while (next) { 276 n = rb_entry(next, struct hist_entry, rb_node); 277 next = rb_next(&n->rb_node); 278 279 rb_erase(&n->rb_node, &self->entries); 280 if (hists__collapse_insert_entry(self, &tmp, n)) 281 hists__inc_nr_entries(self, n); 282 } 283 284 self->entries = tmp; 285 } 286 287 /* 288 * reverse the map, sort on period. 289 */ 290 291 static void __hists__insert_output_entry(struct rb_root *entries, 292 struct hist_entry *he, 293 u64 min_callchain_hits) 294 { 295 struct rb_node **p = &entries->rb_node; 296 struct rb_node *parent = NULL; 297 struct hist_entry *iter; 298 299 if (symbol_conf.use_callchain) 300 callchain_param.sort(&he->sorted_chain, he->callchain, 301 min_callchain_hits, &callchain_param); 302 303 while (*p != NULL) { 304 parent = *p; 305 iter = rb_entry(parent, struct hist_entry, rb_node); 306 307 if (he->period > iter->period) 308 p = &(*p)->rb_left; 309 else 310 p = &(*p)->rb_right; 311 } 312 313 rb_link_node(&he->rb_node, parent, p); 314 rb_insert_color(&he->rb_node, entries); 315 } 316 317 void hists__output_resort(struct hists *self) 318 { 319 struct rb_root tmp; 320 struct rb_node *next; 321 struct hist_entry *n; 322 u64 min_callchain_hits; 323 324 min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100); 325 326 tmp = RB_ROOT; 327 next = rb_first(&self->entries); 328 329 self->nr_entries = 0; 330 hists__reset_col_len(self); 331 332 while (next) { 333 n = rb_entry(next, struct hist_entry, rb_node); 334 next = rb_next(&n->rb_node); 335 336 rb_erase(&n->rb_node, &self->entries); 337 __hists__insert_output_entry(&tmp, n, min_callchain_hits); 338 hists__inc_nr_entries(self, n); 339 } 340 341 self->entries = tmp; 342 } 343 344 static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) 345 { 346 int i; 347 int ret = fprintf(fp, " "); 348 349 for (i = 0; i < left_margin; i++) 350 ret += fprintf(fp, " "); 351 352 return ret; 353 } 354 355 static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, 356 int left_margin) 357 { 358 int i; 359 size_t ret = callchain__fprintf_left_margin(fp, left_margin); 360 361 for (i = 0; i < depth; i++) 362 if (depth_mask & (1 << i)) 363 ret += fprintf(fp, "| "); 364 else 365 ret += fprintf(fp, " "); 366 367 ret += fprintf(fp, "\n"); 368 369 return ret; 370 } 371 372 static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, 373 int depth, int depth_mask, int period, 374 u64 total_samples, u64 hits, 375 int left_margin) 376 { 377 int i; 378 size_t ret = 0; 379 380 ret += callchain__fprintf_left_margin(fp, left_margin); 381 for (i = 0; i < depth; i++) { 382 if (depth_mask & (1 << i)) 383 ret += fprintf(fp, "|"); 384 else 385 ret += fprintf(fp, " "); 386 if (!period && i == depth - 1) { 387 double percent; 388 389 percent = hits * 100.0 / total_samples; 390 ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); 391 } else 392 ret += fprintf(fp, "%s", " "); 393 } 394 if (chain->ms.sym) 395 ret += fprintf(fp, "%s\n", chain->ms.sym->name); 396 else 397 ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); 398 399 return ret; 400 } 401 402 static struct symbol *rem_sq_bracket; 403 static struct callchain_list rem_hits; 404 405 static void init_rem_hits(void) 406 { 407 rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); 408 if (!rem_sq_bracket) { 409 fprintf(stderr, "Not enough memory to display remaining hits\n"); 410 return; 411 } 412 413 strcpy(rem_sq_bracket->name, "[...]"); 414 rem_hits.ms.sym = rem_sq_bracket; 415 } 416 417 static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, 418 u64 total_samples, int depth, 419 int depth_mask, int left_margin) 420 { 421 struct rb_node *node, *next; 422 struct callchain_node *child; 423 struct callchain_list *chain; 424 int new_depth_mask = depth_mask; 425 u64 new_total; 426 u64 remaining; 427 size_t ret = 0; 428 int i; 429 uint entries_printed = 0; 430 431 if (callchain_param.mode == CHAIN_GRAPH_REL) 432 new_total = self->children_hit; 433 else 434 new_total = total_samples; 435 436 remaining = new_total; 437 438 node = rb_first(&self->rb_root); 439 while (node) { 440 u64 cumul; 441 442 child = rb_entry(node, struct callchain_node, rb_node); 443 cumul = callchain_cumul_hits(child); 444 remaining -= cumul; 445 446 /* 447 * The depth mask manages the output of pipes that show 448 * the depth. We don't want to keep the pipes of the current 449 * level for the last child of this depth. 450 * Except if we have remaining filtered hits. They will 451 * supersede the last child 452 */ 453 next = rb_next(node); 454 if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) 455 new_depth_mask &= ~(1 << (depth - 1)); 456 457 /* 458 * But we keep the older depth mask for the line separator 459 * to keep the level link until we reach the last child 460 */ 461 ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, 462 left_margin); 463 i = 0; 464 list_for_each_entry(chain, &child->val, list) { 465 ret += ipchain__fprintf_graph(fp, chain, depth, 466 new_depth_mask, i++, 467 new_total, 468 cumul, 469 left_margin); 470 } 471 ret += __callchain__fprintf_graph(fp, child, new_total, 472 depth + 1, 473 new_depth_mask | (1 << depth), 474 left_margin); 475 node = next; 476 if (++entries_printed == callchain_param.print_limit) 477 break; 478 } 479 480 if (callchain_param.mode == CHAIN_GRAPH_REL && 481 remaining && remaining != new_total) { 482 483 if (!rem_sq_bracket) 484 return ret; 485 486 new_depth_mask &= ~(1 << (depth - 1)); 487 488 ret += ipchain__fprintf_graph(fp, &rem_hits, depth, 489 new_depth_mask, 0, new_total, 490 remaining, left_margin); 491 } 492 493 return ret; 494 } 495 496 static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, 497 u64 total_samples, int left_margin) 498 { 499 struct callchain_list *chain; 500 bool printed = false; 501 int i = 0; 502 int ret = 0; 503 u32 entries_printed = 0; 504 505 list_for_each_entry(chain, &self->val, list) { 506 if (!i++ && sort__first_dimension == SORT_SYM) 507 continue; 508 509 if (!printed) { 510 ret += callchain__fprintf_left_margin(fp, left_margin); 511 ret += fprintf(fp, "|\n"); 512 ret += callchain__fprintf_left_margin(fp, left_margin); 513 ret += fprintf(fp, "---"); 514 515 left_margin += 3; 516 printed = true; 517 } else 518 ret += callchain__fprintf_left_margin(fp, left_margin); 519 520 if (chain->ms.sym) 521 ret += fprintf(fp, " %s\n", chain->ms.sym->name); 522 else 523 ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); 524 525 if (++entries_printed == callchain_param.print_limit) 526 break; 527 } 528 529 ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); 530 531 return ret; 532 } 533 534 static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, 535 u64 total_samples) 536 { 537 struct callchain_list *chain; 538 size_t ret = 0; 539 540 if (!self) 541 return 0; 542 543 ret += callchain__fprintf_flat(fp, self->parent, total_samples); 544 545 546 list_for_each_entry(chain, &self->val, list) { 547 if (chain->ip >= PERF_CONTEXT_MAX) 548 continue; 549 if (chain->ms.sym) 550 ret += fprintf(fp, " %s\n", chain->ms.sym->name); 551 else 552 ret += fprintf(fp, " %p\n", 553 (void *)(long)chain->ip); 554 } 555 556 return ret; 557 } 558 559 static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, 560 u64 total_samples, int left_margin) 561 { 562 struct rb_node *rb_node; 563 struct callchain_node *chain; 564 size_t ret = 0; 565 u32 entries_printed = 0; 566 567 rb_node = rb_first(&self->sorted_chain); 568 while (rb_node) { 569 double percent; 570 571 chain = rb_entry(rb_node, struct callchain_node, rb_node); 572 percent = chain->hit * 100.0 / total_samples; 573 switch (callchain_param.mode) { 574 case CHAIN_FLAT: 575 ret += percent_color_fprintf(fp, " %6.2f%%\n", 576 percent); 577 ret += callchain__fprintf_flat(fp, chain, total_samples); 578 break; 579 case CHAIN_GRAPH_ABS: /* Falldown */ 580 case CHAIN_GRAPH_REL: 581 ret += callchain__fprintf_graph(fp, chain, total_samples, 582 left_margin); 583 case CHAIN_NONE: 584 default: 585 break; 586 } 587 ret += fprintf(fp, "\n"); 588 if (++entries_printed == callchain_param.print_limit) 589 break; 590 rb_node = rb_next(rb_node); 591 } 592 593 return ret; 594 } 595 596 int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, 597 struct hists *hists, struct hists *pair_hists, 598 bool show_displacement, long displacement, 599 bool color, u64 session_total) 600 { 601 struct sort_entry *se; 602 u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; 603 u64 nr_events; 604 const char *sep = symbol_conf.field_sep; 605 int ret; 606 607 if (symbol_conf.exclude_other && !self->parent) 608 return 0; 609 610 if (pair_hists) { 611 period = self->pair ? self->pair->period : 0; 612 nr_events = self->pair ? self->pair->nr_events : 0; 613 total = pair_hists->stats.total_period; 614 period_sys = self->pair ? self->pair->period_sys : 0; 615 period_us = self->pair ? self->pair->period_us : 0; 616 period_guest_sys = self->pair ? self->pair->period_guest_sys : 0; 617 period_guest_us = self->pair ? self->pair->period_guest_us : 0; 618 } else { 619 period = self->period; 620 nr_events = self->nr_events; 621 total = session_total; 622 period_sys = self->period_sys; 623 period_us = self->period_us; 624 period_guest_sys = self->period_guest_sys; 625 period_guest_us = self->period_guest_us; 626 } 627 628 if (total) { 629 if (color) 630 ret = percent_color_snprintf(s, size, 631 sep ? "%.2f" : " %6.2f%%", 632 (period * 100.0) / total); 633 else 634 ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", 635 (period * 100.0) / total); 636 if (symbol_conf.show_cpu_utilization) { 637 ret += percent_color_snprintf(s + ret, size - ret, 638 sep ? "%.2f" : " %6.2f%%", 639 (period_sys * 100.0) / total); 640 ret += percent_color_snprintf(s + ret, size - ret, 641 sep ? "%.2f" : " %6.2f%%", 642 (period_us * 100.0) / total); 643 if (perf_guest) { 644 ret += percent_color_snprintf(s + ret, 645 size - ret, 646 sep ? "%.2f" : " %6.2f%%", 647 (period_guest_sys * 100.0) / 648 total); 649 ret += percent_color_snprintf(s + ret, 650 size - ret, 651 sep ? "%.2f" : " %6.2f%%", 652 (period_guest_us * 100.0) / 653 total); 654 } 655 } 656 } else 657 ret = snprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period); 658 659 if (symbol_conf.show_nr_samples) { 660 if (sep) 661 ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events); 662 else 663 ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); 664 } 665 666 if (pair_hists) { 667 char bf[32]; 668 double old_percent = 0, new_percent = 0, diff; 669 670 if (total > 0) 671 old_percent = (period * 100.0) / total; 672 if (session_total > 0) 673 new_percent = (self->period * 100.0) / session_total; 674 675 diff = new_percent - old_percent; 676 677 if (fabs(diff) >= 0.01) 678 snprintf(bf, sizeof(bf), "%+4.2F%%", diff); 679 else 680 snprintf(bf, sizeof(bf), " "); 681 682 if (sep) 683 ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); 684 else 685 ret += snprintf(s + ret, size - ret, "%11.11s", bf); 686 687 if (show_displacement) { 688 if (displacement) 689 snprintf(bf, sizeof(bf), "%+4ld", displacement); 690 else 691 snprintf(bf, sizeof(bf), " "); 692 693 if (sep) 694 ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); 695 else 696 ret += snprintf(s + ret, size - ret, "%6.6s", bf); 697 } 698 } 699 700 list_for_each_entry(se, &hist_entry__sort_list, list) { 701 if (se->elide) 702 continue; 703 704 ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); 705 ret += se->se_snprintf(self, s + ret, size - ret, 706 hists__col_len(hists, se->se_width_idx)); 707 } 708 709 return ret; 710 } 711 712 int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, 713 struct hists *pair_hists, bool show_displacement, 714 long displacement, FILE *fp, u64 session_total) 715 { 716 char bf[512]; 717 hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists, 718 show_displacement, displacement, 719 true, session_total); 720 return fprintf(fp, "%s\n", bf); 721 } 722 723 static size_t hist_entry__fprintf_callchain(struct hist_entry *self, 724 struct hists *hists, FILE *fp, 725 u64 session_total) 726 { 727 int left_margin = 0; 728 729 if (sort__first_dimension == SORT_COMM) { 730 struct sort_entry *se = list_first_entry(&hist_entry__sort_list, 731 typeof(*se), list); 732 left_margin = hists__col_len(hists, se->se_width_idx); 733 left_margin -= thread__comm_len(self->thread); 734 } 735 736 return hist_entry_callchain__fprintf(fp, self, session_total, 737 left_margin); 738 } 739 740 size_t hists__fprintf(struct hists *self, struct hists *pair, 741 bool show_displacement, FILE *fp) 742 { 743 struct sort_entry *se; 744 struct rb_node *nd; 745 size_t ret = 0; 746 unsigned long position = 1; 747 long displacement = 0; 748 unsigned int width; 749 const char *sep = symbol_conf.field_sep; 750 const char *col_width = symbol_conf.col_width_list_str; 751 752 init_rem_hits(); 753 754 fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); 755 756 if (symbol_conf.show_nr_samples) { 757 if (sep) 758 fprintf(fp, "%cSamples", *sep); 759 else 760 fputs(" Samples ", fp); 761 } 762 763 if (symbol_conf.show_cpu_utilization) { 764 if (sep) { 765 ret += fprintf(fp, "%csys", *sep); 766 ret += fprintf(fp, "%cus", *sep); 767 if (perf_guest) { 768 ret += fprintf(fp, "%cguest sys", *sep); 769 ret += fprintf(fp, "%cguest us", *sep); 770 } 771 } else { 772 ret += fprintf(fp, " sys "); 773 ret += fprintf(fp, " us "); 774 if (perf_guest) { 775 ret += fprintf(fp, " guest sys "); 776 ret += fprintf(fp, " guest us "); 777 } 778 } 779 } 780 781 if (pair) { 782 if (sep) 783 ret += fprintf(fp, "%cDelta", *sep); 784 else 785 ret += fprintf(fp, " Delta "); 786 787 if (show_displacement) { 788 if (sep) 789 ret += fprintf(fp, "%cDisplacement", *sep); 790 else 791 ret += fprintf(fp, " Displ"); 792 } 793 } 794 795 list_for_each_entry(se, &hist_entry__sort_list, list) { 796 if (se->elide) 797 continue; 798 if (sep) { 799 fprintf(fp, "%c%s", *sep, se->se_header); 800 continue; 801 } 802 width = strlen(se->se_header); 803 if (symbol_conf.col_width_list_str) { 804 if (col_width) { 805 hists__set_col_len(self, se->se_width_idx, 806 atoi(col_width)); 807 col_width = strchr(col_width, ','); 808 if (col_width) 809 ++col_width; 810 } 811 } 812 if (!hists__new_col_len(self, se->se_width_idx, width)) 813 width = hists__col_len(self, se->se_width_idx); 814 fprintf(fp, " %*s", width, se->se_header); 815 } 816 fprintf(fp, "\n"); 817 818 if (sep) 819 goto print_entries; 820 821 fprintf(fp, "# ........"); 822 if (symbol_conf.show_nr_samples) 823 fprintf(fp, " .........."); 824 if (pair) { 825 fprintf(fp, " .........."); 826 if (show_displacement) 827 fprintf(fp, " ....."); 828 } 829 list_for_each_entry(se, &hist_entry__sort_list, list) { 830 unsigned int i; 831 832 if (se->elide) 833 continue; 834 835 fprintf(fp, " "); 836 width = hists__col_len(self, se->se_width_idx); 837 if (width == 0) 838 width = strlen(se->se_header); 839 for (i = 0; i < width; i++) 840 fprintf(fp, "."); 841 } 842 843 fprintf(fp, "\n#\n"); 844 845 print_entries: 846 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 847 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 848 849 if (show_displacement) { 850 if (h->pair != NULL) 851 displacement = ((long)h->pair->position - 852 (long)position); 853 else 854 displacement = 0; 855 ++position; 856 } 857 ret += hist_entry__fprintf(h, self, pair, show_displacement, 858 displacement, fp, self->stats.total_period); 859 860 if (symbol_conf.use_callchain) 861 ret += hist_entry__fprintf_callchain(h, self, fp, 862 self->stats.total_period); 863 if (h->ms.map == NULL && verbose > 1) { 864 __map_groups__fprintf_maps(&h->thread->mg, 865 MAP__FUNCTION, verbose, fp); 866 fprintf(fp, "%.10s end\n", graph_dotted_line); 867 } 868 } 869 870 free(rem_sq_bracket); 871 872 return ret; 873 } 874 875 /* 876 * See hists__fprintf to match the column widths 877 */ 878 unsigned int hists__sort_list_width(struct hists *self) 879 { 880 struct sort_entry *se; 881 int ret = 9; /* total % */ 882 883 if (symbol_conf.show_cpu_utilization) { 884 ret += 7; /* count_sys % */ 885 ret += 6; /* count_us % */ 886 if (perf_guest) { 887 ret += 13; /* count_guest_sys % */ 888 ret += 12; /* count_guest_us % */ 889 } 890 } 891 892 if (symbol_conf.show_nr_samples) 893 ret += 11; 894 895 list_for_each_entry(se, &hist_entry__sort_list, list) 896 if (!se->elide) 897 ret += 2 + hists__col_len(self, se->se_width_idx); 898 899 if (verbose) /* Addr + origin */ 900 ret += 3 + BITS_PER_LONG / 4; 901 902 return ret; 903 } 904 905 static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, 906 enum hist_filter filter) 907 { 908 h->filtered &= ~(1 << filter); 909 if (h->filtered) 910 return; 911 912 ++self->nr_entries; 913 if (h->ms.unfolded) 914 self->nr_entries += h->nr_rows; 915 h->row_offset = 0; 916 self->stats.total_period += h->period; 917 self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; 918 919 hists__calc_col_len(self, h); 920 } 921 922 void hists__filter_by_dso(struct hists *self, const struct dso *dso) 923 { 924 struct rb_node *nd; 925 926 self->nr_entries = self->stats.total_period = 0; 927 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 928 hists__reset_col_len(self); 929 930 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 931 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 932 933 if (symbol_conf.exclude_other && !h->parent) 934 continue; 935 936 if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { 937 h->filtered |= (1 << HIST_FILTER__DSO); 938 continue; 939 } 940 941 hists__remove_entry_filter(self, h, HIST_FILTER__DSO); 942 } 943 } 944 945 void hists__filter_by_thread(struct hists *self, const struct thread *thread) 946 { 947 struct rb_node *nd; 948 949 self->nr_entries = self->stats.total_period = 0; 950 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 951 hists__reset_col_len(self); 952 953 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 954 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 955 956 if (thread != NULL && h->thread != thread) { 957 h->filtered |= (1 << HIST_FILTER__THREAD); 958 continue; 959 } 960 961 hists__remove_entry_filter(self, h, HIST_FILTER__THREAD); 962 } 963 } 964 965 int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) 966 { 967 return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); 968 } 969 970 int hist_entry__annotate(struct hist_entry *he, size_t privsize) 971 { 972 /* ANDROID_CHANGE_BEGIN */ 973 #if 0 974 return symbol__annotate(he->ms.sym, he->ms.map, privsize); 975 #else 976 return symbol__annotate(he->ms.sym, he->ms.map, privsize, false); 977 #endif 978 /* ANDROID_CHANGE_END */ 979 } 980 981 void hists__inc_nr_events(struct hists *self, u32 type) 982 { 983 ++self->stats.nr_events[0]; 984 ++self->stats.nr_events[type]; 985 } 986 987 size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) 988 { 989 int i; 990 size_t ret = 0; 991 992 for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { 993 const char *name; 994 995 if (self->stats.nr_events[i] == 0) 996 continue; 997 998 name = perf_event__name(i); 999 if (!strcmp(name, "UNKNOWN")) 1000 continue; 1001 1002 ret += fprintf(fp, "%16s events: %10d\n", name, 1003 self->stats.nr_events[i]); 1004 } 1005 1006 return ret; 1007 } 1008