summaryrefslogtreecommitdiffstats
path: root/tools/perf/util/ui/browsers/hists.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/ui/browsers/hists.c')
-rw-r--r--tools/perf/util/ui/browsers/hists.c327
1 files changed, 196 insertions, 131 deletions
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c
index dafdf6775d7..ebda8c3fde9 100644
--- a/tools/perf/util/ui/browsers/hists.c
+++ b/tools/perf/util/ui/browsers/hists.c
@@ -58,6 +58,11 @@ static char callchain_list__folded(const struct callchain_list *self)
return map_symbol__folded(&self->ms);
}
+static void map_symbol__set_folding(struct map_symbol *self, bool unfold)
+{
+ self->unfolded = unfold ? self->has_children : false;
+}
+
static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
{
int n = 0;
@@ -129,16 +134,16 @@ static void callchain_node__init_have_children_rb_tree(struct callchain_node *se
for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
struct callchain_list *chain;
- int first = true;
+ bool first = true;
list_for_each_entry(chain, &child->val, list) {
if (first) {
first = false;
chain->ms.has_children = chain->list.next != &child->val ||
- rb_first(&child->rb_root) != NULL;
+ !RB_EMPTY_ROOT(&child->rb_root);
} else
chain->ms.has_children = chain->list.next == &child->val &&
- rb_first(&child->rb_root) != NULL;
+ !RB_EMPTY_ROOT(&child->rb_root);
}
callchain_node__init_have_children_rb_tree(child);
@@ -150,7 +155,7 @@ static void callchain_node__init_have_children(struct callchain_node *self)
struct callchain_list *chain;
list_for_each_entry(chain, &self->val, list)
- chain->ms.has_children = rb_first(&self->rb_root) != NULL;
+ chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root);
callchain_node__init_have_children_rb_tree(self);
}
@@ -168,6 +173,7 @@ static void callchain__init_have_children(struct rb_root *self)
static void hist_entry__init_have_children(struct hist_entry *self)
{
if (!self->init_have_children) {
+ self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain);
callchain__init_have_children(&self->sorted_chain);
self->init_have_children = true;
}
@@ -195,43 +201,114 @@ static bool hist_browser__toggle_fold(struct hist_browser *self)
return false;
}
-static int hist_browser__run(struct hist_browser *self, const char *title,
- struct newtExitStruct *es)
+static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold)
+{
+ int n = 0;
+ struct rb_node *nd;
+
+ for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
+ struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
+ struct callchain_list *chain;
+ bool has_children = false;
+
+ list_for_each_entry(chain, &child->val, list) {
+ ++n;
+ map_symbol__set_folding(&chain->ms, unfold);
+ has_children = chain->ms.has_children;
+ }
+
+ if (has_children)
+ n += callchain_node__set_folding_rb_tree(child, unfold);
+ }
+
+ return n;
+}
+
+static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
+{
+ struct callchain_list *chain;
+ bool has_children = false;
+ int n = 0;
+
+ list_for_each_entry(chain, &node->val, list) {
+ ++n;
+ map_symbol__set_folding(&chain->ms, unfold);
+ has_children = chain->ms.has_children;
+ }
+
+ if (has_children)
+ n += callchain_node__set_folding_rb_tree(node, unfold);
+
+ return n;
+}
+
+static int callchain__set_folding(struct rb_root *chain, bool unfold)
+{
+ struct rb_node *nd;
+ int n = 0;
+
+ for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
+ struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
+ n += callchain_node__set_folding(node, unfold);
+ }
+
+ return n;
+}
+
+static void hist_entry__set_folding(struct hist_entry *self, bool unfold)
+{
+ hist_entry__init_have_children(self);
+ map_symbol__set_folding(&self->ms, unfold);
+
+ if (self->ms.has_children) {
+ int n = callchain__set_folding(&self->sorted_chain, unfold);
+ self->nr_rows = unfold ? n : 0;
+ } else
+ self->nr_rows = 0;
+}
+
+static void hists__set_folding(struct hists *self, bool unfold)
+{
+ struct rb_node *nd;
+
+ self->nr_entries = 0;
+
+ for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
+ hist_entry__set_folding(he, unfold);
+ self->nr_entries += 1 + he->nr_rows;
+ }
+}
+
+static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
+{
+ hists__set_folding(self->hists, unfold);
+ self->b.nr_entries = self->hists->nr_entries;
+ /* Go to the start, we may be way after valid entries after a collapse */
+ ui_browser__reset_index(&self->b);
+}
+
+static int hist_browser__run(struct hist_browser *self, const char *title)
{
- char str[256], unit;
- unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
+ int key;
+ int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't',
+ NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, 0, };
self->b.entries = &self->hists->entries;
self->b.nr_entries = self->hists->nr_entries;
hist_browser__refresh_dimensions(self);
- nr_events = convert_unit(nr_events, &unit);
- snprintf(str, sizeof(str), "Events: %lu%c ",
- nr_events, unit);
- newtDrawRootText(0, 0, str);
-
if (ui_browser__show(&self->b, title,
"Press '?' for help on key bindings") < 0)
return -1;
- newtFormAddHotKey(self->b.form, 'a');
- newtFormAddHotKey(self->b.form, '?');
- newtFormAddHotKey(self->b.form, 'h');
- newtFormAddHotKey(self->b.form, 'd');
- newtFormAddHotKey(self->b.form, 'D');
- newtFormAddHotKey(self->b.form, 't');
-
- newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
- newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
- newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
+ ui_browser__add_exit_keys(&self->b, exit_keys);
while (1) {
- ui_browser__run(&self->b, es);
+ key = ui_browser__run(&self->b);
- if (es->reason != NEWT_EXIT_HOTKEY)
- break;
- switch (es->u.key) {
+ switch (key) {
case 'D': { /* Debug */
static int seq;
struct hist_entry *h = rb_entry(self->b.top,
@@ -245,18 +322,26 @@ static int hist_browser__run(struct hist_browser *self, const char *title,
self->b.top_idx,
h->row_offset, h->nr_rows);
}
- continue;
+ break;
+ case 'C':
+ /* Collapse the whole world. */
+ hist_browser__set_folding(self, false);
+ break;
+ case 'E':
+ /* Expand the whole world. */
+ hist_browser__set_folding(self, true);
+ break;
case NEWT_KEY_ENTER:
if (hist_browser__toggle_fold(self))
break;
/* fall thru */
default:
- return 0;
+ goto out;
}
}
-
+out:
ui_browser__hide(&self->b);
- return 0;
+ return key;
}
static char *callchain_list__sym_name(struct callchain_list *self,
@@ -306,15 +391,10 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
int color;
bool was_first = first;
- if (first) {
+ if (first)
first = false;
- chain->ms.has_children = chain->list.next != &child->val ||
- rb_first(&child->rb_root) != NULL;
- } else {
+ else
extra_offset = LEVEL_OFFSET_STEP;
- chain->ms.has_children = chain->list.next == &child->val &&
- rb_first(&child->rb_root) != NULL;
- }
folded_sign = callchain_list__folded(chain);
if (*row_offset != 0) {
@@ -341,8 +421,8 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
*is_current_entry = true;
}
- SLsmg_set_color(color);
- SLsmg_gotorc(self->b.y + row, self->b.x);
+ ui_browser__set_color(&self->b, color);
+ ui_browser__gotorc(&self->b, row, 0);
slsmg_write_nstring(" ", offset + extra_offset);
slsmg_printf("%c ", folded_sign);
slsmg_write_nstring(str, width);
@@ -384,12 +464,7 @@ static int hist_browser__show_callchain_node(struct hist_browser *self,
list_for_each_entry(chain, &node->val, list) {
char ipstr[BITS_PER_LONG / 4 + 1], *s;
int color;
- /*
- * FIXME: This should be moved to somewhere else,
- * probably when the callchain is created, so as not to
- * traverse it all over again
- */
- chain->ms.has_children = rb_first(&node->rb_root) != NULL;
+
folded_sign = callchain_list__folded(chain);
if (*row_offset != 0) {
@@ -405,8 +480,8 @@ static int hist_browser__show_callchain_node(struct hist_browser *self,
}
s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
- SLsmg_gotorc(self->b.y + row, self->b.x);
- SLsmg_set_color(color);
+ ui_browser__gotorc(&self->b, row, 0);
+ ui_browser__set_color(&self->b, color);
slsmg_write_nstring(" ", offset);
slsmg_printf("%c ", folded_sign);
slsmg_write_nstring(s, width - 2);
@@ -465,7 +540,7 @@ static int hist_browser__show_entry(struct hist_browser *self,
}
if (symbol_conf.use_callchain) {
- entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain);
+ hist_entry__init_have_children(entry);
folded_sign = hist_entry__folded(entry);
}
@@ -484,8 +559,8 @@ static int hist_browser__show_entry(struct hist_browser *self,
color = HE_COLORSET_NORMAL;
}
- SLsmg_set_color(color);
- SLsmg_gotorc(self->b.y + row, self->b.x);
+ ui_browser__set_color(&self->b, color);
+ ui_browser__gotorc(&self->b, row, 0);
if (symbol_conf.use_callchain) {
slsmg_printf("%c ", folded_sign);
width -= 2;
@@ -687,8 +762,6 @@ static struct hist_browser *hist_browser__new(struct hists *hists)
static void hist_browser__delete(struct hist_browser *self)
{
- newtFormDestroy(self->b.form);
- newtPopWindow();
free(self);
}
@@ -702,21 +775,26 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self)
return self->he_selection->thread;
}
-static int hist_browser__title(char *bf, size_t size, const char *ev_name,
- const struct dso *dso, const struct thread *thread)
+static int hists__browser_title(struct hists *self, char *bf, size_t size,
+ const char *ev_name, const struct dso *dso,
+ const struct thread *thread)
{
- int printed = 0;
+ char unit;
+ int printed;
+ unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
+
+ nr_events = convert_unit(nr_events, &unit);
+ printed = snprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name);
if (thread)
printed += snprintf(bf + printed, size - printed,
- "Thread: %s(%d)",
- (thread->comm_set ? thread->comm : ""),
+ ", Thread: %s(%d)",
+ (thread->comm_set ? thread->comm : ""),
thread->pid);
if (dso)
printed += snprintf(bf + printed, size - printed,
- "%sDSO: %s", thread ? " " : "",
- dso->short_name);
- return printed ?: snprintf(bf, size, "Event: %s", ev_name);
+ ", DSO: %s", dso->short_name);
+ return printed;
}
int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
@@ -725,7 +803,6 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
struct pstack *fstack;
const struct thread *thread_filter = NULL;
const struct dso *dso_filter = NULL;
- struct newtExitStruct es;
char msg[160];
int key = -1;
@@ -738,9 +815,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
ui_helpline__push(helpline);
- hist_browser__title(msg, sizeof(msg), ev_name,
- dso_filter, thread_filter);
-
+ hists__browser_title(self, msg, sizeof(msg), ev_name,
+ dso_filter, thread_filter);
while (1) {
const struct thread *thread;
const struct dso *dso;
@@ -749,70 +825,63 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
annotate = -2, zoom_dso = -2, zoom_thread = -2,
browse_map = -2;
- if (hist_browser__run(browser, msg, &es))
- break;
+ key = hist_browser__run(browser, msg);
thread = hist_browser__selected_thread(browser);
dso = browser->selection->map ? browser->selection->map->dso : NULL;
- if (es.reason == NEWT_EXIT_HOTKEY) {
- key = es.u.key;
-
- switch (key) {
- case NEWT_KEY_F1:
- goto do_help;
- case NEWT_KEY_TAB:
- case NEWT_KEY_UNTAB:
- /*
- * Exit the browser, let hists__browser_tree
- * go to the next or previous
- */
- goto out_free_stack;
- default:;
- }
-
- switch (key) {
- case 'a':
- if (browser->selection->map == NULL &&
- browser->selection->map->dso->annotate_warned)
- continue;
- goto do_annotate;
- case 'd':
- goto zoom_dso;
- case 't':
- goto zoom_thread;
- case 'h':
- case '?':
-do_help:
- ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
- "<- Zoom out\n"
- "a Annotate current symbol\n"
- "h/?/F1 Show this window\n"
- "d Zoom into current DSO\n"
- "t Zoom into current Thread\n"
- "q/CTRL+C Exit browser");
+ switch (key) {
+ case NEWT_KEY_TAB:
+ case NEWT_KEY_UNTAB:
+ /*
+ * Exit the browser, let hists__browser_tree
+ * go to the next or previous
+ */
+ goto out_free_stack;
+ case 'a':
+ if (browser->selection->map == NULL &&
+ browser->selection->map->dso->annotate_warned)
continue;
- default:;
- }
- if (is_exit_key(key)) {
- if (key == NEWT_KEY_ESCAPE &&
- !ui__dialog_yesno("Do you really want to exit?"))
- continue;
- break;
- }
-
- if (es.u.key == NEWT_KEY_LEFT) {
- const void *top;
+ goto do_annotate;
+ case 'd':
+ goto zoom_dso;
+ case 't':
+ goto zoom_thread;
+ case NEWT_KEY_F1:
+ case 'h':
+ case '?':
+ ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
+ "<- Zoom out\n"
+ "a Annotate current symbol\n"
+ "h/?/F1 Show this window\n"
+ "C Collapse all callchains\n"
+ "E Expand all callchains\n"
+ "d Zoom into current DSO\n"
+ "t Zoom into current Thread\n"
+ "q/CTRL+C Exit browser");
+ continue;
+ case NEWT_KEY_ENTER:
+ case NEWT_KEY_RIGHT:
+ /* menu */
+ break;
+ case NEWT_KEY_LEFT: {
+ const void *top;
- if (pstack__empty(fstack))
- continue;
- top = pstack__pop(fstack);
- if (top == &dso_filter)
- goto zoom_out_dso;
- if (top == &thread_filter)
- goto zoom_out_thread;
+ if (pstack__empty(fstack))
continue;
- }
+ top = pstack__pop(fstack);
+ if (top == &dso_filter)
+ goto zoom_out_dso;
+ if (top == &thread_filter)
+ goto zoom_out_thread;
+ continue;
+ }
+ case NEWT_KEY_ESCAPE:
+ if (!ui__dialog_yesno("Do you really want to exit?"))
+ continue;
+ /* Fall thru */
+ default:
+ goto out_free_stack;
}
if (browser->selection->sym != NULL &&
@@ -885,8 +954,8 @@ zoom_out_dso:
pstack__push(fstack, &dso_filter);
}
hists__filter_by_dso(self, dso_filter);
- hist_browser__title(msg, sizeof(msg), ev_name,
- dso_filter, thread_filter);
+ hists__browser_title(self, msg, sizeof(msg), ev_name,
+ dso_filter, thread_filter);
hist_browser__reset(browser);
} else if (choice == zoom_thread) {
zoom_thread:
@@ -903,8 +972,8 @@ zoom_out_thread:
pstack__push(fstack, &thread_filter);
}
hists__filter_by_thread(self, thread_filter);
- hist_browser__title(msg, sizeof(msg), ev_name,
- dso_filter, thread_filter);
+ hists__browser_title(self, msg, sizeof(msg), ev_name,
+ dso_filter, thread_filter);
hist_browser__reset(browser);
}
}
@@ -925,10 +994,6 @@ int hists__tui_browse_tree(struct rb_root *self, const char *help)
const char *ev_name = __event_name(hists->type, hists->config);
key = hists__browse(hists, help, ev_name);
-
- if (is_exit_key(key))
- break;
-
switch (key) {
case NEWT_KEY_TAB:
next = rb_next(nd);
@@ -940,7 +1005,7 @@ int hists__tui_browse_tree(struct rb_root *self, const char *help)
continue;
nd = rb_prev(nd);
default:
- break;
+ return key;
}
}