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