aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/ui/browsers/hists.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/ui/browsers/hists.c')
-rw-r--r--tools/perf/ui/browsers/hists.c341
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) \ 570struct hpp_arg {
571static 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
576static 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
585static 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 }
661out:
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) \
669static u64 __hpp_get_##_field(struct hist_entry *he) \
670{ \
671 return he->stat._field; \
672} \
673 \
674static 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
580HPP__COLOR_FN(overhead, period) 680__HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain)
581HPP__COLOR_FN(overhead_sys, period_sys) 681__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL)
582HPP__COLOR_FN(overhead_us, period_us) 682__HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL)
583HPP__COLOR_FN(overhead_guest_sys, period_guest_sys) 683__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL)
584HPP__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
588void hist_browser__init_hpp(void) 688void 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 */
1249static bool is_input_name_malloced = false;
1250
1251static 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
1303close_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
1138static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, 1334static 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++;
1355add_exit_option: 1560add_exit_option:
1356 options[nr_options++] = (char *)"Exit"; 1561 options[nr_options++] = (char *)"Exit";
1357retry_popup_menu: 1562retry_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) {
1672do_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 }
1466out_free_stack: 1681out_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
1833static 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
1607static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, 1844static 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
1638int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, 1876int 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
1882single_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}