.. | .. |
---|
6 | 6 | #include <stdlib.h> |
---|
7 | 7 | #include <string.h> |
---|
8 | 8 | #include <linux/rbtree.h> |
---|
| 9 | +#include <linux/string.h> |
---|
9 | 10 | #include <sys/ttydefaults.h> |
---|
| 11 | +#include <linux/time64.h> |
---|
| 12 | +#include <linux/zalloc.h> |
---|
10 | 13 | |
---|
| 14 | +#include "../../util/debug.h" |
---|
| 15 | +#include "../../util/dso.h" |
---|
| 16 | +#include "../../util/callchain.h" |
---|
11 | 17 | #include "../../util/evsel.h" |
---|
12 | 18 | #include "../../util/evlist.h" |
---|
| 19 | +#include "../../util/header.h" |
---|
13 | 20 | #include "../../util/hist.h" |
---|
| 21 | +#include "../../util/machine.h" |
---|
| 22 | +#include "../../util/map.h" |
---|
| 23 | +#include "../../util/maps.h" |
---|
| 24 | +#include "../../util/symbol.h" |
---|
| 25 | +#include "../../util/map_symbol.h" |
---|
| 26 | +#include "../../util/branch.h" |
---|
14 | 27 | #include "../../util/pstack.h" |
---|
15 | 28 | #include "../../util/sort.h" |
---|
16 | | -#include "../../util/util.h" |
---|
17 | 29 | #include "../../util/top.h" |
---|
18 | 30 | #include "../../util/thread.h" |
---|
| 31 | +#include "../../util/block-info.h" |
---|
19 | 32 | #include "../../arch/common.h" |
---|
| 33 | +#include "../../perf.h" |
---|
20 | 34 | |
---|
21 | 35 | #include "../browsers/hists.h" |
---|
22 | 36 | #include "../helpline.h" |
---|
.. | .. |
---|
27 | 41 | #include "srcline.h" |
---|
28 | 42 | #include "string2.h" |
---|
29 | 43 | #include "units.h" |
---|
| 44 | +#include "time-utils.h" |
---|
30 | 45 | |
---|
31 | | -#include "sane_ctype.h" |
---|
| 46 | +#include <linux/ctype.h> |
---|
32 | 47 | |
---|
33 | 48 | extern void hist_browser__init_hpp(void); |
---|
34 | 49 | |
---|
.. | .. |
---|
49 | 64 | struct hists *hists = browser->hists; |
---|
50 | 65 | int unfolded_rows = 0; |
---|
51 | 66 | |
---|
52 | | - for (nd = rb_first(&hists->entries); |
---|
| 67 | + for (nd = rb_first_cached(&hists->entries); |
---|
53 | 68 | (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL; |
---|
54 | 69 | nd = rb_hierarchy_next(nd)) { |
---|
55 | 70 | struct hist_entry *he = |
---|
.. | .. |
---|
267 | 282 | if (he->has_no_entry) |
---|
268 | 283 | return 1; |
---|
269 | 284 | |
---|
270 | | - node = rb_first(&he->hroot_out); |
---|
| 285 | + node = rb_first_cached(&he->hroot_out); |
---|
271 | 286 | while (node) { |
---|
272 | 287 | float percent; |
---|
273 | 288 | |
---|
.. | .. |
---|
372 | 387 | he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain); |
---|
373 | 388 | callchain__init_have_children(&he->sorted_chain); |
---|
374 | 389 | } else { |
---|
375 | | - he->has_children = !RB_EMPTY_ROOT(&he->hroot_out); |
---|
| 390 | + he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root); |
---|
376 | 391 | } |
---|
377 | 392 | |
---|
378 | 393 | he->init_have_children = true; |
---|
| 394 | +} |
---|
| 395 | + |
---|
| 396 | +static bool hist_browser__selection_has_children(struct hist_browser *browser) |
---|
| 397 | +{ |
---|
| 398 | + struct hist_entry *he = browser->he_selection; |
---|
| 399 | + struct map_symbol *ms = browser->selection; |
---|
| 400 | + |
---|
| 401 | + if (!he || !ms) |
---|
| 402 | + return false; |
---|
| 403 | + |
---|
| 404 | + if (ms == &he->ms) |
---|
| 405 | + return he->has_children; |
---|
| 406 | + |
---|
| 407 | + return container_of(ms, struct callchain_list, ms)->has_children; |
---|
| 408 | +} |
---|
| 409 | + |
---|
| 410 | +static bool hist_browser__selection_unfolded(struct hist_browser *browser) |
---|
| 411 | +{ |
---|
| 412 | + struct hist_entry *he = browser->he_selection; |
---|
| 413 | + struct map_symbol *ms = browser->selection; |
---|
| 414 | + |
---|
| 415 | + if (!he || !ms) |
---|
| 416 | + return false; |
---|
| 417 | + |
---|
| 418 | + if (ms == &he->ms) |
---|
| 419 | + return he->unfolded; |
---|
| 420 | + |
---|
| 421 | + return container_of(ms, struct callchain_list, ms)->unfolded; |
---|
| 422 | +} |
---|
| 423 | + |
---|
| 424 | +static char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size) |
---|
| 425 | +{ |
---|
| 426 | + struct hist_entry *he = browser->he_selection; |
---|
| 427 | + struct map_symbol *ms = browser->selection; |
---|
| 428 | + struct callchain_list *callchain_entry; |
---|
| 429 | + |
---|
| 430 | + if (!he || !ms) |
---|
| 431 | + return NULL; |
---|
| 432 | + |
---|
| 433 | + if (ms == &he->ms) { |
---|
| 434 | + hist_entry__sym_snprintf(he, bf, size, 0); |
---|
| 435 | + return bf + 4; // skip the level, e.g. '[k] ' |
---|
| 436 | + } |
---|
| 437 | + |
---|
| 438 | + callchain_entry = container_of(ms, struct callchain_list, ms); |
---|
| 439 | + return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso); |
---|
379 | 440 | } |
---|
380 | 441 | |
---|
381 | 442 | static bool hist_browser__toggle_fold(struct hist_browser *browser) |
---|
.. | .. |
---|
508 | 569 | struct hist_entry *child; |
---|
509 | 570 | int n = 0; |
---|
510 | 571 | |
---|
511 | | - for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) { |
---|
| 572 | + for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) { |
---|
512 | 573 | child = rb_entry(nd, struct hist_entry, rb_node); |
---|
513 | 574 | percent = hist_entry__get_percent_limit(child); |
---|
514 | 575 | if (!child->filtered && percent >= hb->min_pcnt) |
---|
.. | .. |
---|
518 | 579 | return n; |
---|
519 | 580 | } |
---|
520 | 581 | |
---|
521 | | -static void __hist_entry__set_folding(struct hist_entry *he, |
---|
522 | | - struct hist_browser *hb, bool unfold) |
---|
| 582 | +static void hist_entry__set_folding(struct hist_entry *he, |
---|
| 583 | + struct hist_browser *hb, bool unfold) |
---|
523 | 584 | { |
---|
524 | 585 | hist_entry__init_have_children(he); |
---|
525 | 586 | he->unfolded = unfold ? he->has_children : false; |
---|
.. | .. |
---|
537 | 598 | he->nr_rows = 0; |
---|
538 | 599 | } |
---|
539 | 600 | |
---|
540 | | -static void hist_entry__set_folding(struct hist_entry *he, |
---|
541 | | - struct hist_browser *browser, bool unfold) |
---|
542 | | -{ |
---|
543 | | - double percent; |
---|
544 | | - |
---|
545 | | - percent = hist_entry__get_percent_limit(he); |
---|
546 | | - if (he->filtered || percent < browser->min_pcnt) |
---|
547 | | - return; |
---|
548 | | - |
---|
549 | | - __hist_entry__set_folding(he, browser, unfold); |
---|
550 | | - |
---|
551 | | - if (!he->depth || unfold) |
---|
552 | | - browser->nr_hierarchy_entries++; |
---|
553 | | - if (he->leaf) |
---|
554 | | - browser->nr_callchain_rows += he->nr_rows; |
---|
555 | | - else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) { |
---|
556 | | - browser->nr_hierarchy_entries++; |
---|
557 | | - he->has_no_entry = true; |
---|
558 | | - he->nr_rows = 1; |
---|
559 | | - } else |
---|
560 | | - he->has_no_entry = false; |
---|
561 | | -} |
---|
562 | | - |
---|
563 | 601 | static void |
---|
564 | 602 | __hist_browser__set_folding(struct hist_browser *browser, bool unfold) |
---|
565 | 603 | { |
---|
566 | 604 | struct rb_node *nd; |
---|
567 | 605 | struct hist_entry *he; |
---|
| 606 | + double percent; |
---|
568 | 607 | |
---|
569 | | - nd = rb_first(&browser->hists->entries); |
---|
| 608 | + nd = rb_first_cached(&browser->hists->entries); |
---|
570 | 609 | while (nd) { |
---|
571 | 610 | he = rb_entry(nd, struct hist_entry, rb_node); |
---|
572 | 611 | |
---|
.. | .. |
---|
574 | 613 | nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD); |
---|
575 | 614 | |
---|
576 | 615 | hist_entry__set_folding(he, browser, unfold); |
---|
| 616 | + |
---|
| 617 | + percent = hist_entry__get_percent_limit(he); |
---|
| 618 | + if (he->filtered || percent < browser->min_pcnt) |
---|
| 619 | + continue; |
---|
| 620 | + |
---|
| 621 | + if (!he->depth || unfold) |
---|
| 622 | + browser->nr_hierarchy_entries++; |
---|
| 623 | + if (he->leaf) |
---|
| 624 | + browser->nr_callchain_rows += he->nr_rows; |
---|
| 625 | + else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) { |
---|
| 626 | + browser->nr_hierarchy_entries++; |
---|
| 627 | + he->has_no_entry = true; |
---|
| 628 | + he->nr_rows = 1; |
---|
| 629 | + } else |
---|
| 630 | + he->has_no_entry = false; |
---|
577 | 631 | } |
---|
578 | 632 | } |
---|
579 | 633 | |
---|
.. | .. |
---|
593 | 647 | if (!browser->he_selection) |
---|
594 | 648 | return; |
---|
595 | 649 | |
---|
596 | | - hist_entry__set_folding(browser->he_selection, browser, unfold); |
---|
597 | | - browser->b.nr_entries = hist_browser__nr_entries(browser); |
---|
| 650 | + if (unfold == browser->he_selection->unfolded) |
---|
| 651 | + return; |
---|
| 652 | + |
---|
| 653 | + hist_browser__toggle_fold(browser); |
---|
598 | 654 | } |
---|
599 | 655 | |
---|
600 | 656 | static void ui_browser__warn_lost_events(struct ui_browser *browser) |
---|
.. | .. |
---|
611 | 667 | return browser->title ? browser->title(browser, bf, size) : 0; |
---|
612 | 668 | } |
---|
613 | 669 | |
---|
614 | | -int hist_browser__run(struct hist_browser *browser, const char *help, |
---|
615 | | - bool warn_lost_event) |
---|
| 670 | +static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, size_t size, int key) |
---|
616 | 671 | { |
---|
617 | | - int key; |
---|
| 672 | + switch (key) { |
---|
| 673 | + case K_TIMER: { |
---|
| 674 | + struct hist_browser_timer *hbt = browser->hbt; |
---|
| 675 | + u64 nr_entries; |
---|
| 676 | + |
---|
| 677 | + WARN_ON_ONCE(!hbt); |
---|
| 678 | + |
---|
| 679 | + if (hbt) |
---|
| 680 | + hbt->timer(hbt->arg); |
---|
| 681 | + |
---|
| 682 | + if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy) |
---|
| 683 | + hist_browser__update_nr_entries(browser); |
---|
| 684 | + |
---|
| 685 | + nr_entries = hist_browser__nr_entries(browser); |
---|
| 686 | + ui_browser__update_nr_entries(&browser->b, nr_entries); |
---|
| 687 | + |
---|
| 688 | + if (warn_lost_event && |
---|
| 689 | + (browser->hists->stats.nr_lost_warned != |
---|
| 690 | + browser->hists->stats.nr_events[PERF_RECORD_LOST])) { |
---|
| 691 | + browser->hists->stats.nr_lost_warned = |
---|
| 692 | + browser->hists->stats.nr_events[PERF_RECORD_LOST]; |
---|
| 693 | + ui_browser__warn_lost_events(&browser->b); |
---|
| 694 | + } |
---|
| 695 | + |
---|
| 696 | + hist_browser__title(browser, title, size); |
---|
| 697 | + ui_browser__show_title(&browser->b, title); |
---|
| 698 | + break; |
---|
| 699 | + } |
---|
| 700 | + case 'D': { /* Debug */ |
---|
| 701 | + struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node); |
---|
| 702 | + static int seq; |
---|
| 703 | + |
---|
| 704 | + ui_helpline__pop(); |
---|
| 705 | + ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", |
---|
| 706 | + seq++, browser->b.nr_entries, browser->hists->nr_entries, |
---|
| 707 | + browser->b.extra_title_lines, browser->b.rows, |
---|
| 708 | + browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows); |
---|
| 709 | + } |
---|
| 710 | + break; |
---|
| 711 | + case 'C': |
---|
| 712 | + /* Collapse the whole world. */ |
---|
| 713 | + hist_browser__set_folding(browser, false); |
---|
| 714 | + break; |
---|
| 715 | + case 'c': |
---|
| 716 | + /* Collapse the selected entry. */ |
---|
| 717 | + hist_browser__set_folding_selected(browser, false); |
---|
| 718 | + break; |
---|
| 719 | + case 'E': |
---|
| 720 | + /* Expand the whole world. */ |
---|
| 721 | + hist_browser__set_folding(browser, true); |
---|
| 722 | + break; |
---|
| 723 | + case 'e': |
---|
| 724 | + /* Toggle expand/collapse the selected entry. */ |
---|
| 725 | + hist_browser__toggle_fold(browser); |
---|
| 726 | + break; |
---|
| 727 | + case 'H': |
---|
| 728 | + browser->show_headers = !browser->show_headers; |
---|
| 729 | + hist_browser__update_rows(browser); |
---|
| 730 | + break; |
---|
| 731 | + case '+': |
---|
| 732 | + if (hist_browser__toggle_fold(browser)) |
---|
| 733 | + break; |
---|
| 734 | + /* fall thru */ |
---|
| 735 | + default: |
---|
| 736 | + return -1; |
---|
| 737 | + } |
---|
| 738 | + |
---|
| 739 | + return 0; |
---|
| 740 | +} |
---|
| 741 | + |
---|
| 742 | +int hist_browser__run(struct hist_browser *browser, const char *help, |
---|
| 743 | + bool warn_lost_event, int key) |
---|
| 744 | +{ |
---|
618 | 745 | char title[160]; |
---|
619 | 746 | struct hist_browser_timer *hbt = browser->hbt; |
---|
620 | 747 | int delay_secs = hbt ? hbt->refresh : 0; |
---|
.. | .. |
---|
627 | 754 | if (ui_browser__show(&browser->b, title, "%s", help) < 0) |
---|
628 | 755 | return -1; |
---|
629 | 756 | |
---|
| 757 | + if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key)) |
---|
| 758 | + goto out; |
---|
| 759 | + |
---|
630 | 760 | while (1) { |
---|
631 | 761 | key = ui_browser__run(&browser->b, delay_secs); |
---|
632 | 762 | |
---|
633 | | - switch (key) { |
---|
634 | | - case K_TIMER: { |
---|
635 | | - u64 nr_entries; |
---|
636 | | - |
---|
637 | | - WARN_ON_ONCE(!hbt); |
---|
638 | | - |
---|
639 | | - if (hbt) |
---|
640 | | - hbt->timer(hbt->arg); |
---|
641 | | - |
---|
642 | | - if (hist_browser__has_filter(browser) || |
---|
643 | | - symbol_conf.report_hierarchy) |
---|
644 | | - hist_browser__update_nr_entries(browser); |
---|
645 | | - |
---|
646 | | - nr_entries = hist_browser__nr_entries(browser); |
---|
647 | | - ui_browser__update_nr_entries(&browser->b, nr_entries); |
---|
648 | | - |
---|
649 | | - if (warn_lost_event && |
---|
650 | | - (browser->hists->stats.nr_lost_warned != |
---|
651 | | - browser->hists->stats.nr_events[PERF_RECORD_LOST])) { |
---|
652 | | - browser->hists->stats.nr_lost_warned = |
---|
653 | | - browser->hists->stats.nr_events[PERF_RECORD_LOST]; |
---|
654 | | - ui_browser__warn_lost_events(&browser->b); |
---|
655 | | - } |
---|
656 | | - |
---|
657 | | - hist_browser__title(browser, title, sizeof(title)); |
---|
658 | | - ui_browser__show_title(&browser->b, title); |
---|
659 | | - continue; |
---|
660 | | - } |
---|
661 | | - case 'D': { /* Debug */ |
---|
662 | | - static int seq; |
---|
663 | | - struct hist_entry *h = rb_entry(browser->b.top, |
---|
664 | | - struct hist_entry, rb_node); |
---|
665 | | - ui_helpline__pop(); |
---|
666 | | - ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", |
---|
667 | | - seq++, browser->b.nr_entries, |
---|
668 | | - browser->hists->nr_entries, |
---|
669 | | - browser->b.extra_title_lines, |
---|
670 | | - browser->b.rows, |
---|
671 | | - browser->b.index, |
---|
672 | | - browser->b.top_idx, |
---|
673 | | - h->row_offset, h->nr_rows); |
---|
674 | | - } |
---|
| 763 | + if (hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key)) |
---|
675 | 764 | break; |
---|
676 | | - case 'C': |
---|
677 | | - /* Collapse the whole world. */ |
---|
678 | | - hist_browser__set_folding(browser, false); |
---|
679 | | - break; |
---|
680 | | - case 'c': |
---|
681 | | - /* Collapse the selected entry. */ |
---|
682 | | - hist_browser__set_folding_selected(browser, false); |
---|
683 | | - break; |
---|
684 | | - case 'E': |
---|
685 | | - /* Expand the whole world. */ |
---|
686 | | - hist_browser__set_folding(browser, true); |
---|
687 | | - break; |
---|
688 | | - case 'e': |
---|
689 | | - /* Expand the selected entry. */ |
---|
690 | | - hist_browser__set_folding_selected(browser, true); |
---|
691 | | - break; |
---|
692 | | - case 'H': |
---|
693 | | - browser->show_headers = !browser->show_headers; |
---|
694 | | - hist_browser__update_rows(browser); |
---|
695 | | - break; |
---|
696 | | - case K_ENTER: |
---|
697 | | - if (hist_browser__toggle_fold(browser)) |
---|
698 | | - break; |
---|
699 | | - /* fall thru */ |
---|
700 | | - default: |
---|
701 | | - goto out; |
---|
702 | | - } |
---|
703 | 765 | } |
---|
704 | 766 | out: |
---|
705 | 767 | ui_browser__hide(&browser->b); |
---|
.. | .. |
---|
1225 | 1287 | hist_browser__hpp_color_overhead_guest_us; |
---|
1226 | 1288 | perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color = |
---|
1227 | 1289 | hist_browser__hpp_color_overhead_acc; |
---|
| 1290 | + |
---|
| 1291 | + res_sample_init(); |
---|
1228 | 1292 | } |
---|
1229 | 1293 | |
---|
1230 | 1294 | static int hist_browser__show_entry(struct hist_browser *browser, |
---|
.. | .. |
---|
1467 | 1531 | int i = 0; |
---|
1468 | 1532 | |
---|
1469 | 1533 | width -= fmt->entry(fmt, &hpp, entry); |
---|
1470 | | - ui_browser__printf(&browser->b, "%s", ltrim(s)); |
---|
| 1534 | + ui_browser__printf(&browser->b, "%s", skip_spaces(s)); |
---|
1471 | 1535 | |
---|
1472 | 1536 | while (isspace(s[i++])) |
---|
1473 | 1537 | width++; |
---|
.. | .. |
---|
1683 | 1747 | ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL); |
---|
1684 | 1748 | dummy_hpp.buf[ret] = '\0'; |
---|
1685 | 1749 | |
---|
1686 | | - start = trim(dummy_hpp.buf); |
---|
| 1750 | + start = strim(dummy_hpp.buf); |
---|
1687 | 1751 | ret = strlen(start); |
---|
1688 | 1752 | |
---|
1689 | 1753 | if (start != dummy_hpp.buf) |
---|
.. | .. |
---|
1704 | 1768 | hists_browser__scnprintf_hierarchy_headers(browser, headers, |
---|
1705 | 1769 | sizeof(headers)); |
---|
1706 | 1770 | |
---|
1707 | | - ui_browser__gotorc(&browser->b, 0, 0); |
---|
| 1771 | + ui_browser__gotorc_title(&browser->b, 0, 0); |
---|
1708 | 1772 | ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); |
---|
1709 | 1773 | ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); |
---|
1710 | 1774 | } |
---|
.. | .. |
---|
1742 | 1806 | struct hist_browser *hb; |
---|
1743 | 1807 | |
---|
1744 | 1808 | hb = container_of(browser, struct hist_browser, b); |
---|
1745 | | - browser->top = rb_first(&hb->hists->entries); |
---|
| 1809 | + browser->top = rb_first_cached(&hb->hists->entries); |
---|
1746 | 1810 | } |
---|
1747 | 1811 | } |
---|
1748 | 1812 | |
---|
.. | .. |
---|
1769 | 1833 | continue; |
---|
1770 | 1834 | } |
---|
1771 | 1835 | |
---|
1772 | | - percent = hist_entry__get_percent_limit(h); |
---|
| 1836 | + if (symbol_conf.report_individual_block) |
---|
| 1837 | + percent = block_info__total_cycles_percent(h); |
---|
| 1838 | + else |
---|
| 1839 | + percent = hist_entry__get_percent_limit(h); |
---|
| 1840 | + |
---|
1773 | 1841 | if (percent < hb->min_pcnt) |
---|
1774 | 1842 | continue; |
---|
1775 | 1843 | |
---|
.. | .. |
---|
2067 | 2135 | advance_hpp(&hpp, ret); |
---|
2068 | 2136 | } |
---|
2069 | 2137 | |
---|
2070 | | - printed += fprintf(fp, "%s\n", rtrim(s)); |
---|
| 2138 | + strim(s); |
---|
| 2139 | + printed += fprintf(fp, "%s\n", s); |
---|
2071 | 2140 | |
---|
2072 | 2141 | if (he->leaf && folded_sign == '-') { |
---|
2073 | 2142 | printed += hist_browser__fprintf_callchain(browser, he, fp, |
---|
.. | .. |
---|
2178 | 2247 | } |
---|
2179 | 2248 | |
---|
2180 | 2249 | static struct hist_browser * |
---|
2181 | | -perf_evsel_browser__new(struct perf_evsel *evsel, |
---|
| 2250 | +perf_evsel_browser__new(struct evsel *evsel, |
---|
2182 | 2251 | struct hist_browser_timer *hbt, |
---|
2183 | 2252 | struct perf_env *env, |
---|
2184 | 2253 | struct annotation_options *annotation_opts) |
---|
.. | .. |
---|
2209 | 2278 | return browser->he_selection->thread; |
---|
2210 | 2279 | } |
---|
2211 | 2280 | |
---|
| 2281 | +static struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser) |
---|
| 2282 | +{ |
---|
| 2283 | + return browser->he_selection ? browser->he_selection->res_samples : NULL; |
---|
| 2284 | +} |
---|
| 2285 | + |
---|
2212 | 2286 | /* Check whether the browser is for 'top' or 'report' */ |
---|
2213 | 2287 | static inline bool is_report_browser(void *timer) |
---|
2214 | 2288 | { |
---|
.. | .. |
---|
2223 | 2297 | if (!is_report_browser(hbt)) { |
---|
2224 | 2298 | struct perf_top *top = hbt->arg; |
---|
2225 | 2299 | |
---|
| 2300 | + printed += scnprintf(bf + printed, size - printed, |
---|
| 2301 | + " lost: %" PRIu64 "/%" PRIu64, |
---|
| 2302 | + top->lost, top->lost_total); |
---|
| 2303 | + |
---|
| 2304 | + printed += scnprintf(bf + printed, size - printed, |
---|
| 2305 | + " drop: %" PRIu64 "/%" PRIu64, |
---|
| 2306 | + top->drop, top->drop_total); |
---|
| 2307 | + |
---|
2226 | 2308 | if (top->zero) |
---|
2227 | 2309 | printed += scnprintf(bf + printed, size - printed, " [z]"); |
---|
| 2310 | + |
---|
| 2311 | + perf_top__reset_sample_counters(top); |
---|
2228 | 2312 | } |
---|
| 2313 | + |
---|
2229 | 2314 | |
---|
2230 | 2315 | return printed; |
---|
2231 | 2316 | } |
---|
.. | .. |
---|
2308 | 2393 | closedir(pwd_dir); |
---|
2309 | 2394 | |
---|
2310 | 2395 | if (nr_options) { |
---|
2311 | | - choice = ui__popup_menu(nr_options, options); |
---|
| 2396 | + choice = ui__popup_menu(nr_options, options, NULL); |
---|
2312 | 2397 | if (choice < nr_options && choice >= 0) { |
---|
2313 | 2398 | tmp = strdup(abs_path[choice]); |
---|
2314 | 2399 | if (tmp) { |
---|
.. | .. |
---|
2328 | 2413 | } |
---|
2329 | 2414 | |
---|
2330 | 2415 | struct popup_action { |
---|
| 2416 | + unsigned long time; |
---|
2331 | 2417 | struct thread *thread; |
---|
2332 | 2418 | struct map_symbol ms; |
---|
2333 | 2419 | int socket; |
---|
| 2420 | + struct evsel *evsel; |
---|
| 2421 | + enum rstype rstype; |
---|
2334 | 2422 | |
---|
2335 | 2423 | int (*fn)(struct hist_browser *browser, struct popup_action *act); |
---|
2336 | 2424 | }; |
---|
.. | .. |
---|
2338 | 2426 | static int |
---|
2339 | 2427 | do_annotate(struct hist_browser *browser, struct popup_action *act) |
---|
2340 | 2428 | { |
---|
2341 | | - struct perf_evsel *evsel; |
---|
| 2429 | + struct evsel *evsel; |
---|
2342 | 2430 | struct annotation *notes; |
---|
2343 | 2431 | struct hist_entry *he; |
---|
2344 | 2432 | int err; |
---|
.. | .. |
---|
2351 | 2439 | if (!notes->src) |
---|
2352 | 2440 | return 0; |
---|
2353 | 2441 | |
---|
2354 | | - evsel = hists_to_evsel(browser->hists); |
---|
| 2442 | + if (browser->block_evsel) |
---|
| 2443 | + evsel = browser->block_evsel; |
---|
| 2444 | + else |
---|
| 2445 | + evsel = hists_to_evsel(browser->hists); |
---|
| 2446 | + |
---|
2355 | 2447 | err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt, |
---|
2356 | 2448 | browser->annotation_opts); |
---|
2357 | 2449 | he = hist_browser__selected_entry(browser); |
---|
.. | .. |
---|
2368 | 2460 | return 0; |
---|
2369 | 2461 | } |
---|
2370 | 2462 | |
---|
| 2463 | +static struct symbol *symbol__new_unresolved(u64 addr, struct map *map) |
---|
| 2464 | +{ |
---|
| 2465 | + struct annotated_source *src; |
---|
| 2466 | + struct symbol *sym; |
---|
| 2467 | + char name[64]; |
---|
| 2468 | + |
---|
| 2469 | + snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr); |
---|
| 2470 | + |
---|
| 2471 | + sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name); |
---|
| 2472 | + if (sym) { |
---|
| 2473 | + src = symbol__hists(sym, 1); |
---|
| 2474 | + if (!src) { |
---|
| 2475 | + symbol__delete(sym); |
---|
| 2476 | + return NULL; |
---|
| 2477 | + } |
---|
| 2478 | + |
---|
| 2479 | + dso__insert_symbol(map->dso, sym); |
---|
| 2480 | + } |
---|
| 2481 | + |
---|
| 2482 | + return sym; |
---|
| 2483 | +} |
---|
| 2484 | + |
---|
2371 | 2485 | static int |
---|
2372 | 2486 | add_annotate_opt(struct hist_browser *browser __maybe_unused, |
---|
2373 | 2487 | struct popup_action *act, char **optstr, |
---|
2374 | | - struct map *map, struct symbol *sym) |
---|
| 2488 | + struct map_symbol *ms, |
---|
| 2489 | + u64 addr) |
---|
2375 | 2490 | { |
---|
2376 | | - if (sym == NULL || map->dso->annotate_warned) |
---|
| 2491 | + if (!ms->map || !ms->map->dso || ms->map->dso->annotate_warned) |
---|
2377 | 2492 | return 0; |
---|
2378 | 2493 | |
---|
2379 | | - if (asprintf(optstr, "Annotate %s", sym->name) < 0) |
---|
| 2494 | + if (!ms->sym) |
---|
| 2495 | + ms->sym = symbol__new_unresolved(addr, ms->map); |
---|
| 2496 | + |
---|
| 2497 | + if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL) |
---|
2380 | 2498 | return 0; |
---|
2381 | 2499 | |
---|
2382 | | - act->ms.map = map; |
---|
2383 | | - act->ms.sym = sym; |
---|
| 2500 | + if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0) |
---|
| 2501 | + return 0; |
---|
| 2502 | + |
---|
| 2503 | + act->ms = *ms; |
---|
2384 | 2504 | act->fn = do_annotate; |
---|
2385 | 2505 | return 1; |
---|
2386 | 2506 | } |
---|
.. | .. |
---|
2447 | 2567 | return 1; |
---|
2448 | 2568 | } |
---|
2449 | 2569 | |
---|
2450 | | -static int |
---|
2451 | | -do_zoom_dso(struct hist_browser *browser, struct popup_action *act) |
---|
| 2570 | +static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map) |
---|
2452 | 2571 | { |
---|
2453 | | - struct map *map = act->ms.map; |
---|
2454 | | - |
---|
2455 | 2572 | if (!hists__has(browser->hists, dso) || map == NULL) |
---|
2456 | 2573 | return 0; |
---|
2457 | 2574 | |
---|
.. | .. |
---|
2474 | 2591 | } |
---|
2475 | 2592 | |
---|
2476 | 2593 | static int |
---|
| 2594 | +do_zoom_dso(struct hist_browser *browser, struct popup_action *act) |
---|
| 2595 | +{ |
---|
| 2596 | + return hists_browser__zoom_map(browser, act->ms.map); |
---|
| 2597 | +} |
---|
| 2598 | + |
---|
| 2599 | +static int |
---|
2477 | 2600 | add_dso_opt(struct hist_browser *browser, struct popup_action *act, |
---|
2478 | 2601 | char **optstr, struct map *map) |
---|
2479 | 2602 | { |
---|
2480 | 2603 | if (!hists__has(browser->hists, dso) || map == NULL) |
---|
2481 | 2604 | return 0; |
---|
2482 | 2605 | |
---|
2483 | | - if (asprintf(optstr, "Zoom %s %s DSO", |
---|
| 2606 | + if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)", |
---|
2484 | 2607 | browser->hists->dso_filter ? "out of" : "into", |
---|
2485 | 2608 | __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0) |
---|
2486 | 2609 | return 0; |
---|
2487 | 2610 | |
---|
2488 | 2611 | act->ms.map = map; |
---|
2489 | 2612 | act->fn = do_zoom_dso; |
---|
| 2613 | + return 1; |
---|
| 2614 | +} |
---|
| 2615 | + |
---|
| 2616 | +static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused) |
---|
| 2617 | +{ |
---|
| 2618 | + hist_browser__toggle_fold(browser); |
---|
| 2619 | + return 0; |
---|
| 2620 | +} |
---|
| 2621 | + |
---|
| 2622 | +static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr) |
---|
| 2623 | +{ |
---|
| 2624 | + char sym_name[512]; |
---|
| 2625 | + |
---|
| 2626 | + if (!hist_browser__selection_has_children(browser)) |
---|
| 2627 | + return 0; |
---|
| 2628 | + |
---|
| 2629 | + if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)", |
---|
| 2630 | + hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand", |
---|
| 2631 | + hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0) |
---|
| 2632 | + return 0; |
---|
| 2633 | + |
---|
| 2634 | + act->fn = do_toggle_callchain; |
---|
2490 | 2635 | return 1; |
---|
2491 | 2636 | } |
---|
2492 | 2637 | |
---|
.. | .. |
---|
2517 | 2662 | do_run_script(struct hist_browser *browser __maybe_unused, |
---|
2518 | 2663 | struct popup_action *act) |
---|
2519 | 2664 | { |
---|
2520 | | - char script_opt[64]; |
---|
2521 | | - memset(script_opt, 0, sizeof(script_opt)); |
---|
| 2665 | + char *script_opt; |
---|
| 2666 | + int len; |
---|
| 2667 | + int n = 0; |
---|
2522 | 2668 | |
---|
| 2669 | + len = 100; |
---|
| 2670 | + if (act->thread) |
---|
| 2671 | + len += strlen(thread__comm_str(act->thread)); |
---|
| 2672 | + else if (act->ms.sym) |
---|
| 2673 | + len += strlen(act->ms.sym->name); |
---|
| 2674 | + script_opt = malloc(len); |
---|
| 2675 | + if (!script_opt) |
---|
| 2676 | + return -1; |
---|
| 2677 | + |
---|
| 2678 | + script_opt[0] = 0; |
---|
2523 | 2679 | if (act->thread) { |
---|
2524 | | - scnprintf(script_opt, sizeof(script_opt), " -c %s ", |
---|
| 2680 | + n = scnprintf(script_opt, len, " -c %s ", |
---|
2525 | 2681 | thread__comm_str(act->thread)); |
---|
2526 | 2682 | } else if (act->ms.sym) { |
---|
2527 | | - scnprintf(script_opt, sizeof(script_opt), " -S %s ", |
---|
| 2683 | + n = scnprintf(script_opt, len, " -S %s ", |
---|
2528 | 2684 | act->ms.sym->name); |
---|
2529 | 2685 | } |
---|
2530 | 2686 | |
---|
2531 | | - script_browse(script_opt); |
---|
| 2687 | + if (act->time) { |
---|
| 2688 | + char start[32], end[32]; |
---|
| 2689 | + unsigned long starttime = act->time; |
---|
| 2690 | + unsigned long endtime = act->time + symbol_conf.time_quantum; |
---|
| 2691 | + |
---|
| 2692 | + if (starttime == endtime) { /* Display 1ms as fallback */ |
---|
| 2693 | + starttime -= 1*NSEC_PER_MSEC; |
---|
| 2694 | + endtime += 1*NSEC_PER_MSEC; |
---|
| 2695 | + } |
---|
| 2696 | + timestamp__scnprintf_usec(starttime, start, sizeof start); |
---|
| 2697 | + timestamp__scnprintf_usec(endtime, end, sizeof end); |
---|
| 2698 | + n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end); |
---|
| 2699 | + } |
---|
| 2700 | + |
---|
| 2701 | + script_browse(script_opt, act->evsel); |
---|
| 2702 | + free(script_opt); |
---|
2532 | 2703 | return 0; |
---|
2533 | 2704 | } |
---|
2534 | 2705 | |
---|
2535 | 2706 | static int |
---|
2536 | | -add_script_opt(struct hist_browser *browser __maybe_unused, |
---|
2537 | | - struct popup_action *act, char **optstr, |
---|
2538 | | - struct thread *thread, struct symbol *sym) |
---|
| 2707 | +do_res_sample_script(struct hist_browser *browser __maybe_unused, |
---|
| 2708 | + struct popup_action *act) |
---|
2539 | 2709 | { |
---|
| 2710 | + struct hist_entry *he; |
---|
| 2711 | + |
---|
| 2712 | + he = hist_browser__selected_entry(browser); |
---|
| 2713 | + res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype); |
---|
| 2714 | + return 0; |
---|
| 2715 | +} |
---|
| 2716 | + |
---|
| 2717 | +static int |
---|
| 2718 | +add_script_opt_2(struct hist_browser *browser __maybe_unused, |
---|
| 2719 | + struct popup_action *act, char **optstr, |
---|
| 2720 | + struct thread *thread, struct symbol *sym, |
---|
| 2721 | + struct evsel *evsel, const char *tstr) |
---|
| 2722 | +{ |
---|
| 2723 | + |
---|
2540 | 2724 | if (thread) { |
---|
2541 | | - if (asprintf(optstr, "Run scripts for samples of thread [%s]", |
---|
2542 | | - thread__comm_str(thread)) < 0) |
---|
| 2725 | + if (asprintf(optstr, "Run scripts for samples of thread [%s]%s", |
---|
| 2726 | + thread__comm_str(thread), tstr) < 0) |
---|
2543 | 2727 | return 0; |
---|
2544 | 2728 | } else if (sym) { |
---|
2545 | | - if (asprintf(optstr, "Run scripts for samples of symbol [%s]", |
---|
2546 | | - sym->name) < 0) |
---|
| 2729 | + if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s", |
---|
| 2730 | + sym->name, tstr) < 0) |
---|
2547 | 2731 | return 0; |
---|
2548 | 2732 | } else { |
---|
2549 | | - if (asprintf(optstr, "Run scripts for all samples") < 0) |
---|
| 2733 | + if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0) |
---|
2550 | 2734 | return 0; |
---|
2551 | 2735 | } |
---|
2552 | 2736 | |
---|
2553 | 2737 | act->thread = thread; |
---|
2554 | 2738 | act->ms.sym = sym; |
---|
| 2739 | + act->evsel = evsel; |
---|
2555 | 2740 | act->fn = do_run_script; |
---|
| 2741 | + return 1; |
---|
| 2742 | +} |
---|
| 2743 | + |
---|
| 2744 | +static int |
---|
| 2745 | +add_script_opt(struct hist_browser *browser, |
---|
| 2746 | + struct popup_action *act, char **optstr, |
---|
| 2747 | + struct thread *thread, struct symbol *sym, |
---|
| 2748 | + struct evsel *evsel) |
---|
| 2749 | +{ |
---|
| 2750 | + int n, j; |
---|
| 2751 | + struct hist_entry *he; |
---|
| 2752 | + |
---|
| 2753 | + n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, ""); |
---|
| 2754 | + |
---|
| 2755 | + he = hist_browser__selected_entry(browser); |
---|
| 2756 | + if (sort_order && strstr(sort_order, "time")) { |
---|
| 2757 | + char tstr[128]; |
---|
| 2758 | + |
---|
| 2759 | + optstr++; |
---|
| 2760 | + act++; |
---|
| 2761 | + j = sprintf(tstr, " in "); |
---|
| 2762 | + j += timestamp__scnprintf_usec(he->time, tstr + j, |
---|
| 2763 | + sizeof tstr - j); |
---|
| 2764 | + j += sprintf(tstr + j, "-"); |
---|
| 2765 | + timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum, |
---|
| 2766 | + tstr + j, sizeof tstr - j); |
---|
| 2767 | + n += add_script_opt_2(browser, act, optstr, thread, sym, |
---|
| 2768 | + evsel, tstr); |
---|
| 2769 | + act->time = he->time; |
---|
| 2770 | + } |
---|
| 2771 | + return n; |
---|
| 2772 | +} |
---|
| 2773 | + |
---|
| 2774 | +static int |
---|
| 2775 | +add_res_sample_opt(struct hist_browser *browser __maybe_unused, |
---|
| 2776 | + struct popup_action *act, char **optstr, |
---|
| 2777 | + struct res_sample *res_sample, |
---|
| 2778 | + struct evsel *evsel, |
---|
| 2779 | + enum rstype type) |
---|
| 2780 | +{ |
---|
| 2781 | + if (!res_sample) |
---|
| 2782 | + return 0; |
---|
| 2783 | + |
---|
| 2784 | + if (asprintf(optstr, "Show context for individual samples %s", |
---|
| 2785 | + type == A_ASM ? "with assembler" : |
---|
| 2786 | + type == A_SOURCE ? "with source" : "") < 0) |
---|
| 2787 | + return 0; |
---|
| 2788 | + |
---|
| 2789 | + act->fn = do_res_sample_script; |
---|
| 2790 | + act->evsel = evsel; |
---|
| 2791 | + act->rstype = type; |
---|
2556 | 2792 | return 1; |
---|
2557 | 2793 | } |
---|
2558 | 2794 | |
---|
.. | .. |
---|
2642 | 2878 | static void hist_browser__update_nr_entries(struct hist_browser *hb) |
---|
2643 | 2879 | { |
---|
2644 | 2880 | u64 nr_entries = 0; |
---|
2645 | | - struct rb_node *nd = rb_first(&hb->hists->entries); |
---|
| 2881 | + struct rb_node *nd = rb_first_cached(&hb->hists->entries); |
---|
2646 | 2882 | |
---|
2647 | 2883 | if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) { |
---|
2648 | 2884 | hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; |
---|
.. | .. |
---|
2662 | 2898 | double percent) |
---|
2663 | 2899 | { |
---|
2664 | 2900 | struct hist_entry *he; |
---|
2665 | | - struct rb_node *nd = rb_first(&hb->hists->entries); |
---|
| 2901 | + struct rb_node *nd = rb_first_cached(&hb->hists->entries); |
---|
2666 | 2902 | u64 total = hists__total_period(hb->hists); |
---|
2667 | 2903 | u64 min_callchain_hits = total * (percent / 100); |
---|
2668 | 2904 | |
---|
.. | .. |
---|
2700 | 2936 | } |
---|
2701 | 2937 | } |
---|
2702 | 2938 | |
---|
2703 | | -static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, |
---|
| 2939 | +static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events, |
---|
2704 | 2940 | const char *helpline, |
---|
2705 | 2941 | bool left_exits, |
---|
2706 | 2942 | struct hist_browser_timer *hbt, |
---|
.. | .. |
---|
2717 | 2953 | struct popup_action actions[MAX_OPTIONS]; |
---|
2718 | 2954 | int nr_options = 0; |
---|
2719 | 2955 | int key = -1; |
---|
2720 | | - char buf[64]; |
---|
| 2956 | + char buf[128]; |
---|
2721 | 2957 | int delay_secs = hbt ? hbt->refresh : 0; |
---|
2722 | 2958 | |
---|
2723 | 2959 | #define HIST_BROWSER_HELP_COMMON \ |
---|
.. | .. |
---|
2730 | 2966 | "For symbolic views (--sort has sym):\n\n" \ |
---|
2731 | 2967 | "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \ |
---|
2732 | 2968 | "ESC Zoom out\n" \ |
---|
| 2969 | + "+ Expand/Collapse one callchain level\n" \ |
---|
2733 | 2970 | "a Annotate current symbol\n" \ |
---|
2734 | 2971 | "C Collapse all callchains\n" \ |
---|
2735 | 2972 | "d Zoom into current DSO\n" \ |
---|
| 2973 | + "e Expand/Collapse main entry callchains\n" \ |
---|
2736 | 2974 | "E Expand all callchains\n" \ |
---|
2737 | 2975 | "F Toggle percentage of filtered entries\n" \ |
---|
2738 | 2976 | "H Display column headers\n" \ |
---|
| 2977 | + "k Zoom into the kernel map\n" \ |
---|
2739 | 2978 | "L Change percent limit\n" \ |
---|
2740 | 2979 | "m Display context menu\n" \ |
---|
2741 | 2980 | "S Zoom into current Processor Socket\n" \ |
---|
2742 | 2981 | |
---|
2743 | 2982 | /* help messages are sorted by lexical order of the hotkey */ |
---|
2744 | | - const char report_help[] = HIST_BROWSER_HELP_COMMON |
---|
| 2983 | + static const char report_help[] = HIST_BROWSER_HELP_COMMON |
---|
2745 | 2984 | "i Show header information\n" |
---|
2746 | 2985 | "P Print histograms to perf.hist.N\n" |
---|
2747 | 2986 | "r Run available scripts\n" |
---|
2748 | 2987 | "s Switch to another data file in PWD\n" |
---|
2749 | 2988 | "t Zoom into current Thread\n" |
---|
2750 | 2989 | "V Verbose (DSO names in callchains, etc)\n" |
---|
2751 | | - "/ Filter symbol by name"; |
---|
2752 | | - const char top_help[] = HIST_BROWSER_HELP_COMMON |
---|
| 2990 | + "/ Filter symbol by name\n" |
---|
| 2991 | + "0-9 Sort by event n in group"; |
---|
| 2992 | + static const char top_help[] = HIST_BROWSER_HELP_COMMON |
---|
2753 | 2993 | "P Print histograms to perf.hist.N\n" |
---|
2754 | 2994 | "t Zoom into current Thread\n" |
---|
2755 | 2995 | "V Verbose (DSO names in callchains, etc)\n" |
---|
.. | .. |
---|
2780 | 3020 | if (symbol_conf.col_width_list_str) |
---|
2781 | 3021 | perf_hpp__set_user_width(symbol_conf.col_width_list_str); |
---|
2782 | 3022 | |
---|
| 3023 | + if (!is_report_browser(hbt)) |
---|
| 3024 | + browser->b.no_samples_msg = "Collecting samples..."; |
---|
| 3025 | + |
---|
2783 | 3026 | while (1) { |
---|
2784 | 3027 | struct thread *thread = NULL; |
---|
2785 | 3028 | struct map *map = NULL; |
---|
2786 | | - int choice = 0; |
---|
| 3029 | + int choice; |
---|
2787 | 3030 | int socked_id = -1; |
---|
2788 | 3031 | |
---|
2789 | | - nr_options = 0; |
---|
2790 | | - |
---|
2791 | | - key = hist_browser__run(browser, helpline, |
---|
2792 | | - warn_lost_event); |
---|
| 3032 | + key = 0; // reset key |
---|
| 3033 | +do_hotkey: // key came straight from options ui__popup_menu() |
---|
| 3034 | + choice = nr_options = 0; |
---|
| 3035 | + key = hist_browser__run(browser, helpline, warn_lost_event, key); |
---|
2793 | 3036 | |
---|
2794 | 3037 | if (browser->he_selection != NULL) { |
---|
2795 | 3038 | thread = hist_browser__selected_thread(browser); |
---|
.. | .. |
---|
2806 | 3049 | * go to the next or previous |
---|
2807 | 3050 | */ |
---|
2808 | 3051 | goto out_free_stack; |
---|
| 3052 | + case '0' ... '9': |
---|
| 3053 | + if (!symbol_conf.event_group || |
---|
| 3054 | + evsel->core.nr_members < 2) { |
---|
| 3055 | + snprintf(buf, sizeof(buf), |
---|
| 3056 | + "Sort by index only available with group events!"); |
---|
| 3057 | + helpline = buf; |
---|
| 3058 | + continue; |
---|
| 3059 | + } |
---|
| 3060 | + |
---|
| 3061 | + if (key - '0' == symbol_conf.group_sort_idx) |
---|
| 3062 | + continue; |
---|
| 3063 | + |
---|
| 3064 | + symbol_conf.group_sort_idx = key - '0'; |
---|
| 3065 | + |
---|
| 3066 | + if (symbol_conf.group_sort_idx >= evsel->core.nr_members) { |
---|
| 3067 | + snprintf(buf, sizeof(buf), |
---|
| 3068 | + "Max event group index to sort is %d (index from 0 to %d)", |
---|
| 3069 | + evsel->core.nr_members - 1, |
---|
| 3070 | + evsel->core.nr_members - 1); |
---|
| 3071 | + helpline = buf; |
---|
| 3072 | + continue; |
---|
| 3073 | + } |
---|
| 3074 | + |
---|
| 3075 | + key = K_RELOAD; |
---|
| 3076 | + goto out_free_stack; |
---|
2809 | 3077 | case 'a': |
---|
2810 | 3078 | if (!hists__has(hists, sym)) { |
---|
2811 | 3079 | ui_browser__warning(&browser->b, delay_secs * 2, |
---|
.. | .. |
---|
2814 | 3082 | continue; |
---|
2815 | 3083 | } |
---|
2816 | 3084 | |
---|
2817 | | - if (browser->selection == NULL || |
---|
2818 | | - browser->selection->sym == NULL || |
---|
2819 | | - browser->selection->map->dso->annotate_warned) |
---|
| 3085 | + if (!browser->selection || |
---|
| 3086 | + !browser->selection->map || |
---|
| 3087 | + !browser->selection->map->dso || |
---|
| 3088 | + browser->selection->map->dso->annotate_warned) { |
---|
2820 | 3089 | continue; |
---|
| 3090 | + } |
---|
2821 | 3091 | |
---|
2822 | | - actions->ms.map = browser->selection->map; |
---|
2823 | | - actions->ms.sym = browser->selection->sym; |
---|
| 3092 | + if (!browser->selection->sym) { |
---|
| 3093 | + if (!browser->he_selection) |
---|
| 3094 | + continue; |
---|
| 3095 | + |
---|
| 3096 | + if (sort__mode == SORT_MODE__BRANCH) { |
---|
| 3097 | + bi = browser->he_selection->branch_info; |
---|
| 3098 | + if (!bi || !bi->to.ms.map) |
---|
| 3099 | + continue; |
---|
| 3100 | + |
---|
| 3101 | + actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map); |
---|
| 3102 | + actions->ms.map = bi->to.ms.map; |
---|
| 3103 | + } else { |
---|
| 3104 | + actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip, |
---|
| 3105 | + browser->selection->map); |
---|
| 3106 | + actions->ms.map = browser->selection->map; |
---|
| 3107 | + } |
---|
| 3108 | + |
---|
| 3109 | + if (!actions->ms.sym) |
---|
| 3110 | + continue; |
---|
| 3111 | + } else { |
---|
| 3112 | + if (symbol__annotation(browser->selection->sym)->src == NULL) { |
---|
| 3113 | + ui_browser__warning(&browser->b, delay_secs * 2, |
---|
| 3114 | + "No samples for the \"%s\" symbol.\n\n" |
---|
| 3115 | + "Probably appeared just in a callchain", |
---|
| 3116 | + browser->selection->sym->name); |
---|
| 3117 | + continue; |
---|
| 3118 | + } |
---|
| 3119 | + |
---|
| 3120 | + actions->ms.map = browser->selection->map; |
---|
| 3121 | + actions->ms.sym = browser->selection->sym; |
---|
| 3122 | + } |
---|
| 3123 | + |
---|
2824 | 3124 | do_annotate(browser, actions); |
---|
2825 | 3125 | continue; |
---|
2826 | 3126 | case 'P': |
---|
.. | .. |
---|
2829 | 3129 | case 'd': |
---|
2830 | 3130 | actions->ms.map = map; |
---|
2831 | 3131 | do_zoom_dso(browser, actions); |
---|
| 3132 | + continue; |
---|
| 3133 | + case 'k': |
---|
| 3134 | + if (browser->selection != NULL) |
---|
| 3135 | + hists_browser__zoom_map(browser, browser->selection->maps->machine->vmlinux_map); |
---|
2832 | 3136 | continue; |
---|
2833 | 3137 | case 'V': |
---|
2834 | 3138 | verbose = (verbose + 1) % 4; |
---|
.. | .. |
---|
2988 | 3292 | nr_options += add_annotate_opt(browser, |
---|
2989 | 3293 | &actions[nr_options], |
---|
2990 | 3294 | &options[nr_options], |
---|
2991 | | - bi->from.map, |
---|
2992 | | - bi->from.sym); |
---|
2993 | | - if (bi->to.sym != bi->from.sym) |
---|
| 3295 | + &bi->from.ms, |
---|
| 3296 | + bi->from.al_addr); |
---|
| 3297 | + if (bi->to.ms.sym != bi->from.ms.sym) |
---|
2994 | 3298 | nr_options += add_annotate_opt(browser, |
---|
2995 | 3299 | &actions[nr_options], |
---|
2996 | 3300 | &options[nr_options], |
---|
2997 | | - bi->to.map, |
---|
2998 | | - bi->to.sym); |
---|
| 3301 | + &bi->to.ms, |
---|
| 3302 | + bi->to.al_addr); |
---|
2999 | 3303 | } else { |
---|
3000 | 3304 | nr_options += add_annotate_opt(browser, |
---|
3001 | 3305 | &actions[nr_options], |
---|
3002 | 3306 | &options[nr_options], |
---|
3003 | | - browser->selection->map, |
---|
3004 | | - browser->selection->sym); |
---|
| 3307 | + browser->selection, |
---|
| 3308 | + browser->he_selection->ip); |
---|
3005 | 3309 | } |
---|
3006 | 3310 | skip_annotation: |
---|
3007 | 3311 | nr_options += add_thread_opt(browser, &actions[nr_options], |
---|
3008 | 3312 | &options[nr_options], thread); |
---|
3009 | 3313 | nr_options += add_dso_opt(browser, &actions[nr_options], |
---|
3010 | 3314 | &options[nr_options], map); |
---|
| 3315 | + nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]); |
---|
3011 | 3316 | nr_options += add_map_opt(browser, &actions[nr_options], |
---|
3012 | 3317 | &options[nr_options], |
---|
3013 | 3318 | browser->selection ? |
---|
.. | .. |
---|
3024 | 3329 | nr_options += add_script_opt(browser, |
---|
3025 | 3330 | &actions[nr_options], |
---|
3026 | 3331 | &options[nr_options], |
---|
3027 | | - thread, NULL); |
---|
| 3332 | + thread, NULL, evsel); |
---|
3028 | 3333 | } |
---|
3029 | 3334 | /* |
---|
3030 | 3335 | * Note that browser->selection != NULL |
---|
.. | .. |
---|
3039 | 3344 | nr_options += add_script_opt(browser, |
---|
3040 | 3345 | &actions[nr_options], |
---|
3041 | 3346 | &options[nr_options], |
---|
3042 | | - NULL, browser->selection->sym); |
---|
| 3347 | + NULL, browser->selection->sym, |
---|
| 3348 | + evsel); |
---|
3043 | 3349 | } |
---|
3044 | 3350 | } |
---|
3045 | 3351 | nr_options += add_script_opt(browser, &actions[nr_options], |
---|
3046 | | - &options[nr_options], NULL, NULL); |
---|
| 3352 | + &options[nr_options], NULL, NULL, evsel); |
---|
| 3353 | + nr_options += add_res_sample_opt(browser, &actions[nr_options], |
---|
| 3354 | + &options[nr_options], |
---|
| 3355 | + hist_browser__selected_res_sample(browser), |
---|
| 3356 | + evsel, A_NORMAL); |
---|
| 3357 | + nr_options += add_res_sample_opt(browser, &actions[nr_options], |
---|
| 3358 | + &options[nr_options], |
---|
| 3359 | + hist_browser__selected_res_sample(browser), |
---|
| 3360 | + evsel, A_ASM); |
---|
| 3361 | + nr_options += add_res_sample_opt(browser, &actions[nr_options], |
---|
| 3362 | + &options[nr_options], |
---|
| 3363 | + hist_browser__selected_res_sample(browser), |
---|
| 3364 | + evsel, A_SOURCE); |
---|
3047 | 3365 | nr_options += add_switch_opt(browser, &actions[nr_options], |
---|
3048 | 3366 | &options[nr_options]); |
---|
3049 | 3367 | skip_scripting: |
---|
.. | .. |
---|
3053 | 3371 | do { |
---|
3054 | 3372 | struct popup_action *act; |
---|
3055 | 3373 | |
---|
3056 | | - choice = ui__popup_menu(nr_options, options); |
---|
3057 | | - if (choice == -1 || choice >= nr_options) |
---|
| 3374 | + choice = ui__popup_menu(nr_options, options, &key); |
---|
| 3375 | + if (choice == -1) |
---|
3058 | 3376 | break; |
---|
| 3377 | + |
---|
| 3378 | + if (choice == nr_options) |
---|
| 3379 | + goto do_hotkey; |
---|
3059 | 3380 | |
---|
3060 | 3381 | act = &actions[choice]; |
---|
3061 | 3382 | key = act->fn(browser, act); |
---|
.. | .. |
---|
3072 | 3393 | return key; |
---|
3073 | 3394 | } |
---|
3074 | 3395 | |
---|
3075 | | -struct perf_evsel_menu { |
---|
| 3396 | +struct evsel_menu { |
---|
3076 | 3397 | struct ui_browser b; |
---|
3077 | | - struct perf_evsel *selection; |
---|
| 3398 | + struct evsel *selection; |
---|
3078 | 3399 | struct annotation_options *annotation_opts; |
---|
3079 | 3400 | bool lost_events, lost_events_warned; |
---|
3080 | 3401 | float min_pcnt; |
---|
.. | .. |
---|
3084 | 3405 | static void perf_evsel_menu__write(struct ui_browser *browser, |
---|
3085 | 3406 | void *entry, int row) |
---|
3086 | 3407 | { |
---|
3087 | | - struct perf_evsel_menu *menu = container_of(browser, |
---|
3088 | | - struct perf_evsel_menu, b); |
---|
3089 | | - struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); |
---|
| 3408 | + struct evsel_menu *menu = container_of(browser, |
---|
| 3409 | + struct evsel_menu, b); |
---|
| 3410 | + struct evsel *evsel = list_entry(entry, struct evsel, core.node); |
---|
3090 | 3411 | struct hists *hists = evsel__hists(evsel); |
---|
3091 | 3412 | bool current_entry = ui_browser__is_current_entry(browser, row); |
---|
3092 | 3413 | unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
---|
3093 | | - const char *ev_name = perf_evsel__name(evsel); |
---|
| 3414 | + const char *ev_name = evsel__name(evsel); |
---|
3094 | 3415 | char bf[256], unit; |
---|
3095 | 3416 | const char *warn = " "; |
---|
3096 | 3417 | size_t printed; |
---|
.. | .. |
---|
3098 | 3419 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : |
---|
3099 | 3420 | HE_COLORSET_NORMAL); |
---|
3100 | 3421 | |
---|
3101 | | - if (perf_evsel__is_group_event(evsel)) { |
---|
3102 | | - struct perf_evsel *pos; |
---|
| 3422 | + if (evsel__is_group_event(evsel)) { |
---|
| 3423 | + struct evsel *pos; |
---|
3103 | 3424 | |
---|
3104 | | - ev_name = perf_evsel__group_name(evsel); |
---|
| 3425 | + ev_name = evsel__group_name(evsel); |
---|
3105 | 3426 | |
---|
3106 | 3427 | for_each_group_member(pos, evsel) { |
---|
3107 | 3428 | struct hists *pos_hists = evsel__hists(pos); |
---|
.. | .. |
---|
3131 | 3452 | menu->selection = evsel; |
---|
3132 | 3453 | } |
---|
3133 | 3454 | |
---|
3134 | | -static int perf_evsel_menu__run(struct perf_evsel_menu *menu, |
---|
| 3455 | +static int perf_evsel_menu__run(struct evsel_menu *menu, |
---|
3135 | 3456 | int nr_events, const char *help, |
---|
3136 | 3457 | struct hist_browser_timer *hbt, |
---|
3137 | 3458 | bool warn_lost_event) |
---|
3138 | 3459 | { |
---|
3139 | | - struct perf_evlist *evlist = menu->b.priv; |
---|
3140 | | - struct perf_evsel *pos; |
---|
| 3460 | + struct evlist *evlist = menu->b.priv; |
---|
| 3461 | + struct evsel *pos; |
---|
3141 | 3462 | const char *title = "Available samples"; |
---|
3142 | 3463 | int delay_secs = hbt ? hbt->refresh : 0; |
---|
3143 | 3464 | int key; |
---|
.. | .. |
---|
3183 | 3504 | ui_browser__show_title(&menu->b, title); |
---|
3184 | 3505 | switch (key) { |
---|
3185 | 3506 | case K_TAB: |
---|
3186 | | - if (pos->node.next == &evlist->entries) |
---|
3187 | | - pos = perf_evlist__first(evlist); |
---|
| 3507 | + if (pos->core.node.next == &evlist->core.entries) |
---|
| 3508 | + pos = evlist__first(evlist); |
---|
3188 | 3509 | else |
---|
3189 | | - pos = perf_evsel__next(pos); |
---|
| 3510 | + pos = evsel__next(pos); |
---|
3190 | 3511 | goto browse_hists; |
---|
3191 | 3512 | case K_UNTAB: |
---|
3192 | | - if (pos->node.prev == &evlist->entries) |
---|
3193 | | - pos = perf_evlist__last(evlist); |
---|
| 3513 | + if (pos->core.node.prev == &evlist->core.entries) |
---|
| 3514 | + pos = evlist__last(evlist); |
---|
3194 | 3515 | else |
---|
3195 | | - pos = perf_evsel__prev(pos); |
---|
| 3516 | + pos = evsel__prev(pos); |
---|
3196 | 3517 | goto browse_hists; |
---|
3197 | 3518 | case K_SWITCH_INPUT_DATA: |
---|
| 3519 | + case K_RELOAD: |
---|
3198 | 3520 | case 'q': |
---|
3199 | 3521 | case CTRL('c'): |
---|
3200 | 3522 | goto out; |
---|
.. | .. |
---|
3225 | 3547 | static bool filter_group_entries(struct ui_browser *browser __maybe_unused, |
---|
3226 | 3548 | void *entry) |
---|
3227 | 3549 | { |
---|
3228 | | - struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); |
---|
| 3550 | + struct evsel *evsel = list_entry(entry, struct evsel, core.node); |
---|
3229 | 3551 | |
---|
3230 | | - if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel)) |
---|
| 3552 | + if (symbol_conf.event_group && !evsel__is_group_leader(evsel)) |
---|
3231 | 3553 | return true; |
---|
3232 | 3554 | |
---|
3233 | 3555 | return false; |
---|
3234 | 3556 | } |
---|
3235 | 3557 | |
---|
3236 | | -static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, |
---|
| 3558 | +static int __perf_evlist__tui_browse_hists(struct evlist *evlist, |
---|
3237 | 3559 | int nr_entries, const char *help, |
---|
3238 | 3560 | struct hist_browser_timer *hbt, |
---|
3239 | 3561 | float min_pcnt, |
---|
.. | .. |
---|
3241 | 3563 | bool warn_lost_event, |
---|
3242 | 3564 | struct annotation_options *annotation_opts) |
---|
3243 | 3565 | { |
---|
3244 | | - struct perf_evsel *pos; |
---|
3245 | | - struct perf_evsel_menu menu = { |
---|
| 3566 | + struct evsel *pos; |
---|
| 3567 | + struct evsel_menu menu = { |
---|
3246 | 3568 | .b = { |
---|
3247 | | - .entries = &evlist->entries, |
---|
| 3569 | + .entries = &evlist->core.entries, |
---|
3248 | 3570 | .refresh = ui_browser__list_head_refresh, |
---|
3249 | 3571 | .seek = ui_browser__list_head_seek, |
---|
3250 | 3572 | .write = perf_evsel_menu__write, |
---|
.. | .. |
---|
3260 | 3582 | ui_helpline__push("Press ESC to exit"); |
---|
3261 | 3583 | |
---|
3262 | 3584 | evlist__for_each_entry(evlist, pos) { |
---|
3263 | | - const char *ev_name = perf_evsel__name(pos); |
---|
| 3585 | + const char *ev_name = evsel__name(pos); |
---|
3264 | 3586 | size_t line_len = strlen(ev_name) + 7; |
---|
3265 | 3587 | |
---|
3266 | 3588 | if (menu.b.width < line_len) |
---|
.. | .. |
---|
3271 | 3593 | hbt, warn_lost_event); |
---|
3272 | 3594 | } |
---|
3273 | 3595 | |
---|
3274 | | -int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
---|
| 3596 | +static bool perf_evlist__single_entry(struct evlist *evlist) |
---|
| 3597 | +{ |
---|
| 3598 | + int nr_entries = evlist->core.nr_entries; |
---|
| 3599 | + |
---|
| 3600 | + if (nr_entries == 1) |
---|
| 3601 | + return true; |
---|
| 3602 | + |
---|
| 3603 | + if (nr_entries == 2) { |
---|
| 3604 | + struct evsel *last = evlist__last(evlist); |
---|
| 3605 | + |
---|
| 3606 | + if (evsel__is_dummy_event(last)) |
---|
| 3607 | + return true; |
---|
| 3608 | + } |
---|
| 3609 | + |
---|
| 3610 | + return false; |
---|
| 3611 | +} |
---|
| 3612 | + |
---|
| 3613 | +int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help, |
---|
3275 | 3614 | struct hist_browser_timer *hbt, |
---|
3276 | 3615 | float min_pcnt, |
---|
3277 | 3616 | struct perf_env *env, |
---|
3278 | 3617 | bool warn_lost_event, |
---|
3279 | 3618 | struct annotation_options *annotation_opts) |
---|
3280 | 3619 | { |
---|
3281 | | - int nr_entries = evlist->nr_entries; |
---|
| 3620 | + int nr_entries = evlist->core.nr_entries; |
---|
3282 | 3621 | |
---|
3283 | | -single_entry: |
---|
3284 | | - if (nr_entries == 1) { |
---|
3285 | | - struct perf_evsel *first = perf_evlist__first(evlist); |
---|
| 3622 | + if (perf_evlist__single_entry(evlist)) { |
---|
| 3623 | +single_entry: { |
---|
| 3624 | + struct evsel *first = evlist__first(evlist); |
---|
3286 | 3625 | |
---|
3287 | 3626 | return perf_evsel__hists_browse(first, nr_entries, help, |
---|
3288 | 3627 | false, hbt, min_pcnt, |
---|
3289 | 3628 | env, warn_lost_event, |
---|
3290 | 3629 | annotation_opts); |
---|
3291 | 3630 | } |
---|
| 3631 | + } |
---|
3292 | 3632 | |
---|
3293 | 3633 | if (symbol_conf.event_group) { |
---|
3294 | | - struct perf_evsel *pos; |
---|
| 3634 | + struct evsel *pos; |
---|
3295 | 3635 | |
---|
3296 | 3636 | nr_entries = 0; |
---|
3297 | 3637 | evlist__for_each_entry(evlist, pos) { |
---|
3298 | | - if (perf_evsel__is_group_leader(pos)) |
---|
| 3638 | + if (evsel__is_group_leader(pos)) |
---|
3299 | 3639 | nr_entries++; |
---|
3300 | 3640 | } |
---|
3301 | 3641 | |
---|
.. | .. |
---|
3308 | 3648 | warn_lost_event, |
---|
3309 | 3649 | annotation_opts); |
---|
3310 | 3650 | } |
---|
| 3651 | + |
---|
| 3652 | +static int block_hists_browser__title(struct hist_browser *browser, char *bf, |
---|
| 3653 | + size_t size) |
---|
| 3654 | +{ |
---|
| 3655 | + struct hists *hists = evsel__hists(browser->block_evsel); |
---|
| 3656 | + const char *evname = evsel__name(browser->block_evsel); |
---|
| 3657 | + unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
---|
| 3658 | + int ret; |
---|
| 3659 | + |
---|
| 3660 | + ret = scnprintf(bf, size, "# Samples: %lu", nr_samples); |
---|
| 3661 | + if (evname) |
---|
| 3662 | + scnprintf(bf + ret, size - ret, " of event '%s'", evname); |
---|
| 3663 | + |
---|
| 3664 | + return 0; |
---|
| 3665 | +} |
---|
| 3666 | + |
---|
| 3667 | +int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel, |
---|
| 3668 | + float min_percent, struct perf_env *env, |
---|
| 3669 | + struct annotation_options *annotation_opts) |
---|
| 3670 | +{ |
---|
| 3671 | + struct hists *hists = &bh->block_hists; |
---|
| 3672 | + struct hist_browser *browser; |
---|
| 3673 | + int key = -1; |
---|
| 3674 | + struct popup_action action; |
---|
| 3675 | + static const char help[] = |
---|
| 3676 | + " q Quit \n"; |
---|
| 3677 | + |
---|
| 3678 | + browser = hist_browser__new(hists); |
---|
| 3679 | + if (!browser) |
---|
| 3680 | + return -1; |
---|
| 3681 | + |
---|
| 3682 | + browser->block_evsel = evsel; |
---|
| 3683 | + browser->title = block_hists_browser__title; |
---|
| 3684 | + browser->min_pcnt = min_percent; |
---|
| 3685 | + browser->env = env; |
---|
| 3686 | + browser->annotation_opts = annotation_opts; |
---|
| 3687 | + |
---|
| 3688 | + /* reset abort key so that it can get Ctrl-C as a key */ |
---|
| 3689 | + SLang_reset_tty(); |
---|
| 3690 | + SLang_init_tty(0, 0, 0); |
---|
| 3691 | + |
---|
| 3692 | + memset(&action, 0, sizeof(action)); |
---|
| 3693 | + |
---|
| 3694 | + while (1) { |
---|
| 3695 | + key = hist_browser__run(browser, "? - help", true, 0); |
---|
| 3696 | + |
---|
| 3697 | + switch (key) { |
---|
| 3698 | + case 'q': |
---|
| 3699 | + goto out; |
---|
| 3700 | + case '?': |
---|
| 3701 | + ui_browser__help_window(&browser->b, help); |
---|
| 3702 | + break; |
---|
| 3703 | + case 'a': |
---|
| 3704 | + case K_ENTER: |
---|
| 3705 | + if (!browser->selection || |
---|
| 3706 | + !browser->selection->sym) { |
---|
| 3707 | + continue; |
---|
| 3708 | + } |
---|
| 3709 | + |
---|
| 3710 | + action.ms.map = browser->selection->map; |
---|
| 3711 | + action.ms.sym = browser->selection->sym; |
---|
| 3712 | + do_annotate(browser, &action); |
---|
| 3713 | + continue; |
---|
| 3714 | + default: |
---|
| 3715 | + break; |
---|
| 3716 | + } |
---|
| 3717 | + } |
---|
| 3718 | + |
---|
| 3719 | +out: |
---|
| 3720 | + hist_browser__delete(browser); |
---|
| 3721 | + return 0; |
---|
| 3722 | +} |
---|