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 *he, 80 unsigned int cpumode, u64 period) 81 { 82 switch (cpumode) { 83 case PERF_RECORD_MISC_KERNEL: 84 he->period_sys += period; 85 break; 86 case PERF_RECORD_MISC_USER: 87 he->period_us += period; 88 break; 89 case PERF_RECORD_MISC_GUEST_KERNEL: 90 he->period_guest_sys += period; 91 break; 92 case PERF_RECORD_MISC_GUEST_USER: 93 he->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 *he = malloc(sizeof(*he) + callchain_size); 169 170 if (he != NULL) { 171 *he = *template; 172 he->nr_events = 1; 173 if (he->ms.map) 174 he->ms.map->referenced = true; 175 if (symbol_conf.use_callchain) 176 callchain_init(he->callchain); 177 } 178 179 return he; 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 369 while (next) { 370 n = rb_entry(next, struct hist_entry, rb_node_in); 371 next = rb_next(&n->rb_node_in); 372 373 rb_erase(&n->rb_node_in, root); 374 if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n)) { 375 /* 376 * If it wasn't combined with one of the entries already 377 * collapsed, we need to apply the filters that may have 378 * been set by, say, the hist_browser. 379 */ 380 hists__apply_filters(hists, n); 381 } 382 } 383 } 384 385 void hists__collapse_resort(struct hists *hists) 386 { 387 return __hists__collapse_resort(hists, false); 388 } 389 390 void hists__collapse_resort_threaded(struct hists *hists) 391 { 392 return __hists__collapse_resort(hists, true); 393 } 394 395 /* 396 * reverse the map, sort on period. 397 */ 398 399 static void __hists__insert_output_entry(struct rb_root *entries, 400 struct hist_entry *he, 401 u64 min_callchain_hits) 402 { 403 struct rb_node **p = &entries->rb_node; 404 struct rb_node *parent = NULL; 405 struct hist_entry *iter; 406 407 if (symbol_conf.use_callchain) 408 callchain_param.sort(&he->sorted_chain, he->callchain, 409 min_callchain_hits, &callchain_param); 410 411 while (*p != NULL) { 412 parent = *p; 413 iter = rb_entry(parent, struct hist_entry, rb_node); 414 415 if (he->period > iter->period) 416 p = &(*p)->rb_left; 417 else 418 p = &(*p)->rb_right; 419 } 420 421 rb_link_node(&he->rb_node, parent, p); 422 rb_insert_color(&he->rb_node, entries); 423 } 424 425 static void __hists__output_resort(struct hists *hists, bool threaded) 426 { 427 struct rb_root *root; 428 struct rb_node *next; 429 struct hist_entry *n; 430 u64 min_callchain_hits; 431 432 min_callchain_hits = hists->stats.total_period * (callchain_param.min_percent / 100); 433 434 if (sort__need_collapse || threaded) 435 root = &hists->entries_collapsed; 436 else 437 root = hists->entries_in; 438 439 next = rb_first(root); 440 hists->entries = RB_ROOT; 441 442 hists->nr_entries = 0; 443 hists->stats.total_period = 0; 444 hists__reset_col_len(hists); 445 446 while (next) { 447 n = rb_entry(next, struct hist_entry, rb_node_in); 448 next = rb_next(&n->rb_node_in); 449 450 __hists__insert_output_entry(&hists->entries, n, min_callchain_hits); 451 hists__inc_nr_entries(hists, n); 452 } 453 } 454 455 void hists__output_resort(struct hists *hists) 456 { 457 return __hists__output_resort(hists, false); 458 } 459 460 void hists__output_resort_threaded(struct hists *hists) 461 { 462 return __hists__output_resort(hists, true); 463 } 464 465 static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) 466 { 467 int i; 468 int ret = fprintf(fp, " "); 469 470 for (i = 0; i < left_margin; i++) 471 ret += fprintf(fp, " "); 472 473 return ret; 474 } 475 476 static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, 477 int left_margin) 478 { 479 int i; 480 size_t ret = callchain__fprintf_left_margin(fp, left_margin); 481 482 for (i = 0; i < depth; i++) 483 if (depth_mask & (1 << i)) 484 ret += fprintf(fp, "| "); 485 else 486 ret += fprintf(fp, " "); 487 488 ret += fprintf(fp, "\n"); 489 490 return ret; 491 } 492 493 static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, 494 int depth, int depth_mask, int period, 495 u64 total_samples, u64 hits, 496 int left_margin) 497 { 498 int i; 499 size_t ret = 0; 500 501 ret += callchain__fprintf_left_margin(fp, left_margin); 502 for (i = 0; i < depth; i++) { 503 if (depth_mask & (1 << i)) 504 ret += fprintf(fp, "|"); 505 else 506 ret += fprintf(fp, " "); 507 if (!period && i == depth - 1) { 508 double percent; 509 510 percent = hits * 100.0 / total_samples; 511 ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); 512 } else 513 ret += fprintf(fp, "%s", " "); 514 } 515 if (chain->ms.sym) 516 ret += fprintf(fp, "%s\n", chain->ms.sym->name); 517 else 518 ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); 519 520 return ret; 521 } 522 523 static struct symbol *rem_sq_bracket; 524 static struct callchain_list rem_hits; 525 526 static void init_rem_hits(void) 527 { 528 rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); 529 if (!rem_sq_bracket) { 530 fprintf(stderr, "Not enough memory to display remaining hits\n"); 531 return; 532 } 533 534 strcpy(rem_sq_bracket->name, "[...]"); 535 rem_hits.ms.sym = rem_sq_bracket; 536 } 537 538 static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, 539 u64 total_samples, int depth, 540 int depth_mask, int left_margin) 541 { 542 struct rb_node *node, *next; 543 struct callchain_node *child; 544 struct callchain_list *chain; 545 int new_depth_mask = depth_mask; 546 u64 new_total; 547 u64 remaining; 548 size_t ret = 0; 549 int i; 550 uint entries_printed = 0; 551 552 if (callchain_param.mode == CHAIN_GRAPH_REL) 553 new_total = self->children_hit; 554 else 555 new_total = total_samples; 556 557 remaining = new_total; 558 559 node = rb_first(&self->rb_root); 560 while (node) { 561 u64 cumul; 562 563 child = rb_entry(node, struct callchain_node, rb_node); 564 cumul = callchain_cumul_hits(child); 565 remaining -= cumul; 566 567 /* 568 * The depth mask manages the output of pipes that show 569 * the depth. We don't want to keep the pipes of the current 570 * level for the last child of this depth. 571 * Except if we have remaining filtered hits. They will 572 * supersede the last child 573 */ 574 next = rb_next(node); 575 if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) 576 new_depth_mask &= ~(1 << (depth - 1)); 577 578 /* 579 * But we keep the older depth mask for the line separator 580 * to keep the level link until we reach the last child 581 */ 582 ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, 583 left_margin); 584 i = 0; 585 list_for_each_entry(chain, &child->val, list) { 586 ret += ipchain__fprintf_graph(fp, chain, depth, 587 new_depth_mask, i++, 588 new_total, 589 cumul, 590 left_margin); 591 } 592 ret += __callchain__fprintf_graph(fp, child, new_total, 593 depth + 1, 594 new_depth_mask | (1 << depth), 595 left_margin); 596 node = next; 597 if (++entries_printed == callchain_param.print_limit) 598 break; 599 } 600 601 if (callchain_param.mode == CHAIN_GRAPH_REL && 602 remaining && remaining != new_total) { 603 604 if (!rem_sq_bracket) 605 return ret; 606 607 new_depth_mask &= ~(1 << (depth - 1)); 608 609 ret += ipchain__fprintf_graph(fp, &rem_hits, depth, 610 new_depth_mask, 0, new_total, 611 remaining, left_margin); 612 } 613 614 return ret; 615 } 616 617 static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, 618 u64 total_samples, int left_margin) 619 { 620 struct callchain_list *chain; 621 bool printed = false; 622 int i = 0; 623 int ret = 0; 624 u32 entries_printed = 0; 625 626 list_for_each_entry(chain, &self->val, list) { 627 if (!i++ && sort__first_dimension == SORT_SYM) 628 continue; 629 630 if (!printed) { 631 ret += callchain__fprintf_left_margin(fp, left_margin); 632 ret += fprintf(fp, "|\n"); 633 ret += callchain__fprintf_left_margin(fp, left_margin); 634 ret += fprintf(fp, "---"); 635 636 left_margin += 3; 637 printed = true; 638 } else 639 ret += callchain__fprintf_left_margin(fp, left_margin); 640 641 if (chain->ms.sym) 642 ret += fprintf(fp, " %s\n", chain->ms.sym->name); 643 else 644 ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); 645 646 if (++entries_printed == callchain_param.print_limit) 647 break; 648 } 649 650 ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); 651 652 return ret; 653 } 654 655 static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, 656 u64 total_samples) 657 { 658 struct callchain_list *chain; 659 size_t ret = 0; 660 661 if (!self) 662 return 0; 663 664 ret += callchain__fprintf_flat(fp, self->parent, total_samples); 665 666 667 list_for_each_entry(chain, &self->val, list) { 668 if (chain->ip >= PERF_CONTEXT_MAX) 669 continue; 670 if (chain->ms.sym) 671 ret += fprintf(fp, " %s\n", chain->ms.sym->name); 672 else 673 ret += fprintf(fp, " %p\n", 674 (void *)(long)chain->ip); 675 } 676 677 return ret; 678 } 679 680 static size_t hist_entry_callchain__fprintf(struct hist_entry *he, 681 u64 total_samples, int left_margin, 682 FILE *fp) 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(&he->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 *he, char *s, 735 size_t size, struct hists *pair_hists, 736 bool show_displacement, long displacement, 737 bool color, u64 total_period) 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 && !he->parent) 745 return 0; 746 747 if (pair_hists) { 748 period = he->pair ? he->pair->period : 0; 749 nr_events = he->pair ? he->pair->nr_events : 0; 750 total = pair_hists->stats.total_period; 751 period_sys = he->pair ? he->pair->period_sys : 0; 752 period_us = he->pair ? he->pair->period_us : 0; 753 period_guest_sys = he->pair ? he->pair->period_guest_sys : 0; 754 period_guest_us = he->pair ? he->pair->period_guest_us : 0; 755 } else { 756 period = he->period; 757 nr_events = he->nr_events; 758 total = total_period; 759 period_sys = he->period_sys; 760 period_us = he->period_us; 761 period_guest_sys = he->period_guest_sys; 762 period_guest_us = he->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 (total_period > 0) 817 new_percent = (he->period * 100.0) / total_period; 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 static int hist_entry__fprintf(struct hist_entry *he, size_t size, 867 struct hists *hists, struct hists *pair_hists, 868 bool show_displacement, long displacement, 869 u64 total_period, FILE *fp) 870 { 871 char bf[512]; 872 int ret; 873 874 if (size == 0 || size > sizeof(bf)) 875 size = sizeof(bf); 876 877 ret = hist_entry__pcnt_snprintf(he, bf, size, pair_hists, 878 show_displacement, displacement, 879 true, total_period); 880 hist_entry__snprintf(he, bf + ret, size - ret, hists); 881 return fprintf(fp, "%s\n", bf); 882 } 883 884 static size_t hist_entry__fprintf_callchain(struct hist_entry *he, 885 struct hists *hists, 886 u64 total_period, FILE *fp) 887 { 888 int left_margin = 0; 889 890 if (sort__first_dimension == SORT_COMM) { 891 struct sort_entry *se = list_first_entry(&hist_entry__sort_list, 892 typeof(*se), list); 893 left_margin = hists__col_len(hists, se->se_width_idx); 894 left_margin -= thread__comm_len(he->thread); 895 } 896 897 return hist_entry_callchain__fprintf(he, total_period, left_margin, fp); 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 u64 total_period; 908 unsigned long position = 1; 909 long displacement = 0; 910 unsigned int width; 911 const char *sep = symbol_conf.field_sep; 912 const char *col_width = symbol_conf.col_width_list_str; 913 int nr_rows = 0; 914 915 init_rem_hits(); 916 917 if (!show_header) 918 goto print_entries; 919 920 fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); 921 922 if (symbol_conf.show_cpu_utilization) { 923 if (sep) { 924 ret += fprintf(fp, "%csys", *sep); 925 ret += fprintf(fp, "%cus", *sep); 926 if (perf_guest) { 927 ret += fprintf(fp, "%cguest sys", *sep); 928 ret += fprintf(fp, "%cguest us", *sep); 929 } 930 } else { 931 ret += fprintf(fp, " sys "); 932 ret += fprintf(fp, " us "); 933 if (perf_guest) { 934 ret += fprintf(fp, " guest sys "); 935 ret += fprintf(fp, " guest us "); 936 } 937 } 938 } 939 940 if (symbol_conf.show_nr_samples) { 941 if (sep) 942 fprintf(fp, "%cSamples", *sep); 943 else 944 fputs(" Samples ", fp); 945 } 946 947 if (symbol_conf.show_total_period) { 948 if (sep) 949 ret += fprintf(fp, "%cPeriod", *sep); 950 else 951 ret += fprintf(fp, " Period "); 952 } 953 954 if (pair) { 955 if (sep) 956 ret += fprintf(fp, "%cDelta", *sep); 957 else 958 ret += fprintf(fp, " Delta "); 959 960 if (show_displacement) { 961 if (sep) 962 ret += fprintf(fp, "%cDisplacement", *sep); 963 else 964 ret += fprintf(fp, " Displ"); 965 } 966 } 967 968 list_for_each_entry(se, &hist_entry__sort_list, list) { 969 if (se->elide) 970 continue; 971 if (sep) { 972 fprintf(fp, "%c%s", *sep, se->se_header); 973 continue; 974 } 975 width = strlen(se->se_header); 976 if (symbol_conf.col_width_list_str) { 977 if (col_width) { 978 hists__set_col_len(hists, se->se_width_idx, 979 atoi(col_width)); 980 col_width = strchr(col_width, ','); 981 if (col_width) 982 ++col_width; 983 } 984 } 985 if (!hists__new_col_len(hists, se->se_width_idx, width)) 986 width = hists__col_len(hists, se->se_width_idx); 987 fprintf(fp, " %*s", width, se->se_header); 988 } 989 990 fprintf(fp, "\n"); 991 if (max_rows && ++nr_rows >= max_rows) 992 goto out; 993 994 if (sep) 995 goto print_entries; 996 997 fprintf(fp, "# ........"); 998 if (symbol_conf.show_cpu_utilization) 999 fprintf(fp, " ....... ......."); 1000 if (symbol_conf.show_nr_samples) 1001 fprintf(fp, " .........."); 1002 if (symbol_conf.show_total_period) 1003 fprintf(fp, " ............"); 1004 if (pair) { 1005 fprintf(fp, " .........."); 1006 if (show_displacement) 1007 fprintf(fp, " ....."); 1008 } 1009 list_for_each_entry(se, &hist_entry__sort_list, list) { 1010 unsigned int i; 1011 1012 if (se->elide) 1013 continue; 1014 1015 fprintf(fp, " "); 1016 width = hists__col_len(hists, se->se_width_idx); 1017 if (width == 0) 1018 width = strlen(se->se_header); 1019 for (i = 0; i < width; i++) 1020 fprintf(fp, "."); 1021 } 1022 1023 fprintf(fp, "\n"); 1024 if (max_rows && ++nr_rows >= max_rows) 1025 goto out; 1026 1027 fprintf(fp, "#\n"); 1028 if (max_rows && ++nr_rows >= max_rows) 1029 goto out; 1030 1031 print_entries: 1032 total_period = hists->stats.total_period; 1033 1034 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 1035 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1036 1037 if (h->filtered) 1038 continue; 1039 1040 if (show_displacement) { 1041 if (h->pair != NULL) 1042 displacement = ((long)h->pair->position - 1043 (long)position); 1044 else 1045 displacement = 0; 1046 ++position; 1047 } 1048 ret += hist_entry__fprintf(h, max_cols, hists, pair, show_displacement, 1049 displacement, total_period, fp); 1050 1051 if (symbol_conf.use_callchain) 1052 ret += hist_entry__fprintf_callchain(h, hists, total_period, fp); 1053 if (max_rows && ++nr_rows >= max_rows) 1054 goto out; 1055 1056 if (h->ms.map == NULL && verbose > 1) { 1057 __map_groups__fprintf_maps(&h->thread->mg, 1058 MAP__FUNCTION, verbose, fp); 1059 fprintf(fp, "%.10s end\n", graph_dotted_line); 1060 } 1061 } 1062 out: 1063 free(rem_sq_bracket); 1064 1065 return ret; 1066 } 1067 1068 /* 1069 * See hists__fprintf to match the column widths 1070 */ 1071 unsigned int hists__sort_list_width(struct hists *hists) 1072 { 1073 struct sort_entry *se; 1074 int ret = 9; /* total % */ 1075 1076 if (symbol_conf.show_cpu_utilization) { 1077 ret += 7; /* count_sys % */ 1078 ret += 6; /* count_us % */ 1079 if (perf_guest) { 1080 ret += 13; /* count_guest_sys % */ 1081 ret += 12; /* count_guest_us % */ 1082 } 1083 } 1084 1085 if (symbol_conf.show_nr_samples) 1086 ret += 11; 1087 1088 if (symbol_conf.show_total_period) 1089 ret += 13; 1090 1091 list_for_each_entry(se, &hist_entry__sort_list, list) 1092 if (!se->elide) 1093 ret += 2 + hists__col_len(hists, se->se_width_idx); 1094 1095 if (verbose) /* Addr + origin */ 1096 ret += 3 + BITS_PER_LONG / 4; 1097 1098 return ret; 1099 } 1100 1101 static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h, 1102 enum hist_filter filter) 1103 { 1104 h->filtered &= ~(1 << filter); 1105 if (h->filtered) 1106 return; 1107 1108 ++hists->nr_entries; 1109 if (h->ms.unfolded) 1110 hists->nr_entries += h->nr_rows; 1111 h->row_offset = 0; 1112 hists->stats.total_period += h->period; 1113 hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; 1114 1115 hists__calc_col_len(hists, h); 1116 } 1117 1118 1119 static bool hists__filter_entry_by_dso(struct hists *hists, 1120 struct hist_entry *he) 1121 { 1122 if (hists->dso_filter != NULL && 1123 (he->ms.map == NULL || he->ms.map->dso != hists->dso_filter)) { 1124 he->filtered |= (1 << HIST_FILTER__DSO); 1125 return true; 1126 } 1127 1128 return false; 1129 } 1130 1131 void hists__filter_by_dso(struct hists *hists) 1132 { 1133 struct rb_node *nd; 1134 1135 hists->nr_entries = hists->stats.total_period = 0; 1136 hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 1137 hists__reset_col_len(hists); 1138 1139 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 1140 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1141 1142 if (symbol_conf.exclude_other && !h->parent) 1143 continue; 1144 1145 if (hists__filter_entry_by_dso(hists, h)) 1146 continue; 1147 1148 hists__remove_entry_filter(hists, h, HIST_FILTER__DSO); 1149 } 1150 } 1151 1152 static bool hists__filter_entry_by_thread(struct hists *hists, 1153 struct hist_entry *he) 1154 { 1155 if (hists->thread_filter != NULL && 1156 he->thread != hists->thread_filter) { 1157 he->filtered |= (1 << HIST_FILTER__THREAD); 1158 return true; 1159 } 1160 1161 return false; 1162 } 1163 1164 void hists__filter_by_thread(struct hists *hists) 1165 { 1166 struct rb_node *nd; 1167 1168 hists->nr_entries = hists->stats.total_period = 0; 1169 hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 1170 hists__reset_col_len(hists); 1171 1172 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 1173 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1174 1175 if (hists__filter_entry_by_thread(hists, h)) 1176 continue; 1177 1178 hists__remove_entry_filter(hists, h, HIST_FILTER__THREAD); 1179 } 1180 } 1181 1182 int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) 1183 { 1184 return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); 1185 } 1186 1187 int hist_entry__annotate(struct hist_entry *he, size_t privsize) 1188 { 1189 return symbol__annotate(he->ms.sym, he->ms.map, privsize); 1190 } 1191 1192 void hists__inc_nr_events(struct hists *hists, u32 type) 1193 { 1194 ++hists->stats.nr_events[0]; 1195 ++hists->stats.nr_events[type]; 1196 } 1197 1198 size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) 1199 { 1200 int i; 1201 size_t ret = 0; 1202 1203 for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { 1204 const char *name; 1205 1206 if (hists->stats.nr_events[i] == 0) 1207 continue; 1208 1209 name = perf_event__name(i); 1210 if (!strcmp(name, "UNKNOWN")) 1211 continue; 1212 1213 ret += fprintf(fp, "%16s events: %10d\n", name, 1214 hists->stats.nr_events[i]); 1215 } 1216 1217 return ret; 1218 } 1219