2 #include "../libslang.h"
6 #include <linux/rbtree.h>
8 #include "../../util/evsel.h"
9 #include "../../util/evlist.h"
10 #include "../../util/hist.h"
11 #include "../../util/pstack.h"
12 #include "../../util/sort.h"
13 #include "../../util/util.h"
15 #include "../browser.h"
16 #include "../helpline.h"
36 static void hist_browser__refresh_dimensions(
struct hist_browser *browser)
43 static void hist_browser__reset(
struct hist_browser *browser)
45 browser->
b.nr_entries = browser->
hists->nr_entries;
46 hist_browser__refresh_dimensions(browser);
50 static char tree__folded_sign(
bool unfolded)
52 return unfolded ?
'-' :
'+';
55 static char map_symbol__folded(
const struct map_symbol *
ms)
60 static char hist_entry__folded(
const struct hist_entry *he)
62 return map_symbol__folded(&he->
ms);
67 return map_symbol__folded(&cl->
ms);
70 static void map_symbol__set_folding(
struct map_symbol *ms,
bool unfold)
83 char folded_sign =
' ';
88 folded_sign = callchain_list__folded(chain);
89 if (folded_sign ==
'+')
93 if (folded_sign ==
'-')
94 n += callchain_node__count_rows_rb_tree(child);
100 static int callchain_node__count_rows(
struct callchain_node *node)
103 bool unfolded =
false;
108 unfolded = chain->
ms.unfolded;
112 n += callchain_node__count_rows_rb_tree(node);
117 static int callchain__count_rows(
struct rb_root *chain)
124 n += callchain_node__count_rows(node);
130 static bool map_symbol__toggle_fold(
struct map_symbol *ms)
142 static void callchain_node__init_have_children_rb_tree(
struct callchain_node *node)
154 chain->
ms.has_children = chain->
list.next != &child->
val ||
157 chain->
ms.has_children = chain->
list.next == &child->
val &&
161 callchain_node__init_have_children_rb_tree(child);
165 static void callchain_node__init_have_children(
struct callchain_node *node)
172 callchain_node__init_have_children_rb_tree(node);
175 static
void callchain__init_have_children(
struct rb_root *root)
181 callchain_node__init_have_children(node);
185 static void hist_entry__init_have_children(
struct hist_entry *he)
194 static bool hist_browser__toggle_fold(
struct hist_browser *browser)
196 if (map_symbol__toggle_fold(browser->
selection)) {
199 hist_entry__init_have_children(he);
207 browser->
b.nr_entries = browser->
hists->nr_entries;
216 static int callchain_node__set_folding_rb_tree(
struct callchain_node *node,
bool unfold)
224 bool has_children =
false;
228 map_symbol__set_folding(&chain->
ms, unfold);
229 has_children = chain->
ms.has_children;
233 n += callchain_node__set_folding_rb_tree(child, unfold);
239 static int callchain_node__set_folding(
struct callchain_node *node,
bool unfold)
242 bool has_children =
false;
247 map_symbol__set_folding(&chain->
ms, unfold);
248 has_children = chain->
ms.has_children;
252 n += callchain_node__set_folding_rb_tree(node, unfold);
257 static int callchain__set_folding(
struct rb_root *chain,
bool unfold)
264 n += callchain_node__set_folding(node, unfold);
270 static void hist_entry__set_folding(
struct hist_entry *he,
bool unfold)
272 hist_entry__init_have_children(he);
273 map_symbol__set_folding(&he->
ms, unfold);
275 if (he->
ms.has_children) {
276 int n = callchain__set_folding(&he->
sorted_chain, unfold);
282 static void hists__set_folding(
struct hists *
hists,
bool unfold)
290 hist_entry__set_folding(he, unfold);
295 static void hist_browser__set_folding(
struct hist_browser *browser,
bool unfold)
297 hists__set_folding(browser->
hists, unfold);
298 browser->
b.nr_entries = browser->
hists->nr_entries;
303 static void ui_browser__warn_lost_events(
struct ui_browser *browser)
306 "Events are being lost, check IO/CPU overload!\n\n"
307 "You may want to run 'perf' using a RT scheduler policy:\n\n"
308 " perf top -r 80\n\n"
309 "Or reduce the sampling frequency.");
312 static int hist_browser__run(
struct hist_browser *browser,
const char *ev_name,
313 void(*
timer)(
void *
arg),
void *arg,
int delay_secs)
318 browser->
b.entries = &browser->
hists->entries;
319 browser->
b.nr_entries = browser->
hists->nr_entries;
321 hist_browser__refresh_dimensions(browser);
322 hists__browser_title(browser->
hists, title,
sizeof(title), ev_name);
325 "Press '?' for help on key bindings") < 0)
336 if (browser->
hists->stats.nr_lost_warned !=
338 browser->
hists->stats.nr_lost_warned =
340 ui_browser__warn_lost_events(&browser->
b);
343 hists__browser_title(browser->
hists, title,
sizeof(title), ev_name);
351 ui_helpline__fpush(
"%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
352 seq++, browser->
b.nr_entries,
353 browser->
hists->nr_entries,
362 hist_browser__set_folding(browser,
false);
366 hist_browser__set_folding(browser,
true);
369 if (hist_browser__toggle_fold(browser))
382 char *
bf,
size_t bfsize,
bool show_dso)
387 printed =
scnprintf(bf, bfsize,
"%s", cl->
ms.sym->name);
392 scnprintf(bf + printed, bfsize - printed,
" %s",
393 cl->
ms.map ? cl->
ms.map->dso->short_name :
"unknown");
398 #define LEVEL_OFFSET_STEP 3
400 static int hist_browser__show_callchain_node_rb_tree(
struct hist_browser *browser,
405 bool *is_current_entry)
409 u64 new_total, remaining;
416 remaining = new_total;
421 u64 cumul = callchain_cumul_hits(child);
423 char folded_sign =
' ';
425 int extra_offset = 0;
430 char bf[1024], *alloc_str;
433 bool was_first =
first;
440 folded_sign = callchain_list__folded(chain);
441 if (*row_offset != 0) {
447 str = callchain_list__sym_name(chain, bf,
sizeof(bf),
450 double percent = cumul * 100.0 / new_total;
452 if (asprintf(&alloc_str,
"%2.2f%% %s", percent, str) < 0)
453 str =
"Not enough memory!";
459 width = browser->
b.width - (offset + extra_offset + 2);
463 *is_current_entry =
true;
473 if (++row == browser->
b.height)
476 if (folded_sign ==
'+')
480 if (folded_sign ==
'-') {
481 const int new_level = level + (extra_offset ? 2 : 1);
482 row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
483 new_level, row, row_offset,
486 if (row == browser->
b.height)
491 return row - first_row;
494 static int hist_browser__show_callchain_node(
struct hist_browser *browser,
496 int level,
unsigned short row,
498 bool *is_current_entry)
504 char folded_sign =
' ';
510 folded_sign = callchain_list__folded(chain);
512 if (*row_offset != 0) {
521 *is_current_entry =
true;
524 s = callchain_list__sym_name(chain, bf,
sizeof(bf),
532 if (++row == browser->
b.height)
536 if (folded_sign ==
'-')
537 row += hist_browser__show_callchain_node_rb_tree(browser, node,
538 browser->
hists->stats.total_period,
543 return row - first_row;
546 static int hist_browser__show_callchain(
struct hist_browser *browser,
547 struct rb_root *chain,
548 int level,
unsigned short row,
550 bool *is_current_entry)
558 row += hist_browser__show_callchain_node(browser, node, level,
561 if (row == browser->
b.height)
565 return row - first_row;
568 #define HPP__COLOR_FN(_name, _field) \
569 static int hist_browser__hpp_color_ ## _name(struct perf_hpp *hpp, \
570 struct hist_entry *he) \
572 struct hists *hists = he->hists; \
573 double percent = 100.0 * he->stat._field / hists->stats.total_period; \
574 *(double *)hpp->ptr = percent; \
575 return scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); \
591 hist_browser__hpp_color_overhead;
593 hist_browser__hpp_color_overhead_sys;
595 hist_browser__hpp_color_overhead_us;
597 hist_browser__hpp_color_overhead_guest_sys;
599 hist_browser__hpp_color_overhead_guest_us;
602 static int hist_browser__show_entry(
struct hist_browser *browser,
609 int width = browser->
b.width;
610 char folded_sign =
' ';
621 hist_entry__init_have_children(entry);
622 folded_sign = hist_entry__folded(entry);
625 if (row_offset == 0) {
657 if (!current_entry || !browser->
b.navkeypressed)
666 if (!browser->
b.navkeypressed)
676 if (folded_sign ==
'-' && row != browser->
b.height) {
677 printed += hist_browser__show_callchain(browser, &entry->
sorted_chain,
687 static void ui_browser__hists_init_top(
struct ui_browser *browser)
697 static unsigned int hist_browser__refresh(
struct ui_browser *browser)
703 ui_browser__hists_init_top(browser);
705 for (nd = browser->
top; nd; nd =
rb_next(nd)) {
711 row += hist_browser__show_entry(hb, h, row);
712 if (row == browser->
height)
732 static struct rb_node *hists__filter_prev_entries(
struct rb_node *nd)
745 static void ui_browser__hists_seek(
struct ui_browser *browser,
746 off_t offset,
int whence)
755 ui_browser__hists_init_top(browser);
796 if (h->
ms.unfolded) {
798 if (offset > remaining) {
808 nd = hists__filter_entries(
rb_next(nd));
813 }
while (offset != 0);
814 }
else if (offset < 0) {
817 if (h->
ms.unfolded) {
841 nd = hists__filter_prev_entries(
rb_prev(nd));
866 static int hist_browser__fprintf_callchain_node_rb_tree(
struct hist_browser *browser,
868 u64 total,
int level,
873 u64 new_total, remaining;
881 remaining = new_total;
886 u64 cumul = callchain_cumul_hits(child);
888 char folded_sign =
' ';
890 int extra_offset = 0;
895 char bf[1024], *alloc_str;
897 bool was_first =
first;
904 folded_sign = callchain_list__folded(chain);
907 str = callchain_list__sym_name(chain, bf,
sizeof(bf),
910 double percent = cumul * 100.0 / new_total;
912 if (asprintf(&alloc_str,
"%2.2f%% %s", percent, str) < 0)
913 str =
"Not enough memory!";
918 printed +=
fprintf(fp,
"%*s%c %s\n", offset + extra_offset,
" ", folded_sign, str);
920 if (folded_sign ==
'+')
924 if (folded_sign ==
'-') {
925 const int new_level = level + (extra_offset ? 2 : 1);
926 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
936 static int hist_browser__fprintf_callchain_node(
struct hist_browser *browser,
942 char folded_sign =
' ';
948 folded_sign = callchain_list__folded(chain);
949 s = callchain_list__sym_name(chain, bf,
sizeof(bf), browser->
show_dso);
950 printed +=
fprintf(fp,
"%*s%c %s\n", offset,
" ", folded_sign, s);
953 if (folded_sign ==
'-')
954 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
955 browser->
hists->stats.total_period,
960 static int hist_browser__fprintf_callchain(
struct hist_browser *browser,
961 struct rb_root *chain,
int level, FILE *fp)
969 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
975 static int hist_browser__fprintf_entry(
struct hist_browser *browser,
981 char folded_sign =
' ';
984 folded_sign = hist_entry__folded(he);
987 percent = (he->
stat.period * 100.0) / browser->
hists->stats.total_period;
990 printed +=
fprintf(fp,
"%c ", folded_sign);
992 printed +=
fprintf(fp,
" %5.2f%%", percent);
995 printed +=
fprintf(fp,
" %11u", he->
stat.nr_events);
1002 if (folded_sign ==
'-')
1003 printed += hist_browser__fprintf_callchain(browser, &he->
sorted_chain, 1, fp);
1008 static int hist_browser__fprintf(
struct hist_browser *browser, FILE *fp)
1016 printed += hist_browser__fprintf_entry(browser, h, fp);
1017 nd = hists__filter_entries(
rb_next(nd));
1023 static int hist_browser__dump(
struct hist_browser *browser)
1030 if (
access(filename, F_OK))
1041 fp = fopen(filename,
"w");
1044 const char *
err = strerror_r(errno, bf,
sizeof(bf));
1050 hist_browser__fprintf(browser, fp);
1057 static struct hist_browser *hist_browser__new(
struct hists *hists)
1063 browser->
b.refresh = hist_browser__refresh;
1064 browser->
b.seek = ui_browser__hists_seek;
1065 browser->
b.use_navkeypressed =
true;
1075 static void hist_browser__delete(
struct hist_browser *browser)
1090 static int hists__browser_title(
struct hists *hists,
char *bf,
size_t size,
1091 const char *ev_name)
1098 u64 nr_events = hists->
stats.total_period;
1102 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1103 nr_samples, unit, ev_name, nr_events);
1107 printed +=
snprintf(bf + printed, size - printed,
1110 printed +=
scnprintf(bf + printed, size - printed,
1115 printed +=
scnprintf(bf + printed, size - printed,
1120 static inline void free_popup_options(
char **
options,
int n)
1124 for (i = 0; i <
n; ++
i) {
1130 static int perf_evsel__hists_browse(
struct perf_evsel *evsel,
int nr_events,
1131 const char *helpline,
const char *ev_name,
1133 void(*
timer)(
void *arg),
void *arg,
1136 struct hists *hists = &evsel->
hists;
1137 struct hist_browser *browser = hist_browser__new(hists);
1145 if (browser ==
NULL)
1154 memset(options, 0,
sizeof(options));
1157 const struct thread *thread =
NULL;
1158 const struct dso *dso =
NULL;
1160 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1161 annotate_f = -2, annotate_t = -2, browse_map = -2;
1165 key = hist_browser__run(browser, ev_name,
timer, arg, delay_secs);
1168 thread = hist_browser__selected_thread(browser);
1180 goto out_free_stack;
1184 "Annotation is only available for symbolic views, "
1185 "include \"sym*\" in --sort to use it.");
1191 browser->
selection->map->dso->annotate_warned)
1195 hist_browser__dump(browser);
1206 "Please enter the name of symbol you want to see",
1207 buf,
"ENTER: OK, ESC: Cancel",
1211 hist_browser__reset(browser);
1218 "h/?/F1 Show this window\n"
1220 "PGDN/SPACE Navigate\n"
1221 "q/ESC/CTRL+C Exit browser\n\n"
1222 "For multiple event sessions:\n\n"
1223 "TAB/UNTAB Switch events\n\n"
1224 "For symbolic views (--sort has sym):\n\n"
1225 "-> Zoom into DSO/Threads & Annotate current symbol\n"
1227 "a Annotate current symbol\n"
1228 "C Collapse all callchains\n"
1229 "E Expand all callchains\n"
1230 "d Zoom into current DSO\n"
1231 "t Zoom into current Thread\n"
1232 "P Print histograms to perf.hist.N\n"
1233 "V Verbose (DSO names in callchains, etc)\n"
1234 "/ Filter symbol by name");
1248 goto out_free_stack;
1252 if (top == &browser->
hists->dso_filter)
1254 if (top == &browser->
hists->thread_filter)
1255 goto zoom_out_thread;
1261 "Do you really want to exit?"))
1266 goto out_free_stack;
1272 goto add_exit_option;
1279 !bi->
from.map->dso->annotate_warned &&
1280 asprintf(&options[nr_options],
"Annotate %s",
1281 bi->
from.sym->name) > 0)
1282 annotate_f = nr_options++;
1287 !bi->
to.map->dso->annotate_warned &&
1288 (bi->
to.sym != bi->
from.sym ||
1289 bi->
to.map->dso != bi->
from.map->dso) &&
1290 asprintf(&options[nr_options],
"Annotate %s",
1291 bi->
to.sym->name) > 0)
1292 annotate_t = nr_options++;
1297 !browser->
selection->map->dso->annotate_warned &&
1298 asprintf(&options[nr_options],
"Annotate %s",
1300 annotate = nr_options++;
1303 if (thread !=
NULL &&
1304 asprintf(&options[nr_options],
"Zoom %s %s(%d) thread",
1305 (browser->
hists->thread_filter ?
"out of" :
"into"),
1308 zoom_thread = nr_options++;
1311 asprintf(&options[nr_options],
"Zoom %s %s DSO",
1312 (browser->
hists->dso_filter ?
"out of" :
"into"),
1314 zoom_dso = nr_options++;
1318 asprintf(&options[nr_options],
"Browse map details") > 0)
1319 browse_map = nr_options++;
1321 options[nr_options++] = (
char *)
"Exit";
1325 if (choice == nr_options - 1)
1329 free_popup_options(options, nr_options - 1);
1333 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1337 he = hist_browser__selected_entry(browser);
1347 if (choice == annotate_f) {
1350 }
else if (choice == annotate_t) {
1360 timer, arg, delay_secs);
1366 if ((err ==
'q' || err ==
CTRL(
'c'))
1367 && annotate_t != -2 && annotate_f != -2)
1368 goto retry_popup_menu;
1374 }
else if (choice == browse_map)
1376 else if (choice == zoom_dso) {
1378 if (browser->
hists->dso_filter) {
1389 browser->
hists->dso_filter = dso;
1394 hist_browser__reset(browser);
1395 }
else if (choice == zoom_thread) {
1397 if (browser->
hists->thread_filter) {
1412 hist_browser__reset(browser);
1418 hist_browser__delete(browser);
1419 free_popup_options(options, nr_options - 1);
1429 static void perf_evsel_menu__write(
struct ui_browser *browser,
1430 void *entry,
int row)
1439 const char *
warn =
" ";
1446 printed =
scnprintf(bf,
sizeof(bf),
"%lu%c%s%s", nr_events,
1447 unit, unit ==
' ' ?
"" :
" ", ev_name);
1451 if (nr_events != 0) {
1456 printed +=
scnprintf(bf,
sizeof(bf),
": %ld%c%schunks LOST!",
1457 nr_events, unit, unit ==
' ' ?
"" :
" ");
1468 int nr_events,
const char *help,
1469 void(*
timer)(
void *arg),
void *arg,
int delay_secs)
1473 const char *ev_name, *title =
"Available samples";
1477 "ESC: exit, ENTER|->: Browse histograms") < 0)
1488 ui_browser__warn_lost_events(&menu->
b);
1506 key = perf_evsel__hists_browse(pos, nr_events, help,
1507 ev_name,
true,
timer,
1525 "Do you really want to exit?"))
1538 "Do you really want to exit?"))
1554 static int __perf_evlist__tui_browse_hists(
struct perf_evlist *evlist,
1556 void(*
timer)(
void *arg),
void *arg,
1565 .write = perf_evsel_menu__write,
1575 size_t line_len =
strlen(ev_name) + 7;
1577 if (menu.
b.width < line_len)
1578 menu.
b.width = line_len;
1586 void(*
timer)(
void *arg),
void *arg,
1593 return perf_evsel__hists_browse(first, evlist->
nr_entries, help,
1594 ev_name,
false,
timer, arg,
1598 return __perf_evlist__tui_browse_hists(evlist, help,
1599 timer, arg, delay_secs);