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