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