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 const char *sep = symbol_conf.field_sep; 589 int ret; 590 591 if (symbol_conf.exclude_other && !self->parent) 592 return 0; 593 594 if (pair_hists) { 595 period = self->pair ? self->pair->period : 0; 596 total = pair_hists->stats.total_period; 597 period_sys = self->pair ? self->pair->period_sys : 0; 598 period_us = self->pair ? self->pair->period_us : 0; 599 period_guest_sys = self->pair ? self->pair->period_guest_sys : 0; 600 period_guest_us = self->pair ? self->pair->period_guest_us : 0; 601 } else { 602 period = self->period; 603 total = session_total; 604 period_sys = self->period_sys; 605 period_us = self->period_us; 606 period_guest_sys = self->period_guest_sys; 607 period_guest_us = self->period_guest_us; 608 } 609 610 if (total) { 611 if (color) 612 ret = percent_color_snprintf(s, size, 613 sep ? "%.2f" : " %6.2f%%", 614 (period * 100.0) / total); 615 else 616 ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", 617 (period * 100.0) / total); 618 if (symbol_conf.show_cpu_utilization) { 619 ret += percent_color_snprintf(s + ret, size - ret, 620 sep ? "%.2f" : " %6.2f%%", 621 (period_sys * 100.0) / total); 622 ret += percent_color_snprintf(s + ret, size - ret, 623 sep ? "%.2f" : " %6.2f%%", 624 (period_us * 100.0) / total); 625 if (perf_guest) { 626 ret += percent_color_snprintf(s + ret, 627 size - ret, 628 sep ? "%.2f" : " %6.2f%%", 629 (period_guest_sys * 100.0) / 630 total); 631 ret += percent_color_snprintf(s + ret, 632 size - ret, 633 sep ? "%.2f" : " %6.2f%%", 634 (period_guest_us * 100.0) / 635 total); 636 } 637 } 638 } else 639 ret = snprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period); 640 641 if (symbol_conf.show_nr_samples) { 642 if (sep) 643 ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period); 644 else 645 ret += snprintf(s + ret, size - ret, "%11" PRIu64, period); 646 } 647 648 if (pair_hists) { 649 char bf[32]; 650 double old_percent = 0, new_percent = 0, diff; 651 652 if (total > 0) 653 old_percent = (period * 100.0) / total; 654 if (session_total > 0) 655 new_percent = (self->period * 100.0) / session_total; 656 657 diff = new_percent - old_percent; 658 659 if (fabs(diff) >= 0.01) 660 snprintf(bf, sizeof(bf), "%+4.2F%%", diff); 661 else 662 snprintf(bf, sizeof(bf), " "); 663 664 if (sep) 665 ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); 666 else 667 ret += snprintf(s + ret, size - ret, "%11.11s", bf); 668 669 if (show_displacement) { 670 if (displacement) 671 snprintf(bf, sizeof(bf), "%+4ld", displacement); 672 else 673 snprintf(bf, sizeof(bf), " "); 674 675 if (sep) 676 ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); 677 else 678 ret += snprintf(s + ret, size - ret, "%6.6s", bf); 679 } 680 } 681 682 list_for_each_entry(se, &hist_entry__sort_list, list) { 683 if (se->elide) 684 continue; 685 686 ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); 687 ret += se->se_snprintf(self, s + ret, size - ret, 688 hists__col_len(hists, se->se_width_idx)); 689 } 690 691 return ret; 692 } 693 694 int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, 695 struct hists *pair_hists, bool show_displacement, 696 long displacement, FILE *fp, u64 session_total) 697 { 698 char bf[512]; 699 hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists, 700 show_displacement, displacement, 701 true, session_total); 702 return fprintf(fp, "%s\n", bf); 703 } 704 705 static size_t hist_entry__fprintf_callchain(struct hist_entry *self, 706 struct hists *hists, FILE *fp, 707 u64 session_total) 708 { 709 int left_margin = 0; 710 711 if (sort__first_dimension == SORT_COMM) { 712 struct sort_entry *se = list_first_entry(&hist_entry__sort_list, 713 typeof(*se), list); 714 left_margin = hists__col_len(hists, se->se_width_idx); 715 left_margin -= thread__comm_len(self->thread); 716 } 717 718 return hist_entry_callchain__fprintf(fp, self, session_total, 719 left_margin); 720 } 721 722 size_t hists__fprintf(struct hists *self, struct hists *pair, 723 bool show_displacement, FILE *fp) 724 { 725 struct sort_entry *se; 726 struct rb_node *nd; 727 size_t ret = 0; 728 unsigned long position = 1; 729 long displacement = 0; 730 unsigned int width; 731 const char *sep = symbol_conf.field_sep; 732 const char *col_width = symbol_conf.col_width_list_str; 733 734 init_rem_hits(); 735 736 fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); 737 738 if (symbol_conf.show_nr_samples) { 739 if (sep) 740 fprintf(fp, "%cSamples", *sep); 741 else 742 fputs(" Samples ", fp); 743 } 744 745 if (symbol_conf.show_cpu_utilization) { 746 if (sep) { 747 ret += fprintf(fp, "%csys", *sep); 748 ret += fprintf(fp, "%cus", *sep); 749 if (perf_guest) { 750 ret += fprintf(fp, "%cguest sys", *sep); 751 ret += fprintf(fp, "%cguest us", *sep); 752 } 753 } else { 754 ret += fprintf(fp, " sys "); 755 ret += fprintf(fp, " us "); 756 if (perf_guest) { 757 ret += fprintf(fp, " guest sys "); 758 ret += fprintf(fp, " guest us "); 759 } 760 } 761 } 762 763 if (pair) { 764 if (sep) 765 ret += fprintf(fp, "%cDelta", *sep); 766 else 767 ret += fprintf(fp, " Delta "); 768 769 if (show_displacement) { 770 if (sep) 771 ret += fprintf(fp, "%cDisplacement", *sep); 772 else 773 ret += fprintf(fp, " Displ"); 774 } 775 } 776 777 list_for_each_entry(se, &hist_entry__sort_list, list) { 778 if (se->elide) 779 continue; 780 if (sep) { 781 fprintf(fp, "%c%s", *sep, se->se_header); 782 continue; 783 } 784 width = strlen(se->se_header); 785 if (symbol_conf.col_width_list_str) { 786 if (col_width) { 787 hists__set_col_len(self, se->se_width_idx, 788 atoi(col_width)); 789 col_width = strchr(col_width, ','); 790 if (col_width) 791 ++col_width; 792 } 793 } 794 if (!hists__new_col_len(self, se->se_width_idx, width)) 795 width = hists__col_len(self, se->se_width_idx); 796 fprintf(fp, " %*s", width, se->se_header); 797 } 798 fprintf(fp, "\n"); 799 800 if (sep) 801 goto print_entries; 802 803 fprintf(fp, "# ........"); 804 if (symbol_conf.show_nr_samples) 805 fprintf(fp, " .........."); 806 if (pair) { 807 fprintf(fp, " .........."); 808 if (show_displacement) 809 fprintf(fp, " ....."); 810 } 811 list_for_each_entry(se, &hist_entry__sort_list, list) { 812 unsigned int i; 813 814 if (se->elide) 815 continue; 816 817 fprintf(fp, " "); 818 width = hists__col_len(self, se->se_width_idx); 819 if (width == 0) 820 width = strlen(se->se_header); 821 for (i = 0; i < width; i++) 822 fprintf(fp, "."); 823 } 824 825 fprintf(fp, "\n#\n"); 826 827 print_entries: 828 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 829 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 830 831 if (show_displacement) { 832 if (h->pair != NULL) 833 displacement = ((long)h->pair->position - 834 (long)position); 835 else 836 displacement = 0; 837 ++position; 838 } 839 ret += hist_entry__fprintf(h, self, pair, show_displacement, 840 displacement, fp, self->stats.total_period); 841 842 if (symbol_conf.use_callchain) 843 ret += hist_entry__fprintf_callchain(h, self, fp, 844 self->stats.total_period); 845 if (h->ms.map == NULL && verbose > 1) { 846 __map_groups__fprintf_maps(&h->thread->mg, 847 MAP__FUNCTION, verbose, fp); 848 fprintf(fp, "%.10s end\n", graph_dotted_line); 849 } 850 } 851 852 free(rem_sq_bracket); 853 854 return ret; 855 } 856 857 /* 858 * See hists__fprintf to match the column widths 859 */ 860 unsigned int hists__sort_list_width(struct hists *self) 861 { 862 struct sort_entry *se; 863 int ret = 9; /* total % */ 864 865 if (symbol_conf.show_cpu_utilization) { 866 ret += 7; /* count_sys % */ 867 ret += 6; /* count_us % */ 868 if (perf_guest) { 869 ret += 13; /* count_guest_sys % */ 870 ret += 12; /* count_guest_us % */ 871 } 872 } 873 874 if (symbol_conf.show_nr_samples) 875 ret += 11; 876 877 list_for_each_entry(se, &hist_entry__sort_list, list) 878 if (!se->elide) 879 ret += 2 + hists__col_len(self, se->se_width_idx); 880 881 if (verbose) /* Addr + origin */ 882 ret += 3 + BITS_PER_LONG / 4; 883 884 return ret; 885 } 886 887 static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, 888 enum hist_filter filter) 889 { 890 h->filtered &= ~(1 << filter); 891 if (h->filtered) 892 return; 893 894 ++self->nr_entries; 895 if (h->ms.unfolded) 896 self->nr_entries += h->nr_rows; 897 h->row_offset = 0; 898 self->stats.total_period += h->period; 899 self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; 900 901 hists__calc_col_len(self, h); 902 } 903 904 void hists__filter_by_dso(struct hists *self, const struct dso *dso) 905 { 906 struct rb_node *nd; 907 908 self->nr_entries = self->stats.total_period = 0; 909 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 910 hists__reset_col_len(self); 911 912 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 913 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 914 915 if (symbol_conf.exclude_other && !h->parent) 916 continue; 917 918 if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { 919 h->filtered |= (1 << HIST_FILTER__DSO); 920 continue; 921 } 922 923 hists__remove_entry_filter(self, h, HIST_FILTER__DSO); 924 } 925 } 926 927 void hists__filter_by_thread(struct hists *self, const struct thread *thread) 928 { 929 struct rb_node *nd; 930 931 self->nr_entries = self->stats.total_period = 0; 932 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 933 hists__reset_col_len(self); 934 935 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 936 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 937 938 if (thread != NULL && h->thread != thread) { 939 h->filtered |= (1 << HIST_FILTER__THREAD); 940 continue; 941 } 942 943 hists__remove_entry_filter(self, h, HIST_FILTER__THREAD); 944 } 945 } 946 947 static int symbol__alloc_hist(struct symbol *self) 948 { 949 struct sym_priv *priv = symbol__priv(self); 950 const int size = (sizeof(*priv->hist) + 951 (self->end - self->start) * sizeof(u64)); 952 953 priv->hist = zalloc(size); 954 return priv->hist == NULL ? -1 : 0; 955 } 956 957 int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) 958 { 959 unsigned int sym_size, offset; 960 struct symbol *sym = self->ms.sym; 961 struct sym_priv *priv; 962 struct sym_hist *h; 963 964 if (!sym || !self->ms.map) 965 return 0; 966 967 priv = symbol__priv(sym); 968 if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) 969 return -ENOMEM; 970 971 sym_size = sym->end - sym->start; 972 offset = ip - sym->start; 973 974 pr_debug3("%s: ip=%#" PRIx64 "\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); 975 976 if (offset >= sym_size) 977 return 0; 978 979 h = priv->hist; 980 h->sum++; 981 h->ip[offset]++; 982 983 pr_debug3("%#" PRIx64 " %s: period++ [ip: %#" PRIx64 ", %#" PRIx64 984 "] => %" PRIu64 "\n", self->ms.sym->start, self->ms.sym->name, 985 ip, ip - self->ms.sym->start, h->ip[offset]); 986 return 0; 987 } 988 989 static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) 990 { 991 struct objdump_line *self = malloc(sizeof(*self) + privsize); 992 993 if (self != NULL) { 994 self->offset = offset; 995 self->line = line; 996 } 997 998 return self; 999 } 1000 1001 void objdump_line__free(struct objdump_line *self) 1002 { 1003 free(self->line); 1004 free(self); 1005 } 1006 1007 static void objdump__add_line(struct list_head *head, struct objdump_line *line) 1008 { 1009 list_add_tail(&line->node, head); 1010 } 1011 1012 struct objdump_line *objdump__get_next_ip_line(struct list_head *head, 1013 struct objdump_line *pos) 1014 { 1015 list_for_each_entry_continue(pos, head, node) 1016 if (pos->offset >= 0) 1017 return pos; 1018 1019 return NULL; 1020 } 1021 1022 static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, 1023 struct list_head *head, size_t privsize) 1024 { 1025 struct symbol *sym = self->ms.sym; 1026 struct objdump_line *objdump_line; 1027 char *line = NULL, *tmp, *tmp2, *c; 1028 size_t line_len; 1029 s64 line_ip, offset = -1; 1030 1031 if (getline(&line, &line_len, file) < 0) 1032 return -1; 1033 1034 if (!line) 1035 return -1; 1036 1037 while (line_len != 0 && isspace(line[line_len - 1])) 1038 line[--line_len] = '\0'; 1039 1040 c = strchr(line, '\n'); 1041 if (c) 1042 *c = 0; 1043 1044 line_ip = -1; 1045 1046 /* 1047 * Strip leading spaces: 1048 */ 1049 tmp = line; 1050 while (*tmp) { 1051 if (*tmp != ' ') 1052 break; 1053 tmp++; 1054 } 1055 1056 if (*tmp) { 1057 /* 1058 * Parse hexa addresses followed by ':' 1059 */ 1060 line_ip = strtoull(tmp, &tmp2, 16); 1061 if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') 1062 line_ip = -1; 1063 } 1064 1065 if (line_ip != -1) { 1066 u64 start = map__rip_2objdump(self->ms.map, sym->start), 1067 end = map__rip_2objdump(self->ms.map, sym->end); 1068 1069 offset = line_ip - start; 1070 if (offset < 0 || (u64)line_ip > end) 1071 offset = -1; 1072 } 1073 1074 objdump_line = objdump_line__new(offset, line, privsize); 1075 if (objdump_line == NULL) { 1076 free(line); 1077 return -1; 1078 } 1079 objdump__add_line(head, objdump_line); 1080 1081 return 0; 1082 } 1083 1084 int hist_entry__annotate(struct hist_entry *self, struct list_head *head, 1085 size_t privsize) 1086 { 1087 struct symbol *sym = self->ms.sym; 1088 struct map *map = self->ms.map; 1089 struct dso *dso = map->dso; 1090 char *filename = dso__build_id_filename(dso, NULL, 0); 1091 bool free_filename = true; 1092 char command[PATH_MAX * 2]; 1093 FILE *file; 1094 int err = 0; 1095 u64 len; 1096 char symfs_filename[PATH_MAX]; 1097 1098 if (filename) { 1099 snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", 1100 symbol_conf.symfs, filename); 1101 } 1102 1103 if (filename == NULL) { 1104 if (dso->has_build_id) { 1105 pr_err("Can't annotate %s: not enough memory\n", 1106 sym->name); 1107 return -ENOMEM; 1108 } 1109 goto fallback; 1110 } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || 1111 strstr(command, "[kernel.kallsyms]") || 1112 access(symfs_filename, R_OK)) { 1113 free(filename); 1114 fallback: 1115 /* 1116 * If we don't have build-ids or the build-id file isn't in the 1117 * cache, or is just a kallsyms file, well, lets hope that this 1118 * DSO is the same as when 'perf record' ran. 1119 */ 1120 filename = dso->long_name; 1121 snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", 1122 symbol_conf.symfs, filename); 1123 free_filename = false; 1124 } 1125 1126 if (dso->origin == DSO__ORIG_KERNEL) { 1127 if (dso->annotate_warned) 1128 goto out_free_filename; 1129 err = -ENOENT; 1130 dso->annotate_warned = 1; 1131 pr_err("Can't annotate %s: No vmlinux file was found in the " 1132 "path\n", sym->name); 1133 goto out_free_filename; 1134 } 1135 1136 pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, 1137 filename, sym->name, map->unmap_ip(map, sym->start), 1138 map->unmap_ip(map, sym->end)); 1139 1140 len = sym->end - sym->start; 1141 1142 pr_debug("annotating [%p] %30s : [%p] %30s\n", 1143 dso, dso->long_name, sym, sym->name); 1144 1145 snprintf(command, sizeof(command), 1146 "objdump --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand", 1147 map__rip_2objdump(map, sym->start), 1148 map__rip_2objdump(map, sym->end), 1149 symfs_filename, filename); 1150 1151 pr_debug("Executing: %s\n", command); 1152 1153 file = popen(command, "r"); 1154 if (!file) 1155 goto out_free_filename; 1156 1157 while (!feof(file)) 1158 if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0) 1159 break; 1160 1161 pclose(file); 1162 out_free_filename: 1163 if (free_filename) 1164 free(filename); 1165 return err; 1166 } 1167 1168 void hists__inc_nr_events(struct hists *self, u32 type) 1169 { 1170 ++self->stats.nr_events[0]; 1171 ++self->stats.nr_events[type]; 1172 } 1173 1174 size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) 1175 { 1176 int i; 1177 size_t ret = 0; 1178 1179 for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { 1180 const char *name = event__get_event_name(i); 1181 1182 if (!strcmp(name, "UNKNOWN")) 1183 continue; 1184 1185 ret += fprintf(fp, "%16s events: %10d\n", name, 1186 self->stats.nr_events[i]); 1187 } 1188 1189 return ret; 1190 } 1191