diff options
Diffstat (limited to 'tools/perf/ui/browsers/hists.c')
-rw-r--r-- | tools/perf/ui/browsers/hists.c | 341 |
1 files changed, 298 insertions, 43 deletions
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index ccc4bd161420..aa22704047d6 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
@@ -567,26 +567,128 @@ static int hist_browser__show_callchain(struct hist_browser *browser, | |||
567 | return row - first_row; | 567 | return row - first_row; |
568 | } | 568 | } |
569 | 569 | ||
570 | #define HPP__COLOR_FN(_name, _field) \ | 570 | struct hpp_arg { |
571 | static int hist_browser__hpp_color_ ## _name(struct perf_hpp *hpp, \ | 571 | struct ui_browser *b; |
572 | struct hist_entry *he) \ | 572 | char folded_sign; |
573 | bool current_entry; | ||
574 | }; | ||
575 | |||
576 | static int __hpp__color_callchain(struct hpp_arg *arg) | ||
577 | { | ||
578 | if (!symbol_conf.use_callchain) | ||
579 | return 0; | ||
580 | |||
581 | slsmg_printf("%c ", arg->folded_sign); | ||
582 | return 2; | ||
583 | } | ||
584 | |||
585 | static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he, | ||
586 | u64 (*get_field)(struct hist_entry *), | ||
587 | int (*callchain_cb)(struct hpp_arg *)) | ||
588 | { | ||
589 | int ret = 0; | ||
590 | double percent = 0.0; | ||
591 | struct hists *hists = he->hists; | ||
592 | struct hpp_arg *arg = hpp->ptr; | ||
593 | |||
594 | if (hists->stats.total_period) | ||
595 | percent = 100.0 * get_field(he) / hists->stats.total_period; | ||
596 | |||
597 | ui_browser__set_percent_color(arg->b, percent, arg->current_entry); | ||
598 | |||
599 | if (callchain_cb) | ||
600 | ret += callchain_cb(arg); | ||
601 | |||
602 | ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); | ||
603 | slsmg_printf("%s", hpp->buf); | ||
604 | |||
605 | if (symbol_conf.event_group) { | ||
606 | int prev_idx, idx_delta; | ||
607 | struct perf_evsel *evsel = hists_to_evsel(hists); | ||
608 | struct hist_entry *pair; | ||
609 | int nr_members = evsel->nr_members; | ||
610 | |||
611 | if (nr_members <= 1) | ||
612 | goto out; | ||
613 | |||
614 | prev_idx = perf_evsel__group_idx(evsel); | ||
615 | |||
616 | list_for_each_entry(pair, &he->pairs.head, pairs.node) { | ||
617 | u64 period = get_field(pair); | ||
618 | u64 total = pair->hists->stats.total_period; | ||
619 | |||
620 | if (!total) | ||
621 | continue; | ||
622 | |||
623 | evsel = hists_to_evsel(pair->hists); | ||
624 | idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1; | ||
625 | |||
626 | while (idx_delta--) { | ||
627 | /* | ||
628 | * zero-fill group members in the middle which | ||
629 | * have no sample | ||
630 | */ | ||
631 | ui_browser__set_percent_color(arg->b, 0.0, | ||
632 | arg->current_entry); | ||
633 | ret += scnprintf(hpp->buf, hpp->size, | ||
634 | " %6.2f%%", 0.0); | ||
635 | slsmg_printf("%s", hpp->buf); | ||
636 | } | ||
637 | |||
638 | percent = 100.0 * period / total; | ||
639 | ui_browser__set_percent_color(arg->b, percent, | ||
640 | arg->current_entry); | ||
641 | ret += scnprintf(hpp->buf, hpp->size, | ||
642 | " %6.2f%%", percent); | ||
643 | slsmg_printf("%s", hpp->buf); | ||
644 | |||
645 | prev_idx = perf_evsel__group_idx(evsel); | ||
646 | } | ||
647 | |||
648 | idx_delta = nr_members - prev_idx - 1; | ||
649 | |||
650 | while (idx_delta--) { | ||
651 | /* | ||
652 | * zero-fill group members at last which have no sample | ||
653 | */ | ||
654 | ui_browser__set_percent_color(arg->b, 0.0, | ||
655 | arg->current_entry); | ||
656 | ret += scnprintf(hpp->buf, hpp->size, | ||
657 | " %6.2f%%", 0.0); | ||
658 | slsmg_printf("%s", hpp->buf); | ||
659 | } | ||
660 | } | ||
661 | out: | ||
662 | if (!arg->current_entry || !arg->b->navkeypressed) | ||
663 | ui_browser__set_color(arg->b, HE_COLORSET_NORMAL); | ||
664 | |||
665 | return ret; | ||
666 | } | ||
667 | |||
668 | #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \ | ||
669 | static u64 __hpp_get_##_field(struct hist_entry *he) \ | ||
670 | { \ | ||
671 | return he->stat._field; \ | ||
672 | } \ | ||
673 | \ | ||
674 | static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp, \ | ||
675 | struct hist_entry *he) \ | ||
573 | { \ | 676 | { \ |
574 | struct hists *hists = he->hists; \ | 677 | return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb); \ |
575 | double percent = 100.0 * he->stat._field / hists->stats.total_period; \ | ||
576 | *(double *)hpp->ptr = percent; \ | ||
577 | return scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); \ | ||
578 | } | 678 | } |
579 | 679 | ||
580 | HPP__COLOR_FN(overhead, period) | 680 | __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain) |
581 | HPP__COLOR_FN(overhead_sys, period_sys) | 681 | __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL) |
582 | HPP__COLOR_FN(overhead_us, period_us) | 682 | __HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL) |
583 | HPP__COLOR_FN(overhead_guest_sys, period_guest_sys) | 683 | __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL) |
584 | HPP__COLOR_FN(overhead_guest_us, period_guest_us) | 684 | __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL) |
585 | 685 | ||
586 | #undef HPP__COLOR_FN | 686 | #undef __HPP_COLOR_PERCENT_FN |
587 | 687 | ||
588 | void hist_browser__init_hpp(void) | 688 | void hist_browser__init_hpp(void) |
589 | { | 689 | { |
690 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | ||
691 | |||
590 | perf_hpp__init(); | 692 | perf_hpp__init(); |
591 | 693 | ||
592 | perf_hpp__format[PERF_HPP__OVERHEAD].color = | 694 | perf_hpp__format[PERF_HPP__OVERHEAD].color = |
@@ -606,13 +708,13 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
606 | unsigned short row) | 708 | unsigned short row) |
607 | { | 709 | { |
608 | char s[256]; | 710 | char s[256]; |
609 | double percent; | 711 | int printed = 0; |
610 | int i, printed = 0; | ||
611 | int width = browser->b.width; | 712 | int width = browser->b.width; |
612 | char folded_sign = ' '; | 713 | char folded_sign = ' '; |
613 | bool current_entry = ui_browser__is_current_entry(&browser->b, row); | 714 | bool current_entry = ui_browser__is_current_entry(&browser->b, row); |
614 | off_t row_offset = entry->row_offset; | 715 | off_t row_offset = entry->row_offset; |
615 | bool first = true; | 716 | bool first = true; |
717 | struct perf_hpp_fmt *fmt; | ||
616 | 718 | ||
617 | if (current_entry) { | 719 | if (current_entry) { |
618 | browser->he_selection = entry; | 720 | browser->he_selection = entry; |
@@ -625,41 +727,30 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
625 | } | 727 | } |
626 | 728 | ||
627 | if (row_offset == 0) { | 729 | if (row_offset == 0) { |
730 | struct hpp_arg arg = { | ||
731 | .b = &browser->b, | ||
732 | .folded_sign = folded_sign, | ||
733 | .current_entry = current_entry, | ||
734 | }; | ||
628 | struct perf_hpp hpp = { | 735 | struct perf_hpp hpp = { |
629 | .buf = s, | 736 | .buf = s, |
630 | .size = sizeof(s), | 737 | .size = sizeof(s), |
738 | .ptr = &arg, | ||
631 | }; | 739 | }; |
632 | 740 | ||
633 | ui_browser__gotorc(&browser->b, row, 0); | 741 | ui_browser__gotorc(&browser->b, row, 0); |
634 | 742 | ||
635 | for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { | 743 | perf_hpp__for_each_format(fmt) { |
636 | if (!perf_hpp__format[i].cond) | ||
637 | continue; | ||
638 | |||
639 | if (!first) { | 744 | if (!first) { |
640 | slsmg_printf(" "); | 745 | slsmg_printf(" "); |
641 | width -= 2; | 746 | width -= 2; |
642 | } | 747 | } |
643 | first = false; | 748 | first = false; |
644 | 749 | ||
645 | if (perf_hpp__format[i].color) { | 750 | if (fmt->color) { |
646 | hpp.ptr = &percent; | 751 | width -= fmt->color(&hpp, entry); |
647 | /* It will set percent for us. See HPP__COLOR_FN above. */ | ||
648 | width -= perf_hpp__format[i].color(&hpp, entry); | ||
649 | |||
650 | ui_browser__set_percent_color(&browser->b, percent, current_entry); | ||
651 | |||
652 | if (i == PERF_HPP__OVERHEAD && symbol_conf.use_callchain) { | ||
653 | slsmg_printf("%c ", folded_sign); | ||
654 | width -= 2; | ||
655 | } | ||
656 | |||
657 | slsmg_printf("%s", s); | ||
658 | |||
659 | if (!current_entry || !browser->b.navkeypressed) | ||
660 | ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL); | ||
661 | } else { | 752 | } else { |
662 | width -= perf_hpp__format[i].entry(&hpp, entry); | 753 | width -= fmt->entry(&hpp, entry); |
663 | slsmg_printf("%s", s); | 754 | slsmg_printf("%s", s); |
664 | } | 755 | } |
665 | } | 756 | } |
@@ -1098,6 +1189,21 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size, | |||
1098 | const struct thread *thread = hists->thread_filter; | 1189 | const struct thread *thread = hists->thread_filter; |
1099 | unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; | 1190 | unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
1100 | u64 nr_events = hists->stats.total_period; | 1191 | u64 nr_events = hists->stats.total_period; |
1192 | struct perf_evsel *evsel = hists_to_evsel(hists); | ||
1193 | char buf[512]; | ||
1194 | size_t buflen = sizeof(buf); | ||
1195 | |||
1196 | if (symbol_conf.event_group && evsel->nr_members > 1) { | ||
1197 | struct perf_evsel *pos; | ||
1198 | |||
1199 | perf_evsel__group_desc(evsel, buf, buflen); | ||
1200 | ev_name = buf; | ||
1201 | |||
1202 | for_each_group_member(pos, evsel) { | ||
1203 | nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | ||
1204 | nr_events += pos->hists.stats.total_period; | ||
1205 | } | ||
1206 | } | ||
1101 | 1207 | ||
1102 | nr_samples = convert_unit(nr_samples, &unit); | 1208 | nr_samples = convert_unit(nr_samples, &unit); |
1103 | printed = scnprintf(bf, size, | 1209 | printed = scnprintf(bf, size, |
@@ -1135,6 +1241,96 @@ static inline bool is_report_browser(void *timer) | |||
1135 | return timer == NULL; | 1241 | return timer == NULL; |
1136 | } | 1242 | } |
1137 | 1243 | ||
1244 | /* | ||
1245 | * Only runtime switching of perf data file will make "input_name" point | ||
1246 | * to a malloced buffer. So add "is_input_name_malloced" flag to decide | ||
1247 | * whether we need to call free() for current "input_name" during the switch. | ||
1248 | */ | ||
1249 | static bool is_input_name_malloced = false; | ||
1250 | |||
1251 | static int switch_data_file(void) | ||
1252 | { | ||
1253 | char *pwd, *options[32], *abs_path[32], *tmp; | ||
1254 | DIR *pwd_dir; | ||
1255 | int nr_options = 0, choice = -1, ret = -1; | ||
1256 | struct dirent *dent; | ||
1257 | |||
1258 | pwd = getenv("PWD"); | ||
1259 | if (!pwd) | ||
1260 | return ret; | ||
1261 | |||
1262 | pwd_dir = opendir(pwd); | ||
1263 | if (!pwd_dir) | ||
1264 | return ret; | ||
1265 | |||
1266 | memset(options, 0, sizeof(options)); | ||
1267 | memset(options, 0, sizeof(abs_path)); | ||
1268 | |||
1269 | while ((dent = readdir(pwd_dir))) { | ||
1270 | char path[PATH_MAX]; | ||
1271 | u64 magic; | ||
1272 | char *name = dent->d_name; | ||
1273 | FILE *file; | ||
1274 | |||
1275 | if (!(dent->d_type == DT_REG)) | ||
1276 | continue; | ||
1277 | |||
1278 | snprintf(path, sizeof(path), "%s/%s", pwd, name); | ||
1279 | |||
1280 | file = fopen(path, "r"); | ||
1281 | if (!file) | ||
1282 | continue; | ||
1283 | |||
1284 | if (fread(&magic, 1, 8, file) < 8) | ||
1285 | goto close_file_and_continue; | ||
1286 | |||
1287 | if (is_perf_magic(magic)) { | ||
1288 | options[nr_options] = strdup(name); | ||
1289 | if (!options[nr_options]) | ||
1290 | goto close_file_and_continue; | ||
1291 | |||
1292 | abs_path[nr_options] = strdup(path); | ||
1293 | if (!abs_path[nr_options]) { | ||
1294 | free(options[nr_options]); | ||
1295 | ui__warning("Can't search all data files due to memory shortage.\n"); | ||
1296 | fclose(file); | ||
1297 | break; | ||
1298 | } | ||
1299 | |||
1300 | nr_options++; | ||
1301 | } | ||
1302 | |||
1303 | close_file_and_continue: | ||
1304 | fclose(file); | ||
1305 | if (nr_options >= 32) { | ||
1306 | ui__warning("Too many perf data files in PWD!\n" | ||
1307 | "Only the first 32 files will be listed.\n"); | ||
1308 | break; | ||
1309 | } | ||
1310 | } | ||
1311 | closedir(pwd_dir); | ||
1312 | |||
1313 | if (nr_options) { | ||
1314 | choice = ui__popup_menu(nr_options, options); | ||
1315 | if (choice < nr_options && choice >= 0) { | ||
1316 | tmp = strdup(abs_path[choice]); | ||
1317 | if (tmp) { | ||
1318 | if (is_input_name_malloced) | ||
1319 | free((void *)input_name); | ||
1320 | input_name = tmp; | ||
1321 | is_input_name_malloced = true; | ||
1322 | ret = 0; | ||
1323 | } else | ||
1324 | ui__warning("Data switch failed due to memory shortage!\n"); | ||
1325 | } | ||
1326 | } | ||
1327 | |||
1328 | free_popup_options(options, nr_options); | ||
1329 | free_popup_options(abs_path, nr_options); | ||
1330 | return ret; | ||
1331 | } | ||
1332 | |||
1333 | |||
1138 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | 1334 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, |
1139 | const char *helpline, const char *ev_name, | 1335 | const char *helpline, const char *ev_name, |
1140 | bool left_exits, | 1336 | bool left_exits, |
@@ -1169,7 +1365,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1169 | int choice = 0, | 1365 | int choice = 0, |
1170 | annotate = -2, zoom_dso = -2, zoom_thread = -2, | 1366 | annotate = -2, zoom_dso = -2, zoom_thread = -2, |
1171 | annotate_f = -2, annotate_t = -2, browse_map = -2; | 1367 | annotate_f = -2, annotate_t = -2, browse_map = -2; |
1172 | int scripts_comm = -2, scripts_symbol = -2, scripts_all = -2; | 1368 | int scripts_comm = -2, scripts_symbol = -2, |
1369 | scripts_all = -2, switch_data = -2; | ||
1173 | 1370 | ||
1174 | nr_options = 0; | 1371 | nr_options = 0; |
1175 | 1372 | ||
@@ -1226,6 +1423,10 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1226 | if (is_report_browser(hbt)) | 1423 | if (is_report_browser(hbt)) |
1227 | goto do_scripts; | 1424 | goto do_scripts; |
1228 | continue; | 1425 | continue; |
1426 | case 's': | ||
1427 | if (is_report_browser(hbt)) | ||
1428 | goto do_data_switch; | ||
1429 | continue; | ||
1229 | case K_F1: | 1430 | case K_F1: |
1230 | case 'h': | 1431 | case 'h': |
1231 | case '?': | 1432 | case '?': |
@@ -1245,6 +1446,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1245 | "d Zoom into current DSO\n" | 1446 | "d Zoom into current DSO\n" |
1246 | "t Zoom into current Thread\n" | 1447 | "t Zoom into current Thread\n" |
1247 | "r Run available scripts('perf report' only)\n" | 1448 | "r Run available scripts('perf report' only)\n" |
1449 | "s Switch to another data file in PWD ('perf report' only)\n" | ||
1248 | "P Print histograms to perf.hist.N\n" | 1450 | "P Print histograms to perf.hist.N\n" |
1249 | "V Verbose (DSO names in callchains, etc)\n" | 1451 | "V Verbose (DSO names in callchains, etc)\n" |
1250 | "/ Filter symbol by name"); | 1452 | "/ Filter symbol by name"); |
@@ -1352,6 +1554,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1352 | if (asprintf(&options[nr_options], "Run scripts for all samples") > 0) | 1554 | if (asprintf(&options[nr_options], "Run scripts for all samples") > 0) |
1353 | scripts_all = nr_options++; | 1555 | scripts_all = nr_options++; |
1354 | 1556 | ||
1557 | if (is_report_browser(hbt) && asprintf(&options[nr_options], | ||
1558 | "Switch to another data file in PWD") > 0) | ||
1559 | switch_data = nr_options++; | ||
1355 | add_exit_option: | 1560 | add_exit_option: |
1356 | options[nr_options++] = (char *)"Exit"; | 1561 | options[nr_options++] = (char *)"Exit"; |
1357 | retry_popup_menu: | 1562 | retry_popup_menu: |
@@ -1462,6 +1667,16 @@ do_scripts: | |||
1462 | 1667 | ||
1463 | script_browse(script_opt); | 1668 | script_browse(script_opt); |
1464 | } | 1669 | } |
1670 | /* Switch to another data file */ | ||
1671 | else if (choice == switch_data) { | ||
1672 | do_data_switch: | ||
1673 | if (!switch_data_file()) { | ||
1674 | key = K_SWITCH_INPUT_DATA; | ||
1675 | break; | ||
1676 | } else | ||
1677 | ui__warning("Won't switch the data files due to\n" | ||
1678 | "no valid data file get selected!\n"); | ||
1679 | } | ||
1465 | } | 1680 | } |
1466 | out_free_stack: | 1681 | out_free_stack: |
1467 | pstack__delete(fstack); | 1682 | pstack__delete(fstack); |
@@ -1494,6 +1709,16 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
1494 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : | 1709 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : |
1495 | HE_COLORSET_NORMAL); | 1710 | HE_COLORSET_NORMAL); |
1496 | 1711 | ||
1712 | if (symbol_conf.event_group && evsel->nr_members > 1) { | ||
1713 | struct perf_evsel *pos; | ||
1714 | |||
1715 | ev_name = perf_evsel__group_name(evsel); | ||
1716 | |||
1717 | for_each_group_member(pos, evsel) { | ||
1718 | nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | ||
1719 | } | ||
1720 | } | ||
1721 | |||
1497 | nr_events = convert_unit(nr_events, &unit); | 1722 | nr_events = convert_unit(nr_events, &unit); |
1498 | printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, | 1723 | printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, |
1499 | unit, unit == ' ' ? "" : " ", ev_name); | 1724 | unit, unit == ' ' ? "" : " ", ev_name); |
@@ -1578,6 +1803,7 @@ browse_hists: | |||
1578 | "Do you really want to exit?")) | 1803 | "Do you really want to exit?")) |
1579 | continue; | 1804 | continue; |
1580 | /* Fall thru */ | 1805 | /* Fall thru */ |
1806 | case K_SWITCH_INPUT_DATA: | ||
1581 | case 'q': | 1807 | case 'q': |
1582 | case CTRL('c'): | 1808 | case CTRL('c'): |
1583 | goto out; | 1809 | goto out; |
@@ -1604,8 +1830,19 @@ out: | |||
1604 | return key; | 1830 | return key; |
1605 | } | 1831 | } |
1606 | 1832 | ||
1833 | static bool filter_group_entries(struct ui_browser *self __maybe_unused, | ||
1834 | void *entry) | ||
1835 | { | ||
1836 | struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); | ||
1837 | |||
1838 | if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel)) | ||
1839 | return true; | ||
1840 | |||
1841 | return false; | ||
1842 | } | ||
1843 | |||
1607 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | 1844 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, |
1608 | const char *help, | 1845 | int nr_entries, const char *help, |
1609 | struct hist_browser_timer *hbt, | 1846 | struct hist_browser_timer *hbt, |
1610 | struct perf_session_env *env) | 1847 | struct perf_session_env *env) |
1611 | { | 1848 | { |
@@ -1616,7 +1853,8 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | |||
1616 | .refresh = ui_browser__list_head_refresh, | 1853 | .refresh = ui_browser__list_head_refresh, |
1617 | .seek = ui_browser__list_head_seek, | 1854 | .seek = ui_browser__list_head_seek, |
1618 | .write = perf_evsel_menu__write, | 1855 | .write = perf_evsel_menu__write, |
1619 | .nr_entries = evlist->nr_entries, | 1856 | .filter = filter_group_entries, |
1857 | .nr_entries = nr_entries, | ||
1620 | .priv = evlist, | 1858 | .priv = evlist, |
1621 | }, | 1859 | }, |
1622 | .env = env, | 1860 | .env = env, |
@@ -1632,20 +1870,37 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | |||
1632 | menu.b.width = line_len; | 1870 | menu.b.width = line_len; |
1633 | } | 1871 | } |
1634 | 1872 | ||
1635 | return perf_evsel_menu__run(&menu, evlist->nr_entries, help, hbt); | 1873 | return perf_evsel_menu__run(&menu, nr_entries, help, hbt); |
1636 | } | 1874 | } |
1637 | 1875 | ||
1638 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | 1876 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
1639 | struct hist_browser_timer *hbt, | 1877 | struct hist_browser_timer *hbt, |
1640 | struct perf_session_env *env) | 1878 | struct perf_session_env *env) |
1641 | { | 1879 | { |
1642 | if (evlist->nr_entries == 1) { | 1880 | int nr_entries = evlist->nr_entries; |
1881 | |||
1882 | single_entry: | ||
1883 | if (nr_entries == 1) { | ||
1643 | struct perf_evsel *first = list_entry(evlist->entries.next, | 1884 | struct perf_evsel *first = list_entry(evlist->entries.next, |
1644 | struct perf_evsel, node); | 1885 | struct perf_evsel, node); |
1645 | const char *ev_name = perf_evsel__name(first); | 1886 | const char *ev_name = perf_evsel__name(first); |
1646 | return perf_evsel__hists_browse(first, evlist->nr_entries, help, | 1887 | |
1888 | return perf_evsel__hists_browse(first, nr_entries, help, | ||
1647 | ev_name, false, hbt, env); | 1889 | ev_name, false, hbt, env); |
1648 | } | 1890 | } |
1649 | 1891 | ||
1650 | return __perf_evlist__tui_browse_hists(evlist, help, hbt, env); | 1892 | if (symbol_conf.event_group) { |
1893 | struct perf_evsel *pos; | ||
1894 | |||
1895 | nr_entries = 0; | ||
1896 | list_for_each_entry(pos, &evlist->entries, node) | ||
1897 | if (perf_evsel__is_group_leader(pos)) | ||
1898 | nr_entries++; | ||
1899 | |||
1900 | if (nr_entries == 1) | ||
1901 | goto single_entry; | ||
1902 | } | ||
1903 | |||
1904 | return __perf_evlist__tui_browse_hists(evlist, nr_entries, help, | ||
1905 | hbt, env); | ||
1651 | } | 1906 | } |