From 8c694d2559acf09bfd8b71fe1795e740063ad803 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 21 Jun 2010 12:44:42 -0300 Subject: perf ui: Introduce routine ui_browser__is_current_entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Will be used in more places in the new tree widget. Cc: Frédéric Weisbecker Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index cf182ca132fe..1e774e7f2281 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -270,6 +270,11 @@ struct ui_browser { u32 nr_entries; }; +static bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) +{ + return (self->first_visible_entry_idx + row) == self->index; +} + static void ui_browser__refresh_dimensions(struct ui_browser *self) { int cols, rows; @@ -286,8 +291,8 @@ static void ui_browser__refresh_dimensions(struct ui_browser *self) static void ui_browser__reset_index(struct ui_browser *self) { - self->index = self->first_visible_entry_idx = 0; - self->first_visible_entry = NULL; + self->index = self->first_visible_entry_idx = 0; + self->first_visible_entry = NULL; } static int objdump_line__show(struct objdump_line *self, struct list_head *head, @@ -353,7 +358,7 @@ static int ui_browser__refresh_entries(struct ui_browser *self) pos = list_entry(self->first_visible_entry, struct objdump_line, node); list_for_each_entry_from(pos, head, node) { - bool current_entry = (self->first_visible_entry_idx + row) == self->index; + bool current_entry = ui_browser__is_current_entry(self, row); SLsmg_gotorc(self->top + row, self->left); objdump_line__show(pos, head, self->width, he, len, current_entry); -- cgit v1.2.3 From 46b0a07a45b07ed5ca8053bbb6ec522982d1a1dd Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 21 Jun 2010 13:36:20 -0300 Subject: perf ui: Introduce ui_browser->seek to support multiple list structures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So that we can use the ui_browser on things like an rb_tree, etc. Cc: Frédéric Weisbecker Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 69 +++++++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 26 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 1e774e7f2281..0ffc8281363c 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -267,9 +267,42 @@ struct ui_browser { void *first_visible_entry, *entries; u16 top, left, width, height; void *priv; + void (*seek)(struct ui_browser *self, + off_t offset, int whence); u32 nr_entries; }; +static void ui_browser__list_head_seek(struct ui_browser *self, + off_t offset, int whence) +{ + struct list_head *head = self->entries; + struct list_head *pos; + + switch (whence) { + case SEEK_SET: + pos = head->next; + break; + case SEEK_CUR: + pos = self->first_visible_entry; + break; + case SEEK_END: + pos = head->prev; + break; + default: + return; + } + + if (offset > 0) { + while (offset-- != 0) + pos = pos->next; + } else { + while (offset++ != 0) + pos = pos->prev; + } + + self->first_visible_entry = pos; +} + static bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) { return (self->first_visible_entry_idx + row) == self->index; @@ -292,7 +325,7 @@ static void ui_browser__refresh_dimensions(struct ui_browser *self) static void ui_browser__reset_index(struct ui_browser *self) { self->index = self->first_visible_entry_idx = 0; - self->first_visible_entry = NULL; + self->seek(self, 0, SEEK_SET); } static int objdump_line__show(struct objdump_line *self, struct list_head *head, @@ -408,7 +441,7 @@ static int ui_browser__run(struct ui_browser *self, const char *title, newtFormAddComponent(self->form, self->sb); while (1) { - unsigned int offset; + off_t offset; newtFormRun(self->form, es); @@ -422,9 +455,8 @@ static int ui_browser__run(struct ui_browser *self, const char *title, break; ++self->index; if (self->index == self->first_visible_entry_idx + self->height) { - struct list_head *pos = self->first_visible_entry; ++self->first_visible_entry_idx; - self->first_visible_entry = pos->next; + self->seek(self, +1, SEEK_CUR); } break; case NEWT_KEY_UP: @@ -432,9 +464,8 @@ static int ui_browser__run(struct ui_browser *self, const char *title, break; --self->index; if (self->index < self->first_visible_entry_idx) { - struct list_head *pos = self->first_visible_entry; --self->first_visible_entry_idx; - self->first_visible_entry = pos->prev; + self->seek(self, -1, SEEK_CUR); } break; case NEWT_KEY_PGDN: @@ -447,12 +478,7 @@ static int ui_browser__run(struct ui_browser *self, const char *title, offset = self->nr_entries - 1 - self->index; self->index += offset; self->first_visible_entry_idx += offset; - - while (offset--) { - struct list_head *pos = self->first_visible_entry; - self->first_visible_entry = pos->next; - } - + self->seek(self, +offset, SEEK_CUR); break; case NEWT_KEY_PGUP: if (self->first_visible_entry_idx == 0) @@ -465,29 +491,19 @@ static int ui_browser__run(struct ui_browser *self, const char *title, self->index -= offset; self->first_visible_entry_idx -= offset; - - while (offset--) { - struct list_head *pos = self->first_visible_entry; - self->first_visible_entry = pos->prev; - } + self->seek(self, -offset, SEEK_CUR); break; case NEWT_KEY_HOME: ui_browser__reset_index(self); break; - case NEWT_KEY_END: { - struct list_head *head = self->entries; + case NEWT_KEY_END: offset = self->height - 1; if (offset > self->nr_entries) offset = self->nr_entries; self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset; - self->first_visible_entry = head->prev; - while (offset-- != 0) { - struct list_head *pos = self->first_visible_entry; - self->first_visible_entry = pos->prev; - } - } + self->seek(self, -offset, SEEK_END); break; case NEWT_KEY_RIGHT: case NEWT_KEY_LEFT: @@ -706,7 +722,8 @@ int hist_entry__tui_annotate(struct hist_entry *self) ui_helpline__push("Press <- or ESC to exit"); memset(&browser, 0, sizeof(browser)); - browser.entries = &head; + browser.entries = &head; + browser.seek = ui_browser__list_head_seek; browser.priv = self; list_for_each_entry(pos, &head, node) { size_t line_len = strlen(pos->line); -- cgit v1.2.3 From 13f499f076c67675e6e3022973729b5d906a84e9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 21 Jun 2010 18:04:02 -0300 Subject: perf ui: Separate showing the entries from running the browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Another patch eroding the changes I had to move to a tree widget that doesn't requires adding all entries in an existing list/tree structure to a generic tree widget, but instead allows traversing just the entries that should appear on the screen on a given moment. Cc: Frédéric Weisbecker Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 60 ++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 31 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 0ffc8281363c..9fa5b20d4090 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -328,6 +328,32 @@ static void ui_browser__reset_index(struct ui_browser *self) self->seek(self, 0, SEEK_SET); } +static int ui_browser__show(struct ui_browser *self, const char *title) +{ + if (self->form != NULL) + return 0; + ui_browser__refresh_dimensions(self); + newtCenteredWindow(self->width + 2, self->height, title); + self->form = newt_form__new(); + if (self->form == NULL) + return -1; + + self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, + HE_COLORSET_NORMAL, + HE_COLORSET_SELECTED); + if (self->sb == NULL) + return -1; + + newtFormAddHotKey(self->form, NEWT_KEY_UP); + newtFormAddHotKey(self->form, NEWT_KEY_DOWN); + newtFormAddHotKey(self->form, NEWT_KEY_PGUP); + newtFormAddHotKey(self->form, NEWT_KEY_PGDN); + newtFormAddHotKey(self->form, NEWT_KEY_HOME); + newtFormAddHotKey(self->form, NEWT_KEY_END); + newtFormAddComponent(self->form, self->sb); + return 0; +} + static int objdump_line__show(struct objdump_line *self, struct list_head *head, int width, struct hist_entry *he, int len, bool current_entry) @@ -406,39 +432,10 @@ static int ui_browser__refresh_entries(struct ui_browser *self) return 0; } -static int ui_browser__run(struct ui_browser *self, const char *title, - struct newtExitStruct *es) +static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) { - if (self->form) { - newtFormDestroy(self->form); - newtPopWindow(); - } - - ui_browser__refresh_dimensions(self); - newtCenteredWindow(self->width + 2, self->height, title); - self->form = newt_form__new(); - if (self->form == NULL) - return -1; - - self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, - HE_COLORSET_NORMAL, - HE_COLORSET_SELECTED); - if (self->sb == NULL) - return -1; - - newtFormAddHotKey(self->form, NEWT_KEY_UP); - newtFormAddHotKey(self->form, NEWT_KEY_DOWN); - newtFormAddHotKey(self->form, NEWT_KEY_PGUP); - newtFormAddHotKey(self->form, NEWT_KEY_PGDN); - newtFormAddHotKey(self->form, ' '); - newtFormAddHotKey(self->form, NEWT_KEY_HOME); - newtFormAddHotKey(self->form, NEWT_KEY_END); - newtFormAddHotKey(self->form, NEWT_KEY_TAB); - newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); - if (ui_browser__refresh_entries(self) < 0) return -1; - newtFormAddComponent(self->form, self->sb); while (1) { off_t offset; @@ -733,7 +730,8 @@ int hist_entry__tui_annotate(struct hist_entry *self) } browser.width += 18; /* Percentage */ - ret = ui_browser__run(&browser, self->ms.sym->name, &es); + ui_browser__show(&browser, self->ms.sym->name); + ui_browser__run(&browser, &es); newtFormDestroy(browser.form); newtPopWindow(); list_for_each_entry_safe(pos, n, &head, node) { -- cgit v1.2.3 From 9f61d85fd0fe91f9a17a9f304d67e55f8ae38b2e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 21 Jun 2010 19:38:33 -0300 Subject: perf ui: Move objdump_line specific stuff out of ui_browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By adding a ui_browser->refresh_entries() pure virtual member. Cc: Frédéric Weisbecker Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 49 ++++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 9fa5b20d4090..7bdbfd3e24d2 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -267,6 +267,7 @@ struct ui_browser { void *first_visible_entry, *entries; u16 top, left, width, height; void *priv; + unsigned int (*refresh_entries)(struct ui_browser *self); void (*seek)(struct ui_browser *self, off_t offset, int whence); u32 nr_entries; @@ -405,26 +406,10 @@ static int objdump_line__show(struct objdump_line *self, struct list_head *head, static int ui_browser__refresh_entries(struct ui_browser *self) { - struct objdump_line *pos; - struct list_head *head = self->entries; - struct hist_entry *he = self->priv; - int row = 0; - int len = he->ms.sym->end - he->ms.sym->start; - - if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) - self->first_visible_entry = head->next; - - pos = list_entry(self->first_visible_entry, struct objdump_line, node); - - list_for_each_entry_from(pos, head, node) { - bool current_entry = ui_browser__is_current_entry(self, row); - SLsmg_gotorc(self->top + row, self->left); - objdump_line__show(pos, head, self->width, - he, len, current_entry); - if (++row == self->height) - break; - } + int row; + newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); + row = self->refresh_entries(self); SLsmg_set_color(HE_COLORSET_NORMAL); SLsmg_fill_region(self->top + row, self->left, self->height - row, self->width, ' '); @@ -557,6 +542,31 @@ static char *callchain_list__sym_name(struct callchain_list *self, return bf; } +static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self) +{ + struct objdump_line *pos; + struct list_head *head = self->entries; + struct hist_entry *he = self->priv; + int row = 0; + int len = he->ms.sym->end - he->ms.sym->start; + + if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) + self->first_visible_entry = head->next; + + pos = list_entry(self->first_visible_entry, struct objdump_line, node); + + list_for_each_entry_from(pos, head, node) { + bool current_entry = ui_browser__is_current_entry(self, row); + SLsmg_gotorc(self->top + row, self->left); + objdump_line__show(pos, head, self->width, + he, len, current_entry); + if (++row == self->height) + break; + } + + return row; +} + static void __callchain__append_graph_browser(struct callchain_node *self, newtComponent tree, u64 total, int *indexes, int depth) @@ -720,6 +730,7 @@ int hist_entry__tui_annotate(struct hist_entry *self) memset(&browser, 0, sizeof(browser)); browser.entries = &head; + browser.refresh_entries = hist_entry__annotate_browser_refresh; browser.seek = ui_browser__list_head_seek; browser.priv = self; list_for_each_entry(pos, &head, node) { -- cgit v1.2.3 From 0879b100f3c187257729f36cba33d96ec2875766 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Tue, 29 Jun 2010 23:02:26 +0530 Subject: perf: Fix hist_entry__tui_annotate() build failure When compiling perf on latest tip/master I see the following error: cc1: warnings being treated as errors util/newt.c: In function 'hist_entry__tui_annotate': util/newt.c:764: warning: 'ret' is used uninitialized in this function make: *** [util/newt.o] Error 1 I think the problem was introduced by commit 13f499f076c67675e6e3022973729b5d906a84e9 Below is a patch that fixes the problem. Signed-off-by: Srikar Dronamraju Cc: Arnaldo Carvalho de Melo LKML-Reference: <20100629173226.GC23231@linux.vnet.ibm.com> Signed-off-by: Ingo Molnar --- tools/perf/util/newt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 89c52fc9b22e..06f248fde5cf 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -753,7 +753,7 @@ int hist_entry__tui_annotate(struct hist_entry *self) browser.width += 18; /* Percentage */ ui_browser__show(&browser, self->ms.sym->name); - ui_browser__run(&browser, &es); + ret = ui_browser__run(&browser, &es); newtFormDestroy(browser.form); newtPopWindow(); list_for_each_entry_safe(pos, n, &head, node) { -- cgit v1.2.3 From 63f20e744a595444f7ab1d47a29c5b74830feb47 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 15 Jul 2010 07:21:07 -0300 Subject: perf ui: Make END go to the last entry, not the top of the last page Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 06f248fde5cf..932f12468c3c 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -491,11 +491,11 @@ static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) break; case NEWT_KEY_END: offset = self->height - 1; + if (offset >= self->nr_entries) + offset = self->nr_entries - 1; - if (offset > self->nr_entries) - offset = self->nr_entries; - - self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset; + self->index = self->nr_entries - 1; + self->first_visible_entry_idx = self->index - offset; self->seek(self, -offset, SEEK_END); break; case NEWT_KEY_RIGHT: -- cgit v1.2.3 From b66ecd97c1866f9a869643a5b69a984d5ce8f8f1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 15 Jul 2010 07:24:30 -0300 Subject: perf ui: Make ui_browser__run exit on unhandled hot keys Right now ENTER doesn't always exits the newt tree widget, as it is used for expanding/collapsing branches, but with the new tree widget being developed we need to regain control to handle it, expanding/collapsing branches. In fact its really up to the ui_browser user to state what extra keys should stop ui_browser__run, and it should handle just the ones needed for basic browsing. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 932f12468c3c..7979003adeaf 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -498,12 +498,8 @@ static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) self->first_visible_entry_idx = self->index - offset; self->seek(self, -offset, SEEK_END); break; - case NEWT_KEY_RIGHT: - case NEWT_KEY_LEFT: - case NEWT_KEY_TAB: - return es->u.key; default: - continue; + return es->u.key; } if (ui_browser__refresh_entries(self) < 0) return -1; -- cgit v1.2.3 From 8a6c5b261c1188379469807d84bfb1365d0f6823 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 20 Jul 2010 14:42:52 -0300 Subject: perf sort: Make column width code per hists instance They were globals, and since we support multiple hists and sessions at the same time, it doesn't make sense to calculate those values considereing all symbols in all sessions. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 34 +++++++------- tools/perf/util/hist.c | 115 ++++++++++++++++++++++++++++++++--------------- tools/perf/util/hist.h | 27 ++++++++--- tools/perf/util/newt.c | 9 ++-- tools/perf/util/sort.c | 17 +++---- tools/perf/util/sort.h | 6 +-- tools/perf/util/symbol.c | 9 ++++ tools/perf/util/symbol.h | 2 + 8 files changed, 140 insertions(+), 79 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index d7f21d71eb69..121339f4360d 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -340,30 +340,29 @@ int event__synthesize_kernel_mmap(event__handler_t process, return process(&ev, session); } -static void thread__comm_adjust(struct thread *self) +static void thread__comm_adjust(struct thread *self, struct hists *hists) { char *comm = self->comm; if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && (!symbol_conf.comm_list || strlist__has_entry(symbol_conf.comm_list, comm))) { - unsigned int slen = strlen(comm); + u16 slen = strlen(comm); - if (slen > comms__col_width) { - comms__col_width = slen; - threads__col_width = slen + 6; - } + if (hists__new_col_len(hists, HISTC_COMM, slen)) + hists__set_col_len(hists, HISTC_THREAD, slen + 6); } } -static int thread__set_comm_adjust(struct thread *self, const char *comm) +static int thread__set_comm_adjust(struct thread *self, const char *comm, + struct hists *hists) { int ret = thread__set_comm(self, comm); if (ret) return ret; - thread__comm_adjust(self); + thread__comm_adjust(self, hists); return 0; } @@ -374,7 +373,8 @@ int event__process_comm(event_t *self, struct perf_session *session) dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid); - if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) { + if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm, + &session->hists)) { dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); return -1; } @@ -641,16 +641,13 @@ void thread__find_addr_location(struct thread *self, al->sym = NULL; } -static void dso__calc_col_width(struct dso *self) +static void dso__calc_col_width(struct dso *self, struct hists *hists) { if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && (!symbol_conf.dso_list || strlist__has_entry(symbol_conf.dso_list, self->name))) { - u16 slen = self->short_name_len; - if (verbose) - slen = self->long_name_len; - if (dsos__col_width < slen) - dsos__col_width = slen; + u16 slen = dso__name_len(self); + hists__new_col_len(hists, HISTC_DSO, slen); } self->slen_calculated = 1; @@ -729,16 +726,17 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, * sampled. */ if (!sort_dso.elide && !al->map->dso->slen_calculated) - dso__calc_col_width(al->map->dso); + dso__calc_col_width(al->map->dso, &session->hists); al->sym = map__find_symbol(al->map, al->addr, filter); } else { const unsigned int unresolved_col_width = BITS_PER_LONG / 4; - if (dsos__col_width < unresolved_col_width && + if (hists__col_len(&session->hists, HISTC_DSO) < unresolved_col_width && !symbol_conf.col_width_list_str && !symbol_conf.field_sep && !symbol_conf.dso_list) - dsos__col_width = unresolved_col_width; + hists__set_col_len(&session->hists, HISTC_DSO, + unresolved_col_width); } if (symbol_conf.sym_list && al->sym && diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index d998d1d706eb..0bc67900352c 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -16,6 +16,50 @@ struct callchain_param callchain_param = { .min_percent = 0.5 }; +u16 hists__col_len(struct hists *self, enum hist_column col) +{ + return self->col_len[col]; +} + +void hists__set_col_len(struct hists *self, enum hist_column col, u16 len) +{ + self->col_len[col] = len; +} + +bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len) +{ + if (len > hists__col_len(self, col)) { + hists__set_col_len(self, col, len); + return true; + } + return false; +} + +static void hists__reset_col_len(struct hists *self) +{ + enum hist_column col; + + for (col = 0; col < HISTC_NR_COLS; ++col) + hists__set_col_len(self, col, 0); +} + +static void hists__calc_col_len(struct hists *self, struct hist_entry *h) +{ + u16 len; + + if (h->ms.sym) + hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); + + len = thread__comm_len(h->thread); + if (hists__new_col_len(self, HISTC_COMM, len)) + hists__set_col_len(self, HISTC_THREAD, len + 6); + + if (h->ms.map) { + len = dso__name_len(h->ms.map->dso); + hists__new_col_len(self, HISTC_DSO, len); + } +} + static void hist_entry__add_cpumode_period(struct hist_entry *self, unsigned int cpumode, u64 period) { @@ -56,13 +100,12 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) return self; } -static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) +static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h) { - if (entry->filtered) - return; - if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen) - self->max_sym_namelen = entry->ms.sym->namelen; - ++self->nr_entries; + if (!h->filtered) { + hists__calc_col_len(self, h); + ++self->nr_entries; + } } static u8 symbol__parent_filter(const struct symbol *parent) @@ -208,7 +251,7 @@ void hists__collapse_resort(struct hists *self) tmp = RB_ROOT; next = rb_first(&self->entries); self->nr_entries = 0; - self->max_sym_namelen = 0; + hists__reset_col_len(self); while (next) { n = rb_entry(next, struct hist_entry, rb_node); @@ -265,7 +308,7 @@ void hists__output_resort(struct hists *self) next = rb_first(&self->entries); self->nr_entries = 0; - self->max_sym_namelen = 0; + hists__reset_col_len(self); while (next) { n = rb_entry(next, struct hist_entry, rb_node); @@ -532,8 +575,9 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, } int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, - struct hists *pair_hists, bool show_displacement, - long displacement, bool color, u64 session_total) + struct hists *hists, struct hists *pair_hists, + bool show_displacement, long displacement, + bool color, u64 session_total) { struct sort_entry *se; u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; @@ -637,24 +681,25 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); ret += se->se_snprintf(self, s + ret, size - ret, - se->se_width ? *se->se_width : 0); + hists__col_len(hists, se->se_width_idx)); } return ret; } -int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, - bool show_displacement, long displacement, FILE *fp, - u64 session_total) +int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, + struct hists *pair_hists, bool show_displacement, + long displacement, FILE *fp, u64 session_total) { char bf[512]; - hist_entry__snprintf(self, bf, sizeof(bf), pair_hists, + hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists, show_displacement, displacement, true, session_total); return fprintf(fp, "%s\n", bf); } -static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, +static size_t hist_entry__fprintf_callchain(struct hist_entry *self, + struct hists *hists, FILE *fp, u64 session_total) { int left_margin = 0; @@ -662,7 +707,7 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, if (sort__first_dimension == SORT_COMM) { struct sort_entry *se = list_first_entry(&hist_entry__sort_list, typeof(*se), list); - left_margin = se->se_width ? *se->se_width : 0; + left_margin = hists__col_len(hists, se->se_width_idx); left_margin -= thread__comm_len(self->thread); } @@ -733,17 +778,17 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, continue; } width = strlen(se->se_header); - if (se->se_width) { - if (symbol_conf.col_width_list_str) { - if (col_width) { - *se->se_width = atoi(col_width); - col_width = strchr(col_width, ','); - if (col_width) - ++col_width; - } + if (symbol_conf.col_width_list_str) { + if (col_width) { + hists__set_col_len(self, se->se_width_idx, + atoi(col_width)); + col_width = strchr(col_width, ','); + if (col_width) + ++col_width; } - width = *se->se_width = max(*se->se_width, width); } + if (!hists__new_col_len(self, se->se_width_idx, width)) + width = hists__col_len(self, se->se_width_idx); fprintf(fp, " %*s", width, se->se_header); } fprintf(fp, "\n"); @@ -766,9 +811,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, continue; fprintf(fp, " "); - if (se->se_width) - width = *se->se_width; - else + width = hists__col_len(self, se->se_width_idx); + if (width == 0) width = strlen(se->se_header); for (i = 0; i < width; i++) fprintf(fp, "."); @@ -788,12 +832,12 @@ print_entries: displacement = 0; ++position; } - ret += hist_entry__fprintf(h, pair, show_displacement, + ret += hist_entry__fprintf(h, self, pair, show_displacement, displacement, fp, self->stats.total_period); if (symbol_conf.use_callchain) - ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period); - + ret += hist_entry__fprintf_callchain(h, self, fp, + self->stats.total_period); if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, MAP__FUNCTION, verbose, fp); @@ -817,8 +861,7 @@ static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, self->stats.total_period += h->period; self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; - if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) - self->max_sym_namelen = h->ms.sym->namelen; + hists__calc_col_len(self, h); } void hists__filter_by_dso(struct hists *self, const struct dso *dso) @@ -827,7 +870,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) self->nr_entries = self->stats.total_period = 0; self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; - self->max_sym_namelen = 0; + hists__reset_col_len(self); for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); @@ -850,7 +893,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) self->nr_entries = self->stats.total_period = 0; self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; - self->max_sym_namelen = 0; + hists__reset_col_len(self); for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 83fa33a7b38b..92962b2f579b 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -56,6 +56,16 @@ struct events_stats { u32 nr_unknown_events; }; +enum hist_column { + HISTC_SYMBOL, + HISTC_DSO, + HISTC_THREAD, + HISTC_COMM, + HISTC_PARENT, + HISTC_CPU, + HISTC_NR_COLS, /* Last entry */ +}; + struct hists { struct rb_node rb_node; struct rb_root entries; @@ -64,7 +74,7 @@ struct hists { u64 config; u64 event_stream; u32 type; - u32 max_sym_namelen; + u16 col_len[HISTC_NR_COLS]; }; struct hist_entry *__hists__add_entry(struct hists *self, @@ -72,12 +82,13 @@ struct hist_entry *__hists__add_entry(struct hists *self, struct symbol *parent, u64 period); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); -int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, - bool show_displacement, long displacement, FILE *fp, - u64 total); +int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, + struct hists *pair_hists, bool show_displacement, + long displacement, FILE *fp, u64 total); int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, - struct hists *pair_hists, bool show_displacement, - long displacement, bool color, u64 total); + struct hists *hists, struct hists *pair_hists, + bool show_displacement, long displacement, + bool color, u64 total); void hist_entry__free(struct hist_entry *); void hists__output_resort(struct hists *self); @@ -95,6 +106,10 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head); void hists__filter_by_dso(struct hists *self, const struct dso *dso); void hists__filter_by_thread(struct hists *self, const struct thread *thread); +u16 hists__col_len(struct hists *self, enum hist_column col); +void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); +bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); + #ifdef NO_NEWT_SUPPORT static inline int hists__browse(struct hists *self __used, const char *helpline __used, diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 7979003adeaf..ab6eb368cbf5 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -692,7 +692,8 @@ static void hist_entry__append_callchain_browser(struct hist_entry *self, } static size_t hist_entry__append_browser(struct hist_entry *self, - newtComponent tree, u64 total) + newtComponent tree, + struct hists *hists) { char s[256]; size_t ret; @@ -700,8 +701,8 @@ static size_t hist_entry__append_browser(struct hist_entry *self, if (symbol_conf.exclude_other && !self->parent) return 0; - ret = hist_entry__snprintf(self, s, sizeof(s), NULL, - false, 0, false, total); + ret = hist_entry__snprintf(self, s, sizeof(s), hists, NULL, + false, 0, false, hists->stats.total_period); if (symbol_conf.use_callchain) { int indexes[2]; @@ -842,7 +843,7 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists if (h->filtered) continue; - len = hist_entry__append_browser(h, self->tree, hists->stats.total_period); + len = hist_entry__append_browser(h, self->tree, hists); if (len > max_len) max_len = len; if (symbol_conf.use_callchain) diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index c27b4b03fbc1..1c61a4f4aa8a 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1,4 +1,5 @@ #include "sort.h" +#include "hist.h" regex_t parent_regex; const char default_parent_pattern[] = "^sys_|^do_page_fault"; @@ -10,11 +11,6 @@ int sort__has_parent = 0; enum sort_type sort__first_dimension; -unsigned int dsos__col_width; -unsigned int comms__col_width; -unsigned int threads__col_width; -unsigned int cpus__col_width; -static unsigned int parent_symbol__col_width; char * field_sep; LIST_HEAD(hist_entry__sort_list); @@ -36,7 +32,7 @@ struct sort_entry sort_thread = { .se_header = "Command: Pid", .se_cmp = sort__thread_cmp, .se_snprintf = hist_entry__thread_snprintf, - .se_width = &threads__col_width, + .se_width_idx = HISTC_THREAD, }; struct sort_entry sort_comm = { @@ -44,34 +40,35 @@ struct sort_entry sort_comm = { .se_cmp = sort__comm_cmp, .se_collapse = sort__comm_collapse, .se_snprintf = hist_entry__comm_snprintf, - .se_width = &comms__col_width, + .se_width_idx = HISTC_COMM, }; struct sort_entry sort_dso = { .se_header = "Shared Object", .se_cmp = sort__dso_cmp, .se_snprintf = hist_entry__dso_snprintf, - .se_width = &dsos__col_width, + .se_width_idx = HISTC_DSO, }; struct sort_entry sort_sym = { .se_header = "Symbol", .se_cmp = sort__sym_cmp, .se_snprintf = hist_entry__sym_snprintf, + .se_width_idx = HISTC_SYMBOL, }; struct sort_entry sort_parent = { .se_header = "Parent symbol", .se_cmp = sort__parent_cmp, .se_snprintf = hist_entry__parent_snprintf, - .se_width = &parent_symbol__col_width, + .se_width_idx = HISTC_PARENT, }; struct sort_entry sort_cpu = { .se_header = "CPU", .se_cmp = sort__cpu_cmp, .se_snprintf = hist_entry__cpu_snprintf, - .se_width = &cpus__col_width, + .se_width_idx = HISTC_CPU, }; struct sort_dimension { diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 560c855417e4..03a1722e6d02 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -36,10 +36,6 @@ extern struct sort_entry sort_comm; extern struct sort_entry sort_dso; extern struct sort_entry sort_sym; extern struct sort_entry sort_parent; -extern unsigned int dsos__col_width; -extern unsigned int comms__col_width; -extern unsigned int threads__col_width; -extern unsigned int cpus__col_width; extern enum sort_type sort__first_dimension; struct hist_entry { @@ -87,7 +83,7 @@ struct sort_entry { int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size, unsigned int width); - unsigned int *se_width; + u8 se_width_idx; bool elide; }; diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 971d0a05d6b4..bc6e7e8c480d 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -12,6 +12,7 @@ #include #include #include "build-id.h" +#include "debug.h" #include "symbol.h" #include "strlist.h" @@ -40,6 +41,14 @@ struct symbol_conf symbol_conf = { .try_vmlinux_path = true, }; +int dso__name_len(const struct dso *self) +{ + if (verbose) + return self->long_name_len; + + return self->short_name_len; +} + bool dso__loaded(const struct dso *self, enum map_type type) { return self->loaded & (1 << type); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 80e569bbdecc..d436ee3d3a73 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -146,6 +146,8 @@ struct dso *dso__new(const char *name); struct dso *dso__new_kernel(const char *name); void dso__delete(struct dso *self); +int dso__name_len(const struct dso *self); + bool dso__loaded(const struct dso *self, enum map_type type); bool dso__sorted_by_name(const struct dso *self, enum map_type type); -- cgit v1.2.3 From b61b55ed995fd2765cd4ec0b22f0348dee272070 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 21 Jul 2010 17:55:32 -0300 Subject: perf ui: Restore SPACE as an alias to PGDN in annotate Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index ab6eb368cbf5..2f5f7a1878cf 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -750,6 +750,7 @@ int hist_entry__tui_annotate(struct hist_entry *self) browser.width += 18; /* Percentage */ ui_browser__show(&browser, self->ms.sym->name); + newtFormAddHotKey(browser.form, ' '); ret = ui_browser__run(&browser, &es); newtFormDestroy(browser.form); newtPopWindow(); -- cgit v1.2.3 From 63160f73e7baa6618f19d7681bcab5be5c557205 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 26 Jul 2010 13:47:15 -0300 Subject: perf ui: Consider the refreshed dimensions in ui_browser__show When we call ui_browser__show we may have called ui_browser__refresh_dimensions to check if the maximum lenght for the contained entries changed, such as when zooming in and out DSOs or threads in the hist browser. For that to happen we must delete the old form, that will take care of deleting the vertical scrollbar, etc, and then recreate them, with the new dimensions. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 2f5f7a1878cf..aed21490ccdc 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -342,8 +342,10 @@ static void ui_browser__reset_index(struct ui_browser *self) static int ui_browser__show(struct ui_browser *self, const char *title) { - if (self->form != NULL) - return 0; + if (self->form != NULL) { + newtFormDestroy(self->form); + newtPopWindow(); + } ui_browser__refresh_dimensions(self); newtCenteredWindow(self->width + 2, self->height, title); self->form = newt_form__new(); -- cgit v1.2.3 From 8d8c369f3d697e31e04133ca18b516952549ee33 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 26 Jul 2010 14:08:48 -0300 Subject: perf ui: Show the scroll bar over the left window frame So that we gain two columns and look more like classical (at least in TUIs) scroll bars bars. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index aed21490ccdc..cdd958eb68cf 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -347,12 +347,12 @@ static int ui_browser__show(struct ui_browser *self, const char *title) newtPopWindow(); } ui_browser__refresh_dimensions(self); - newtCenteredWindow(self->width + 2, self->height, title); + newtCenteredWindow(self->width, self->height, title); self->form = newt_form__new(); if (self->form == NULL) return -1; - self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, + self->sb = newtVerticalScrollbar(self->width, 0, self->height, HE_COLORSET_NORMAL, HE_COLORSET_SELECTED); if (self->sb == NULL) -- cgit v1.2.3 From 0f0cbf7aa3d3460a3eb201a772326739a0c0210a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 26 Jul 2010 17:13:40 -0300 Subject: perf ui: New hists tree widget The stock newt checkbox tree widget we were using was not really suitable for hist entry + callchain browsing. The problems with it were manifold: - We needed to traverse the whole hist_entry rb_tree to add each entry + callchains beforehand. - No control over the colors used for each row So a new tree widget, based mostly on slang, was written. It extends the ui_browser class already used for annotate to allow the user to fold/unfold branches in the callchains tree, using extra fields in the symbol_map class that is embedded in hist_entry and callchain_node instances to store the folding state and when changing this state calculates the number of rows that are produced when showing a particular hist_entry instance. This greatly speeds up browsing as we don't have to upfront touch all the entries and only calculate callchain related operations when some callchain branch is actually unfolded. The memory footprint is also reduced as the data structure is not duplicated, just some extra fields for controling callchain state and to simplify the process of seeking thru entries (nr_rows, row_offset) were added. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 3 + tools/perf/util/newt.c | 972 ++++++++++++++++++++++++++++++++--------------- tools/perf/util/sort.h | 12 + tools/perf/util/symbol.h | 2 + 4 files changed, 678 insertions(+), 311 deletions(-) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f93095ffaab0..d0f07f7bdf16 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -885,6 +885,9 @@ static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, return; ++self->nr_entries; + if (h->ms.unfolded) + self->nr_entries += h->nr_rows; + h->row_offset = 0; self->stats.total_period += h->period; self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index cdd958eb68cf..28f74eb8fe7c 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -509,38 +509,6 @@ static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) return 0; } -/* - * When debugging newt problems it was useful to be able to "unroll" - * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate - * a source file with the sequence of calls to these methods, to then - * tweak the arrays to get the intended results, so I'm keeping this code - * here, may be useful again in the future. - */ -#undef NEWT_DEBUG - -static void newt_checkbox_tree__add(newtComponent tree, const char *str, - void *priv, int *indexes) -{ -#ifdef NEWT_DEBUG - /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */ - int i = 0, len = 40 - strlen(str); - - fprintf(stderr, - "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ", - len, len, " ", str, priv); - while (indexes[i] != NEWT_ARG_LAST) { - if (indexes[i] != NEWT_ARG_APPEND) - fprintf(stderr, " %d,", indexes[i]); - else - fprintf(stderr, " %s,", "NEWT_ARG_APPEND"); - ++i; - } - fprintf(stderr, " %s", " NEWT_ARG_LAST);\n"); - fflush(stderr); -#endif - newtCheckboxTreeAddArray(tree, str, priv, 0, indexes); -} - static char *callchain_list__sym_name(struct callchain_list *self, char *bf, size_t bfsize) { @@ -576,147 +544,6 @@ static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self return row; } -static void __callchain__append_graph_browser(struct callchain_node *self, - newtComponent tree, u64 total, - int *indexes, int depth) -{ - struct rb_node *node; - u64 new_total, remaining; - int idx = 0; - - if (callchain_param.mode == CHAIN_GRAPH_REL) - new_total = self->children_hit; - else - new_total = total; - - remaining = new_total; - node = rb_first(&self->rb_root); - while (node) { - struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); - struct rb_node *next = rb_next(node); - u64 cumul = cumul_hits(child); - struct callchain_list *chain; - int first = true, printed = 0; - int chain_idx = -1; - remaining -= cumul; - - indexes[depth] = NEWT_ARG_APPEND; - indexes[depth + 1] = NEWT_ARG_LAST; - - list_for_each_entry(chain, &child->val, list) { - char ipstr[BITS_PER_LONG / 4 + 1], - *alloc_str = NULL; - const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - - if (first) { - double percent = cumul * 100.0 / new_total; - - first = false; - if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) - str = "Not enough memory!"; - else - str = alloc_str; - } else { - indexes[depth] = idx; - indexes[depth + 1] = NEWT_ARG_APPEND; - indexes[depth + 2] = NEWT_ARG_LAST; - ++chain_idx; - } - newt_checkbox_tree__add(tree, str, &chain->ms, indexes); - free(alloc_str); - ++printed; - } - - indexes[depth] = idx; - if (chain_idx != -1) - indexes[depth + 1] = chain_idx; - if (printed != 0) - ++idx; - __callchain__append_graph_browser(child, tree, new_total, indexes, - depth + (chain_idx != -1 ? 2 : 1)); - node = next; - } -} - -static void callchain__append_graph_browser(struct callchain_node *self, - newtComponent tree, u64 total, - int *indexes, int parent_idx) -{ - struct callchain_list *chain; - int i = 0; - - indexes[1] = NEWT_ARG_APPEND; - indexes[2] = NEWT_ARG_LAST; - - list_for_each_entry(chain, &self->val, list) { - char ipstr[BITS_PER_LONG / 4 + 1], *str; - - if (chain->ip >= PERF_CONTEXT_MAX) - continue; - - if (!i++ && sort__first_dimension == SORT_SYM) - continue; - - str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - newt_checkbox_tree__add(tree, str, &chain->ms, indexes); - } - - indexes[1] = parent_idx; - indexes[2] = NEWT_ARG_APPEND; - indexes[3] = NEWT_ARG_LAST; - __callchain__append_graph_browser(self, tree, total, indexes, 2); -} - -static void hist_entry__append_callchain_browser(struct hist_entry *self, - newtComponent tree, u64 total, int parent_idx) -{ - struct rb_node *rb_node; - int indexes[1024] = { [0] = parent_idx, }; - int idx = 0; - struct callchain_node *chain; - - rb_node = rb_first(&self->sorted_chain); - while (rb_node) { - chain = rb_entry(rb_node, struct callchain_node, rb_node); - switch (callchain_param.mode) { - case CHAIN_FLAT: - break; - case CHAIN_GRAPH_ABS: /* falldown */ - case CHAIN_GRAPH_REL: - callchain__append_graph_browser(chain, tree, total, indexes, idx++); - break; - case CHAIN_NONE: - default: - break; - } - rb_node = rb_next(rb_node); - } -} - -static size_t hist_entry__append_browser(struct hist_entry *self, - newtComponent tree, - struct hists *hists) -{ - char s[256]; - size_t ret; - - if (symbol_conf.exclude_other && !self->parent) - return 0; - - ret = hist_entry__snprintf(self, s, sizeof(s), hists, NULL, - false, 0, false, hists->stats.total_period); - if (symbol_conf.use_callchain) { - int indexes[2]; - - indexes[0] = NEWT_ARG_APPEND; - indexes[1] = NEWT_ARG_LAST; - newt_checkbox_tree__add(tree, s, &self->ms, indexes); - } else - newtListboxAppendEntry(tree, s, &self->ms); - - return ret; -} - int hist_entry__tui_annotate(struct hist_entry *self) { struct ui_browser browser; @@ -764,157 +591,48 @@ int hist_entry__tui_annotate(struct hist_entry *self) return ret; } -static const void *newt__symbol_tree_get_current(newtComponent self) -{ - if (symbol_conf.use_callchain) - return newtCheckboxTreeGetCurrent(self); - return newtListboxGetCurrent(self); -} - -static void hist_browser__selection(newtComponent self, void *data) -{ - const struct map_symbol **symbol_ptr = data; - *symbol_ptr = newt__symbol_tree_get_current(self); -} - struct hist_browser { - newtComponent form, tree; - const struct map_symbol *selection; + struct ui_browser b; + struct hists *hists; + struct hist_entry *he_selection; + struct map_symbol *selection; }; -static struct hist_browser *hist_browser__new(void) +static void hist_browser__reset(struct hist_browser *self); +static int hist_browser__run(struct hist_browser *self, const char *title, + struct newtExitStruct *es); +static unsigned int hist_browser__refresh_entries(struct ui_browser *self); +static void ui_browser__hists_seek(struct ui_browser *self, + off_t offset, int whence); + +static struct hist_browser *hist_browser__new(struct hists *hists) { - struct hist_browser *self = malloc(sizeof(*self)); + struct hist_browser *self = zalloc(sizeof(*self)); - if (self != NULL) - self->form = NULL; + if (self) { + self->hists = hists; + self->b.refresh_entries = hist_browser__refresh_entries; + self->b.seek = ui_browser__hists_seek; + } return self; } static void hist_browser__delete(struct hist_browser *self) { - newtFormDestroy(self->form); + newtFormDestroy(self->b.form); newtPopWindow(); free(self); } -static int hist_browser__populate(struct hist_browser *self, struct hists *hists, - const char *title) -{ - int max_len = 0, idx, cols, rows; - struct ui_progress *progress; - struct rb_node *nd; - u64 curr_hist = 0; - char seq[] = ".", unit; - char str[256]; - unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; - - if (self->form) { - newtFormDestroy(self->form); - newtPopWindow(); - } - - nr_events = convert_unit(nr_events, &unit); - snprintf(str, sizeof(str), "Events: %lu%c ", - nr_events, unit); - newtDrawRootText(0, 0, str); - - newtGetScreenSize(NULL, &rows); - - if (symbol_conf.use_callchain) - self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, - NEWT_FLAG_SCROLL); - else - self->tree = newtListbox(0, 0, rows - 5, - (NEWT_FLAG_SCROLL | - NEWT_FLAG_RETURNEXIT)); - - newtComponentAddCallback(self->tree, hist_browser__selection, - &self->selection); - - progress = ui_progress__new("Adding entries to the browser...", - hists->nr_entries); - if (progress == NULL) - return -1; - - idx = 0; - for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - int len; - - if (h->filtered) - continue; - - len = hist_entry__append_browser(h, self->tree, hists); - if (len > max_len) - max_len = len; - if (symbol_conf.use_callchain) - hist_entry__append_callchain_browser(h, self->tree, - hists->stats.total_period, idx++); - ++curr_hist; - if (curr_hist % 5) - ui_progress__update(progress, curr_hist); - } - - ui_progress__delete(progress); - - newtGetScreenSize(&cols, &rows); - - if (max_len > cols) - max_len = cols - 3; - - if (!symbol_conf.use_callchain) - newtListboxSetWidth(self->tree, max_len); - - newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0), - rows - 5, title); - self->form = newt_form__new(); - if (self->form == NULL) - return -1; - - newtFormAddHotKey(self->form, 'A'); - newtFormAddHotKey(self->form, 'a'); - newtFormAddHotKey(self->form, 'D'); - newtFormAddHotKey(self->form, 'd'); - newtFormAddHotKey(self->form, 'T'); - newtFormAddHotKey(self->form, 't'); - newtFormAddHotKey(self->form, '?'); - newtFormAddHotKey(self->form, 'H'); - newtFormAddHotKey(self->form, 'h'); - newtFormAddHotKey(self->form, NEWT_KEY_F1); - newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); - newtFormAddHotKey(self->form, NEWT_KEY_TAB); - newtFormAddHotKey(self->form, NEWT_KEY_UNTAB); - newtFormAddComponents(self->form, self->tree, NULL); - self->selection = newt__symbol_tree_get_current(self->tree); - - return 0; -} - static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) { - int *indexes; - - if (!symbol_conf.use_callchain) - goto out; - - indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection); - if (indexes) { - bool is_hist_entry = indexes[1] == NEWT_ARG_LAST; - free(indexes); - if (is_hist_entry) - goto out; - } - return NULL; -out: - return container_of(self->selection, struct hist_entry, ms); + return self->he_selection; } static struct thread *hist_browser__selected_thread(struct hist_browser *self) { - struct hist_entry *he = hist_browser__selected_entry(self); - return he ? he->thread : NULL; + return self->he_selection->thread; } static int hist_browser__title(char *bf, size_t size, const char *ev_name, @@ -936,7 +654,7 @@ static int hist_browser__title(char *bf, size_t size, const char *ev_name, int hists__browse(struct hists *self, const char *helpline, const char *ev_name) { - struct hist_browser *browser = hist_browser__new(); + struct hist_browser *browser = hist_browser__new(self); struct pstack *fstack; const struct thread *thread_filter = NULL; const struct dso *dso_filter = NULL; @@ -955,8 +673,6 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) hist_browser__title(msg, sizeof(msg), ev_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, self, msg) < 0) - goto out_free_stack; while (1) { const struct thread *thread; @@ -965,7 +681,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) int nr_options = 0, choice = 0, i, annotate = -2, zoom_dso = -2, zoom_thread = -2; - newtFormRun(browser->form, &es); + if (hist_browser__run(browser, msg, &es)) + break; thread = hist_browser__selected_thread(browser); dso = browser->selection->map ? browser->selection->map->dso : NULL; @@ -1100,8 +817,7 @@ zoom_out_dso: hists__filter_by_dso(self, dso_filter); hist_browser__title(msg, sizeof(msg), ev_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, self, msg) < 0) - goto out; + hist_browser__reset(browser); } else if (choice == zoom_thread) { zoom_thread: if (thread_filter) { @@ -1119,8 +835,7 @@ zoom_out_thread: hists__filter_by_thread(self, thread_filter); hist_browser__title(msg, sizeof(msg), ev_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, self, msg) < 0) - goto out; + hist_browser__reset(browser); } } out_free_stack: @@ -1207,3 +922,638 @@ void exit_browser(bool wait_for_ok) newtFinished(); } } + +static void hist_browser__refresh_dimensions(struct hist_browser *self) +{ + /* 3 == +/- toggle symbol before actual hist_entry rendering */ + self->b.width = 3 + (hists__sort_list_width(self->hists) + + sizeof("[k]")); +} + +static void hist_browser__reset(struct hist_browser *self) +{ + self->b.nr_entries = self->hists->nr_entries; + hist_browser__refresh_dimensions(self); + ui_browser__reset_index(&self->b); +} + +static char tree__folded_sign(bool unfolded) +{ + return unfolded ? '-' : '+'; +} + +static char map_symbol__folded(const struct map_symbol *self) +{ + return self->has_children ? tree__folded_sign(self->unfolded) : ' '; +} + +static char hist_entry__folded(const struct hist_entry *self) +{ + return map_symbol__folded(&self->ms); +} + +static char callchain_list__folded(const struct callchain_list *self) +{ + return map_symbol__folded(&self->ms); +} + +static bool map_symbol__toggle_fold(struct map_symbol *self) +{ + if (!self->has_children) + return false; + + self->unfolded = !self->unfolded; + return true; +} + +#define LEVEL_OFFSET_STEP 3 + +static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, + struct callchain_node *chain_node, + u64 total, int level, + unsigned short row, + off_t *row_offset, + bool *is_current_entry) +{ + struct rb_node *node; + int first_row = row, width, offset = level * LEVEL_OFFSET_STEP; + u64 new_total, remaining; + + if (callchain_param.mode == CHAIN_GRAPH_REL) + new_total = chain_node->children_hit; + else + new_total = total; + + remaining = new_total; + node = rb_first(&chain_node->rb_root); + while (node) { + struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); + struct rb_node *next = rb_next(node); + u64 cumul = cumul_hits(child); + struct callchain_list *chain; + char folded_sign = ' '; + int first = true; + int extra_offset = 0; + + remaining -= cumul; + + list_for_each_entry(chain, &child->val, list) { + char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str; + const char *str; + int color; + bool was_first = first; + + if (first) { + first = false; + chain->ms.has_children = chain->list.next != &child->val || + rb_first(&child->rb_root) != NULL; + } 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) { + --*row_offset; + goto do_next; + } + + alloc_str = NULL; + str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); + if (was_first) { + double percent = cumul * 100.0 / new_total; + + if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) + str = "Not enough memory!"; + else + str = alloc_str; + } + + color = HE_COLORSET_NORMAL; + width = self->b.width - (offset + extra_offset + 2); + if (ui_browser__is_current_entry(&self->b, row)) { + self->selection = &chain->ms; + color = HE_COLORSET_SELECTED; + *is_current_entry = true; + } + + SLsmg_set_color(color); + SLsmg_gotorc(self->b.top + row, self->b.left); + slsmg_write_nstring(" ", offset + extra_offset); + slsmg_printf("%c ", folded_sign); + slsmg_write_nstring(str, width); + free(alloc_str); + + if (++row == self->b.height) + goto out; +do_next: + if (folded_sign == '+') + break; + } + + if (folded_sign == '-') { + const int new_level = level + (extra_offset ? 2 : 1); + row += hist_browser__show_callchain_node_rb_tree(self, child, new_total, + new_level, row, row_offset, + is_current_entry); + } + if (row == self->b.height) + goto out; + node = next; + } +out: + return row - first_row; +} + +static int hist_browser__show_callchain_node(struct hist_browser *self, + struct callchain_node *node, + int level, unsigned short row, + off_t *row_offset, + bool *is_current_entry) +{ + struct callchain_list *chain; + int first_row = row, + offset = level * LEVEL_OFFSET_STEP, + width = self->b.width - offset; + char folded_sign = ' '; + + 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) { + --*row_offset; + continue; + } + + color = HE_COLORSET_NORMAL; + if (ui_browser__is_current_entry(&self->b, row)) { + self->selection = &chain->ms; + color = HE_COLORSET_SELECTED; + *is_current_entry = true; + } + + s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); + SLsmg_gotorc(self->b.top + row, self->b.left); + SLsmg_set_color(color); + slsmg_write_nstring(" ", offset); + slsmg_printf("%c ", folded_sign); + slsmg_write_nstring(s, width - 2); + + if (++row == self->b.height) + goto out; + } + + if (folded_sign == '-') + row += hist_browser__show_callchain_node_rb_tree(self, node, + self->hists->stats.total_period, + level + 1, row, + row_offset, + is_current_entry); +out: + return row - first_row; +} + +static int hist_browser__show_callchain(struct hist_browser *self, + struct rb_root *chain, + int level, unsigned short row, + off_t *row_offset, + bool *is_current_entry) +{ + struct rb_node *nd; + int first_row = row; + + for (nd = rb_first(chain); nd; nd = rb_next(nd)) { + struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); + + row += hist_browser__show_callchain_node(self, node, level, + row, row_offset, + is_current_entry); + if (row == self->b.height) + break; + } + + return row - first_row; +} + +static int hist_browser__show_entry(struct hist_browser *self, + struct hist_entry *entry, + unsigned short row) +{ + char s[256]; + double percent; + int printed = 0; + int color, width = self->b.width; + char folded_sign = ' '; + bool current_entry = ui_browser__is_current_entry(&self->b, row); + off_t row_offset = entry->row_offset; + + if (current_entry) { + self->he_selection = entry; + self->selection = &entry->ms; + } + + if (symbol_conf.use_callchain) { + entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain); + folded_sign = hist_entry__folded(entry); + } + + if (row_offset == 0) { + hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false, + 0, false, self->hists->stats.total_period); + percent = (entry->period * 100.0) / self->hists->stats.total_period; + + color = HE_COLORSET_SELECTED; + if (!current_entry) { + if (percent >= MIN_RED) + color = HE_COLORSET_TOP; + else if (percent >= MIN_GREEN) + color = HE_COLORSET_MEDIUM; + else + color = HE_COLORSET_NORMAL; + } + + SLsmg_set_color(color); + SLsmg_gotorc(self->b.top + row, self->b.left); + if (symbol_conf.use_callchain) { + slsmg_printf("%c ", folded_sign); + width -= 2; + } + slsmg_write_nstring(s, width); + ++row; + ++printed; + } else + --row_offset; + + if (folded_sign == '-' && row != self->b.height) { + printed += hist_browser__show_callchain(self, &entry->sorted_chain, + 1, row, &row_offset, + ¤t_entry); + if (current_entry) + self->he_selection = entry; + } + + return printed; +} + +static unsigned int hist_browser__refresh_entries(struct ui_browser *self) +{ + unsigned row = 0; + struct rb_node *nd; + struct hist_browser *hb = container_of(self, struct hist_browser, b); + + if (self->first_visible_entry == NULL) + self->first_visible_entry = rb_first(&hb->hists->entries); + + for (nd = self->first_visible_entry; nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (h->filtered) + continue; + + row += hist_browser__show_entry(hb, h, row); + if (row == self->height) + break; + } + + return row; +} + +static void callchain_node__init_have_children_rb_tree(struct callchain_node *self) +{ + struct rb_node *nd = rb_first(&self->rb_root); + + 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; + + 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; + } else + chain->ms.has_children = chain->list.next == &child->val && + rb_first(&child->rb_root) != NULL; + } + + callchain_node__init_have_children_rb_tree(child); + } +} + +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; + + callchain_node__init_have_children_rb_tree(self); +} + +static void callchain__init_have_children(struct rb_root *self) +{ + struct rb_node *nd; + + for (nd = rb_first(self); nd; nd = rb_next(nd)) { + struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); + callchain_node__init_have_children(node); + } +} + +static void hist_entry__init_have_children(struct hist_entry *self) +{ + if (!self->init_have_children) { + callchain__init_have_children(&self->sorted_chain); + self->init_have_children = true; + } +} + +static struct rb_node *hists__filter_entries(struct rb_node *nd) +{ + while (nd != NULL) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + if (!h->filtered) + return nd; + + nd = rb_next(nd); + } + + return NULL; +} + +static struct rb_node *hists__filter_prev_entries(struct rb_node *nd) +{ + while (nd != NULL) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + if (!h->filtered) + return nd; + + nd = rb_prev(nd); + } + + return NULL; +} + +static void ui_browser__hists_seek(struct ui_browser *self, + off_t offset, int whence) +{ + struct hist_entry *h; + struct rb_node *nd; + bool first = true; + + switch (whence) { + case SEEK_SET: + nd = hists__filter_entries(rb_first(self->entries)); + break; + case SEEK_CUR: + nd = self->first_visible_entry; + goto do_offset; + case SEEK_END: + nd = hists__filter_prev_entries(rb_last(self->entries)); + first = false; + break; + default: + return; + } + + /* + * Moves not relative to the first visible entry invalidates its + * row_offset: + */ + h = rb_entry(self->first_visible_entry, struct hist_entry, rb_node); + h->row_offset = 0; + + /* + * Here we have to check if nd is expanded (+), if it is we can't go + * the next top level hist_entry, instead we must compute an offset of + * what _not_ to show and not change the first visible entry. + * + * This offset increments when we are going from top to bottom and + * decreases when we're going from bottom to top. + * + * As we don't have backpointers to the top level in the callchains + * structure, we need to always print the whole hist_entry callchain, + * skipping the first ones that are before the first visible entry + * and stop when we printed enough lines to fill the screen. + */ +do_offset: + if (offset > 0) { + do { + h = rb_entry(nd, struct hist_entry, rb_node); + if (h->ms.unfolded) { + u16 remaining = h->nr_rows - h->row_offset; + if (offset > remaining) { + offset -= remaining; + h->row_offset = 0; + } else { + h->row_offset += offset; + offset = 0; + self->first_visible_entry = nd; + break; + } + } + nd = hists__filter_entries(rb_next(nd)); + if (nd == NULL) + break; + --offset; + self->first_visible_entry = nd; + } while (offset != 0); + } else if (offset < 0) { + while (1) { + h = rb_entry(nd, struct hist_entry, rb_node); + if (h->ms.unfolded) { + if (first) { + if (-offset > h->row_offset) { + offset += h->row_offset; + h->row_offset = 0; + } else { + h->row_offset += offset; + offset = 0; + self->first_visible_entry = nd; + break; + } + } else { + if (-offset > h->nr_rows) { + offset += h->nr_rows; + h->row_offset = 0; + } else { + h->row_offset = h->nr_rows + offset; + offset = 0; + self->first_visible_entry = nd; + break; + } + } + } + + nd = hists__filter_prev_entries(rb_prev(nd)); + if (nd == NULL) + break; + ++offset; + self->first_visible_entry = nd; + if (offset == 0) { + /* + * Last unfiltered hist_entry, check if it is + * unfolded, if it is then we should have + * row_offset at its last entry. + */ + h = rb_entry(nd, struct hist_entry, rb_node); + if (h->ms.unfolded) + h->row_offset = h->nr_rows; + break; + } + first = false; + } + } else { + self->first_visible_entry = nd; + h = rb_entry(nd, struct hist_entry, rb_node); + h->row_offset = 0; + } +} + +static int callchain_node__count_rows_rb_tree(struct callchain_node *self) +{ + 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; + char folded_sign = ' '; /* No children */ + + list_for_each_entry(chain, &child->val, list) { + ++n; + /* We need this because we may not have children */ + folded_sign = callchain_list__folded(chain); + if (folded_sign == '+') + break; + } + + if (folded_sign == '-') /* Have children and they're unfolded */ + n += callchain_node__count_rows_rb_tree(child); + } + + return n; +} + +static int callchain_node__count_rows(struct callchain_node *node) +{ + struct callchain_list *chain; + bool unfolded = false; + int n = 0; + + list_for_each_entry(chain, &node->val, list) { + ++n; + unfolded = chain->ms.unfolded; + } + + if (unfolded) + n += callchain_node__count_rows_rb_tree(node); + + return n; +} + +static int callchain__count_rows(struct rb_root *chain) +{ + 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__count_rows(node); + } + + return n; +} + +static bool hist_browser__toggle_fold(struct hist_browser *self) +{ + if (map_symbol__toggle_fold(self->selection)) { + struct hist_entry *he = self->he_selection; + + hist_entry__init_have_children(he); + self->hists->nr_entries -= he->nr_rows; + + if (he->ms.unfolded) + he->nr_rows = callchain__count_rows(&he->sorted_chain); + else + he->nr_rows = 0; + self->hists->nr_entries += he->nr_rows; + self->b.nr_entries = self->hists->nr_entries; + + return true; + } + + /* If it doesn't have children, no toggling performed */ + return false; +} + +static int hist_browser__run(struct hist_browser *self, const char *title, + struct newtExitStruct *es) +{ + char str[256], unit; + unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE]; + + 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) < 0) + return -1; + + newtFormAddHotKey(self->b.form, 'A'); + newtFormAddHotKey(self->b.form, 'a'); + newtFormAddHotKey(self->b.form, '?'); + newtFormAddHotKey(self->b.form, 'h'); + newtFormAddHotKey(self->b.form, 'H'); + newtFormAddHotKey(self->b.form, 'd'); + + newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); + newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); + newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); + + while (1) { + ui_browser__run(&self->b, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + switch (es->u.key) { + case 'd': { /* Debug */ + static int seq; + struct hist_entry *h = rb_entry(self->b.first_visible_entry, + struct hist_entry, rb_node); + ui_helpline__pop(); + ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", + seq++, self->b.nr_entries, + self->hists->nr_entries, + self->b.height, + self->b.index, + self->b.first_visible_entry_idx, + h->row_offset, h->nr_rows); + } + continue; + case NEWT_KEY_ENTER: + if (hist_browser__toggle_fold(self)) + break; + /* fall thru */ + default: + return 0; + } + } + return 0; +} diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 03a1722e6d02..46e531d09e8b 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -38,6 +38,12 @@ extern struct sort_entry sort_sym; extern struct sort_entry sort_parent; extern enum sort_type sort__first_dimension; +/** + * struct hist_entry - histogram entry + * + * @row_offset - offset from the first callchain expanded to appear on screen + * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding + */ struct hist_entry { struct rb_node rb_node; u64 period; @@ -50,6 +56,12 @@ struct hist_entry { u64 ip; s32 cpu; u32 nr_events; + + /* XXX These two should move to some tree widget lib */ + u16 row_offset; + u16 nr_rows; + + bool init_have_children; char level; u8 filtered; struct symbol *parent; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index d436ee3d3a73..bb1aab9fa34f 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -102,6 +102,8 @@ struct ref_reloc_sym { struct map_symbol { struct map *map; struct symbol *sym; + bool unfolded; + bool has_children; }; struct addr_location { -- cgit v1.2.3 From 73ae8f85fda49410a59d7b532ce69a0b811ef6d5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 30 Jul 2010 10:06:06 -0300 Subject: perf tui: Make CTRL+Z suspend perf Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/newt.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'tools/perf/util/newt.c') diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 28f74eb8fe7c..91de99b58445 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -11,6 +11,7 @@ #define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG #endif #include +#include #include #include #include @@ -891,6 +892,13 @@ static struct newtPercentTreeColors { "blue", "lightgray", }; +static void newt_suspend(void *d __used) +{ + newtSuspend(); + raise(SIGTSTP); + newtResume(); +} + void setup_browser(void) { struct newtPercentTreeColors *c = &defaultPercentTreeColors; @@ -904,6 +912,7 @@ void setup_browser(void) use_browser = 1; newtInit(); newtCls(); + newtSetSuspendCallback(newt_suspend, NULL); ui_helpline__puts(" "); sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); -- cgit v1.2.3