]> bbs.cooldavid.org Git - net-next-2.6.git/commitdiff
Merge branch 'perf/core' into perf/urgent
authorIngo Molnar <mingo@elte.hu>
Wed, 11 Aug 2010 07:39:10 +0000 (09:39 +0200)
committerIngo Molnar <mingo@elte.hu>
Wed, 11 Aug 2010 07:39:10 +0000 (09:39 +0200)
22 files changed:
tools/perf/Makefile
tools/perf/builtin-annotate.c
tools/perf/util/debug.c
tools/perf/util/debug.h
tools/perf/util/hist.c
tools/perf/util/hist.h
tools/perf/util/pstack.h
tools/perf/util/symbol.c
tools/perf/util/ui/browser.c
tools/perf/util/ui/browser.h
tools/perf/util/ui/browsers/annotate.c [new file with mode: 0644]
tools/perf/util/ui/browsers/hists.c [moved from tools/perf/util/newt.c with 61% similarity]
tools/perf/util/ui/browsers/map.c [new file with mode: 0644]
tools/perf/util/ui/browsers/map.h [new file with mode: 0644]
tools/perf/util/ui/helpline.c [new file with mode: 0644]
tools/perf/util/ui/helpline.h [new file with mode: 0644]
tools/perf/util/ui/libslang.h [new file with mode: 0644]
tools/perf/util/ui/progress.c [new file with mode: 0644]
tools/perf/util/ui/progress.h [new file with mode: 0644]
tools/perf/util/ui/setup.c [new file with mode: 0644]
tools/perf/util/ui/util.c [new file with mode: 0644]
tools/perf/util/ui/util.h [new file with mode: 0644]

index d5bce768b4bf8013a01610c208aa46c90d37f4da..41abb90df50d1fdf97a062e5be387613c7131ee6 100644 (file)
@@ -158,7 +158,7 @@ all::
 # Define NO_DWARF if you do not want debug-info analysis feature at all.
 
 $(shell sh -c 'mkdir -p $(OUTPUT)scripts/{perl,python}/Perf-Trace-Util/' 2> /dev/null)
-$(shell sh -c 'mkdir -p $(OUTPUT)util/{ui,scripting-engines}/' 2> /dev/null)
+$(shell sh -c 'mkdir -p $(OUTPUT)util/{ui/browsers,scripting-engines}/' 2> /dev/null)
 $(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null)
 
 $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
@@ -567,9 +567,20 @@ else
                # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h
                BASIC_CFLAGS += -I/usr/include/slang
                EXTLIBS += -lnewt -lslang
-               LIB_OBJS += $(OUTPUT)util/newt.o
+               LIB_OBJS += $(OUTPUT)util/ui/setup.o
                LIB_OBJS += $(OUTPUT)util/ui/browser.o
+               LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o
+               LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o
+               LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o
+               LIB_OBJS += $(OUTPUT)util/ui/helpline.o
+               LIB_OBJS += $(OUTPUT)util/ui/progress.o
+               LIB_OBJS += $(OUTPUT)util/ui/util.o
                LIB_H += util/ui/browser.h
+               LIB_H += util/ui/browsers/map.h
+               LIB_H += util/ui/helpline.h
+               LIB_H += util/ui/libslang.h
+               LIB_H += util/ui/progress.h
+               LIB_H += util/ui/util.h
        endif
 endif
 
@@ -967,10 +978,16 @@ $(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS
 $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
 
-$(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS
+$(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
 
-$(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS
+$(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS
+       $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
+
+$(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
+       $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
+
+$(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
 
 $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
index fd20670ce986bae016759aa61bd01c272bd7f47f..1478dc64bf157fc0f226cba51eef80ec8646c15f 100644 (file)
@@ -285,7 +285,7 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
        LIST_HEAD(head);
        struct objdump_line *pos, *n;
 
-       if (hist_entry__annotate(he, &head) < 0)
+       if (hist_entry__annotate(he, &head, 0) < 0)
                return -1;
 
        if (full_paths)
index 318dab15d17732a709ab7b72b8db03fb6e08b24f..f9c7e3ad1aa715db6d562a45029a747800c20d6d 100644 (file)
@@ -23,7 +23,7 @@ int eprintf(int level, const char *fmt, ...)
        if (verbose >= level) {
                va_start(args, fmt);
                if (use_browser > 0)
-                       ret = browser__show_help(fmt, args);
+                       ret = ui_helpline__show_help(fmt, args);
                else
                        ret = vfprintf(stderr, fmt, args);
                va_end(args);
index 047ac3324ebe0948aefddac341f6994f1f896cce..7a17ee061bcbd4c80ed42084e12bc61b834fa8b3 100644 (file)
@@ -14,7 +14,7 @@ void trace_event(event_t *event);
 struct ui_progress;
 
 #ifdef NO_NEWT_SUPPORT
-static inline int browser__show_help(const char *format __used, va_list ap __used)
+static inline int ui_helpline__show_help(const char *format __used, va_list ap __used)
 {
        return 0;
 }
@@ -30,10 +30,9 @@ static inline void ui_progress__update(struct ui_progress *self __used,
 
 static inline void ui_progress__delete(struct ui_progress *self __used) {}
 #else
-int browser__show_help(const char *format, va_list ap);
-struct ui_progress *ui_progress__new(const char *title, u64 total);
-void ui_progress__update(struct ui_progress *self, u64 curr);
-void ui_progress__delete(struct ui_progress *self);
+extern char ui_helpline__last_msg[];
+int ui_helpline__show_help(const char *format, va_list ap);
+#include "ui/progress.h"
 #endif
 
 #endif /* __PERF_DEBUG_H */
index 62ec9b0e4b9a32f770fe8c8611d61b605e4514b2..be22ae6ef0558009c0a1faaa4f55bcf2c5d828ca 100644 (file)
@@ -983,9 +983,9 @@ int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip)
        return 0;
 }
 
-static struct objdump_line *objdump_line__new(s64 offset, char *line)
+static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
 {
-       struct objdump_line *self = malloc(sizeof(*self));
+       struct objdump_line *self = malloc(sizeof(*self) + privsize);
 
        if (self != NULL) {
                self->offset = offset;
@@ -1017,7 +1017,7 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
 }
 
 static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
-                                         struct list_head *head)
+                                         struct list_head *head, size_t privsize)
 {
        struct symbol *sym = self->ms.sym;
        struct objdump_line *objdump_line;
@@ -1068,7 +1068,7 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
                        offset = -1;
        }
 
-       objdump_line = objdump_line__new(offset, line);
+       objdump_line = objdump_line__new(offset, line, privsize);
        if (objdump_line == NULL) {
                free(line);
                return -1;
@@ -1078,7 +1078,8 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
        return 0;
 }
 
-int hist_entry__annotate(struct hist_entry *self, struct list_head *head)
+int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
+                        size_t privsize)
 {
        struct symbol *sym = self->ms.sym;
        struct map *map = self->ms.map;
@@ -1143,7 +1144,7 @@ fallback:
                goto out_free_filename;
 
        while (!feof(file))
-               if (hist_entry__parse_objdump_line(self, file, head) < 0)
+               if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0)
                        break;
 
        pclose(file);
index 65a48db46a29f0b0766f43474271f6e935b3c952..587d375d34300daa09948c1a0d95407b4f8439fa 100644 (file)
@@ -101,7 +101,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
                      bool show_displacement, FILE *fp);
 
 int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip);
-int hist_entry__annotate(struct hist_entry *self, struct list_head *head);
+int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
+                        size_t privsize);
 
 void hists__filter_by_dso(struct hists *self, const struct dso *dso);
 void hists__filter_by_thread(struct hists *self, const struct thread *thread);
index 5ad07023504bb834e6334bbc5628e269faaa21ce..4cedea59f518ce7c1fcb61b0368598453bdf3a63 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef _PERF_PSTACK_
 #define _PERF_PSTACK_
 
+#include <stdbool.h>
+
 struct pstack;
 struct pstack *pstack__new(unsigned short max_nr_entries);
 void pstack__delete(struct pstack *self);
index b6f5970f910687702619586633a185e0393129d4..1a367734e01693c8a93f79bb3846af4a89f9cffe 100644 (file)
@@ -1079,6 +1079,16 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
                if (!is_label && !elf_sym__is_a(&sym, map->type))
                        continue;
 
+               /* Reject ARM ELF "mapping symbols": these aren't unique and
+                * don't identify functions, so will confuse the profile
+                * output: */
+               if (ehdr.e_machine == EM_ARM) {
+                       if (!strcmp(elf_name, "$a") ||
+                           !strcmp(elf_name, "$d") ||
+                           !strcmp(elf_name, "$t"))
+                               continue;
+               }
+
                if (opdsec && sym.st_shndx == opdidx) {
                        u32 offset = sym.st_value - opdshdr.sh_addr;
                        u64 *opd = opddata->d_buf + offset;
index 0b2b9306312d9a254f8d23b978697a12a30f4594..66f2d583d8c4326971dc9d37cd47df57eeb82306 100644 (file)
@@ -16,6 +16,7 @@
 #include <stdlib.h>
 #include <sys/ttydefaults.h>
 #include "browser.h"
+#include "helpline.h"
 #include "../color.h"
 #include "../util.h"
 
@@ -49,7 +50,7 @@ void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whenc
                pos = head->next;
                break;
        case SEEK_CUR:
-               pos = self->first_visible_entry;
+               pos = self->top;
                break;
        case SEEK_END:
                pos = head->prev;
@@ -66,7 +67,7 @@ void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whenc
                        pos = pos->prev;
        }
 
-       self->first_visible_entry = pos;
+       self->top = pos;
 }
 
 void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
@@ -79,7 +80,7 @@ void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
                nd = rb_first(root);
                break;
        case SEEK_CUR:
-               nd = self->first_visible_entry;
+               nd = self->top;
                break;
        case SEEK_END:
                nd = rb_last(root);
@@ -96,7 +97,7 @@ void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
                        nd = rb_prev(nd);
        }
 
-       self->first_visible_entry = nd;
+       self->top = nd;
 }
 
 unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
@@ -104,13 +105,13 @@ unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
        struct rb_node *nd;
        int row = 0;
 
-       if (self->first_visible_entry == NULL)
-                self->first_visible_entry = rb_first(self->entries);
+       if (self->top == NULL)
+                self->top = rb_first(self->entries);
 
-       nd = self->first_visible_entry;
+       nd = self->top;
 
        while (nd != NULL) {
-               SLsmg_gotorc(self->top + row, self->left);
+               SLsmg_gotorc(self->y + row, self->x);
                self->write(self, nd, row);
                if (++row == self->height)
                        break;
@@ -122,7 +123,7 @@ unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
 
 bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
 {
-       return (self->first_visible_entry_idx + row) == self->index;
+       return self->top_idx + row == self->index;
 }
 
 void ui_browser__refresh_dimensions(struct ui_browser *self)
@@ -135,18 +136,21 @@ void ui_browser__refresh_dimensions(struct ui_browser *self)
        self->height = rows - 5;
        if (self->height > self->nr_entries)
                self->height = self->nr_entries;
-       self->top  = (rows - self->height) / 2;
-       self->left = (cols - self->width) / 2;
+       self->y  = (rows - self->height) / 2;
+       self->x = (cols - self->width) / 2;
 }
 
 void ui_browser__reset_index(struct ui_browser *self)
 {
-       self->index = self->first_visible_entry_idx = 0;
+       self->index = self->top_idx = 0;
        self->seek(self, 0, SEEK_SET);
 }
 
-int ui_browser__show(struct ui_browser *self, const char *title)
+int ui_browser__show(struct ui_browser *self, const char *title,
+                    const char *helpline, ...)
 {
+       va_list ap;
+
        if (self->form != NULL) {
                newtFormDestroy(self->form);
                newtPopWindow();
@@ -169,10 +173,23 @@ int ui_browser__show(struct ui_browser *self, const char *title)
        newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
        newtFormAddHotKey(self->form, NEWT_KEY_HOME);
        newtFormAddHotKey(self->form, NEWT_KEY_END);
+       newtFormAddHotKey(self->form, ' ');
        newtFormAddComponent(self->form, self->sb);
+
+       va_start(ap, helpline);
+       ui_helpline__vpush(helpline, ap);
+       va_end(ap);
        return 0;
 }
 
+void ui_browser__hide(struct ui_browser *self)
+{
+       newtFormDestroy(self->form);
+       newtPopWindow();
+       self->form = NULL;
+       ui_helpline__pop();
+}
+
 int ui_browser__refresh(struct ui_browser *self)
 {
        int row;
@@ -180,7 +197,7 @@ int ui_browser__refresh(struct ui_browser *self)
        newtScrollbarSet(self->sb, self->index, self->nr_entries - 1);
        row = self->refresh(self);
        SLsmg_set_color(HE_COLORSET_NORMAL);
-       SLsmg_fill_region(self->top + row, self->left,
+       SLsmg_fill_region(self->y + row, self->x,
                          self->height - row, self->width, ' ');
 
        return 0;
@@ -205,8 +222,8 @@ int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
                        if (self->index == self->nr_entries - 1)
                                break;
                        ++self->index;
-                       if (self->index == self->first_visible_entry_idx + self->height) {
-                               ++self->first_visible_entry_idx;
+                       if (self->index == self->top_idx + self->height) {
+                               ++self->top_idx;
                                self->seek(self, +1, SEEK_CUR);
                        }
                        break;
@@ -214,34 +231,34 @@ int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
                        if (self->index == 0)
                                break;
                        --self->index;
-                       if (self->index < self->first_visible_entry_idx) {
-                               --self->first_visible_entry_idx;
+                       if (self->index < self->top_idx) {
+                               --self->top_idx;
                                self->seek(self, -1, SEEK_CUR);
                        }
                        break;
                case NEWT_KEY_PGDN:
                case ' ':
-                       if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
+                       if (self->top_idx + self->height > self->nr_entries - 1)
                                break;
 
                        offset = self->height;
                        if (self->index + offset > self->nr_entries - 1)
                                offset = self->nr_entries - 1 - self->index;
                        self->index += offset;
-                       self->first_visible_entry_idx += offset;
+                       self->top_idx += offset;
                        self->seek(self, +offset, SEEK_CUR);
                        break;
                case NEWT_KEY_PGUP:
-                       if (self->first_visible_entry_idx == 0)
+                       if (self->top_idx == 0)
                                break;
 
-                       if (self->first_visible_entry_idx < self->height)
-                               offset = self->first_visible_entry_idx;
+                       if (self->top_idx < self->height)
+                               offset = self->top_idx;
                        else
                                offset = self->height;
 
                        self->index -= offset;
-                       self->first_visible_entry_idx -= offset;
+                       self->top_idx -= offset;
                        self->seek(self, -offset, SEEK_CUR);
                        break;
                case NEWT_KEY_HOME:
@@ -253,7 +270,7 @@ int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
                                offset = self->nr_entries - 1;
 
                        self->index = self->nr_entries - 1;
-                       self->first_visible_entry_idx = self->index - offset;
+                       self->top_idx = self->index - offset;
                        self->seek(self, -offset, SEEK_END);
                        break;
                default:
@@ -271,14 +288,13 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
        struct list_head *head = self->entries;
        int row = 0;
 
-       if (self->first_visible_entry == NULL ||
-           self->first_visible_entry == self->entries)
-                self->first_visible_entry = head->next;
+       if (self->top == NULL || self->top == self->entries)
+                self->top = head->next;
 
-       pos = self->first_visible_entry;
+       pos = self->top;
 
        list_for_each_from(pos, head) {
-               SLsmg_gotorc(self->top + row, self->left);
+               SLsmg_gotorc(self->y + row, self->x);
                self->write(self, pos, row);
                if (++row == self->height)
                        break;
index bcc4391405bded1da6a73c944446b26b72998dcc..0b9f829214f756ec16227745835cfea55d7ee503 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <stdbool.h>
 #include <newt.h>
+#include <sys/types.h>
 #include "../types.h"
 
 #define HE_COLORSET_TOP                50
@@ -13,9 +14,9 @@
 
 struct ui_browser {
        newtComponent form, sb;
-       u64           index, first_visible_entry_idx;
-       void          *first_visible_entry, *entries;
-       u16           top, left, width, height;
+       u64           index, top_idx;
+       void          *top, *entries;
+       u16           y, x, width, height;
        void          *priv;
        unsigned int  (*refresh)(struct ui_browser *self);
        void          (*write)(struct ui_browser *self, void *entry, int row);
@@ -29,7 +30,9 @@ bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row);
 void ui_browser__refresh_dimensions(struct ui_browser *self);
 void ui_browser__reset_index(struct ui_browser *self);
 
-int ui_browser__show(struct ui_browser *self, const char *title);
+int ui_browser__show(struct ui_browser *self, const char *title,
+                    const char *helpline, ...);
+void ui_browser__hide(struct ui_browser *self);
 int ui_browser__refresh(struct ui_browser *self);
 int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es);
 
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c
new file mode 100644 (file)
index 0000000..55ff792
--- /dev/null
@@ -0,0 +1,240 @@
+#include "../browser.h"
+#include "../helpline.h"
+#include "../libslang.h"
+#include "../../hist.h"
+#include "../../sort.h"
+#include "../../symbol.h"
+
+static void ui__error_window(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
+       va_end(ap);
+}
+
+struct annotate_browser {
+       struct ui_browser b;
+       struct rb_root    entries;
+       struct rb_node    *curr_hot;
+};
+
+struct objdump_line_rb_node {
+       struct rb_node  rb_node;
+       double          percent;
+       u32             idx;
+};
+
+static inline
+struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
+{
+       return (struct objdump_line_rb_node *)(self + 1);
+}
+
+static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
+{
+       struct objdump_line *ol = rb_entry(entry, struct objdump_line, node);
+       bool current_entry = ui_browser__is_current_entry(self, row);
+       int width = self->width;
+
+       if (ol->offset != -1) {
+               struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
+               int color = ui_browser__percent_color(olrb->percent, current_entry);
+               SLsmg_set_color(color);
+               slsmg_printf(" %7.2f ", olrb->percent);
+               if (!current_entry)
+                       SLsmg_set_color(HE_COLORSET_CODE);
+       } else {
+               int color = ui_browser__percent_color(0, current_entry);
+               SLsmg_set_color(color);
+               slsmg_write_nstring(" ", 9);
+       }
+
+       SLsmg_write_char(':');
+       slsmg_write_nstring(" ", 8);
+       if (!*ol->line)
+               slsmg_write_nstring(" ", width - 18);
+       else
+               slsmg_write_nstring(ol->line, width - 18);
+}
+
+static double objdump_line__calc_percent(struct objdump_line *self,
+                                        struct list_head *head,
+                                        struct symbol *sym)
+{
+       double percent = 0.0;
+
+       if (self->offset != -1) {
+               int len = sym->end - sym->start;
+               unsigned int hits = 0;
+               struct sym_priv *priv = symbol__priv(sym);
+               struct sym_ext *sym_ext = priv->ext;
+               struct sym_hist *h = priv->hist;
+               s64 offset = self->offset;
+               struct objdump_line *next = objdump__get_next_ip_line(head, self);
+
+
+               while (offset < (s64)len &&
+                      (next == NULL || offset < next->offset)) {
+                       if (sym_ext) {
+                               percent += sym_ext[offset].percent;
+                       } else
+                               hits += h->ip[offset];
+
+                       ++offset;
+               }
+
+               if (sym_ext == NULL && h->sum)
+                       percent = 100.0 * hits / h->sum;
+       }
+
+       return percent;
+}
+
+static void objdump__insert_line(struct rb_root *self,
+                                struct objdump_line_rb_node *line)
+{
+       struct rb_node **p = &self->rb_node;
+       struct rb_node *parent = NULL;
+       struct objdump_line_rb_node *l;
+
+       while (*p != NULL) {
+               parent = *p;
+               l = rb_entry(parent, struct objdump_line_rb_node, rb_node);
+               if (line->percent < l->percent)
+                       p = &(*p)->rb_left;
+               else
+                       p = &(*p)->rb_right;
+       }
+       rb_link_node(&line->rb_node, parent, p);
+       rb_insert_color(&line->rb_node, self);
+}
+
+static void annotate_browser__set_top(struct annotate_browser *self,
+                                     struct rb_node *nd)
+{
+       struct objdump_line_rb_node *rbpos;
+       struct objdump_line *pos;
+       unsigned back;
+
+       ui_browser__refresh_dimensions(&self->b);
+       back = self->b.height / 2;
+       rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
+       pos = ((struct objdump_line *)rbpos) - 1;
+       self->b.top_idx = self->b.index = rbpos->idx;
+
+       while (self->b.top_idx != 0 && back != 0) {
+               pos = list_entry(pos->node.prev, struct objdump_line, node);
+
+               --self->b.top_idx;
+               --back;
+       }
+
+       self->b.top = pos;
+       self->curr_hot = nd;
+}
+
+static int annotate_browser__run(struct annotate_browser *self,
+                                struct newtExitStruct *es)
+{
+       struct rb_node *nd;
+       struct hist_entry *he = self->b.priv;
+
+       if (ui_browser__show(&self->b, he->ms.sym->name,
+                            "<- or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0)
+               return -1;
+
+       newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
+
+       nd = self->curr_hot;
+       if (nd) {
+               newtFormAddHotKey(self->b.form, NEWT_KEY_TAB);
+               newtFormAddHotKey(self->b.form, NEWT_KEY_UNTAB);
+       }
+
+       while (1) {
+               ui_browser__run(&self->b, es);
+
+               if (es->reason != NEWT_EXIT_HOTKEY)
+                       break;
+
+               switch (es->u.key) {
+               case NEWT_KEY_TAB:
+                       nd = rb_prev(nd);
+                       if (nd == NULL)
+                               nd = rb_last(&self->entries);
+                       annotate_browser__set_top(self, nd);
+                       break;
+               case NEWT_KEY_UNTAB:
+                       nd = rb_next(nd);
+                       if (nd == NULL)
+                               nd = rb_first(&self->entries);
+                       annotate_browser__set_top(self, nd);
+                       break;
+               default:
+                       goto out;
+               }
+       }
+out:
+       ui_browser__hide(&self->b);
+       return 0;
+}
+
+int hist_entry__tui_annotate(struct hist_entry *self)
+{
+       struct newtExitStruct es;
+       struct objdump_line *pos, *n;
+       struct objdump_line_rb_node *rbpos;
+       LIST_HEAD(head);
+       struct annotate_browser browser = {
+               .b = {
+                       .entries = &head,
+                       .refresh = ui_browser__list_head_refresh,
+                       .seek    = ui_browser__list_head_seek,
+                       .write   = annotate_browser__write,
+                       .priv    = self,
+               },
+       };
+       int ret;
+
+       if (self->ms.sym == NULL)
+               return -1;
+
+       if (self->ms.map->dso->annotate_warned)
+               return -1;
+
+       if (hist_entry__annotate(self, &head, sizeof(*rbpos)) < 0) {
+               ui__error_window(ui_helpline__last_msg);
+               return -1;
+       }
+
+       ui_helpline__push("Press <- or ESC to exit");
+
+       list_for_each_entry(pos, &head, node) {
+               size_t line_len = strlen(pos->line);
+               if (browser.b.width < line_len)
+                       browser.b.width = line_len;
+               rbpos = objdump_line__rb(pos);
+               rbpos->idx = browser.b.nr_entries++;
+               rbpos->percent = objdump_line__calc_percent(pos, &head, self->ms.sym);
+               if (rbpos->percent < 0.01)
+                       continue;
+               objdump__insert_line(&browser.entries, rbpos);
+       }
+
+       /*
+        * Position the browser at the hottest line.
+        */
+       browser.curr_hot = rb_last(&browser.entries);
+       if (browser.curr_hot)
+               annotate_browser__set_top(&browser, browser.curr_hot);
+
+       browser.b.width += 18; /* Percentage */
+       ret = annotate_browser__run(&browser, &es);
+       list_for_each_entry_safe(pos, n, &head, node) {
+               list_del(&pos->node);
+               objdump_line__free(pos);
+       }
+       return ret;
+}
similarity index 61%
rename from tools/perf/util/newt.c
rename to tools/perf/util/ui/browsers/hists.c
index 266a9e06525b1f5be511e5ea023bdc4c578478c4..dafdf6775d77f44d69abf1980b1a9cfe4ab053dc 100644 (file)
 #define _GNU_SOURCE
 #include <stdio.h>
 #undef _GNU_SOURCE
-/*
- * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
- * the build if it isn't defined. Use the equivalent one that glibc
- * has on features.h.
- */
-#include <features.h>
-#ifndef HAVE_LONG_LONG
-#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
-#endif
-#include <slang.h>
-#include <signal.h>
+#include "../libslang.h"
 #include <stdlib.h>
-#include <elf.h>
+#include <string.h>
 #include <newt.h>
-#include <sys/ttydefaults.h>
-
-#include "cache.h"
-#include "hist.h"
-#include "pstack.h"
-#include "session.h"
-#include "sort.h"
-#include "symbol.h"
-#include "ui/browser.h"
-
-#if SLANG_VERSION < 20104
-#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
-#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
-#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
-                                                        (char *)fg, (char *)bg)
-#else
-#define slsmg_printf SLsmg_printf
-#define slsmg_write_nstring SLsmg_write_nstring
-#define sltt_set_color SLtt_set_color
-#endif
-
-newtComponent newt_form__new(void);
-
-struct ui_progress {
-       newtComponent form, scale;
-};
-
-struct ui_progress *ui_progress__new(const char *title, u64 total)
-{
-       struct ui_progress *self = malloc(sizeof(*self));
-
-       if (self != NULL) {
-               int cols;
-
-               if (use_browser <= 0)   
-                       return self;
-               newtGetScreenSize(&cols, NULL);
-               cols -= 4;
-               newtCenteredWindow(cols, 1, title);
-               self->form  = newtForm(NULL, NULL, 0);
-               if (self->form == NULL)
-                       goto out_free_self;
-               self->scale = newtScale(0, 0, cols, total);
-               if (self->scale == NULL)
-                       goto out_free_form;
-               newtFormAddComponent(self->form, self->scale);
-               newtRefresh();
-       }
+#include <linux/rbtree.h>
 
-       return self;
+#include "../../hist.h"
+#include "../../pstack.h"
+#include "../../sort.h"
+#include "../../util.h"
 
-out_free_form:
-       newtFormDestroy(self->form);
-out_free_self:
-       free(self);
-       return NULL;
-}
+#include "../browser.h"
+#include "../helpline.h"
+#include "../util.h"
+#include "map.h"
 
-void ui_progress__update(struct ui_progress *self, u64 curr)
-{
-       /*
-        * FIXME: We should have a per UI backend way of showing progress,
-        * stdio will just show a percentage as NN%, etc.
-        */
-       if (use_browser <= 0)
-               return;
-       newtScaleSet(self->scale, curr);
-       newtRefresh();
-}
+struct hist_browser {
+       struct ui_browser   b;
+       struct hists        *hists;
+       struct hist_entry   *he_selection;
+       struct map_symbol   *selection;
+};
 
-void ui_progress__delete(struct ui_progress *self)
+static void hist_browser__refresh_dimensions(struct hist_browser *self)
 {
-       if (use_browser > 0) {
-               newtFormDestroy(self->form);
-               newtPopWindow();
-       }
-       free(self);
+       /* 3 == +/- toggle symbol before actual hist_entry rendering */
+       self->b.width = 3 + (hists__sort_list_width(self->hists) +
+                            sizeof("[k]"));
 }
 
-static void ui_helpline__pop(void)
+static void hist_browser__reset(struct hist_browser *self)
 {
-       newtPopHelpLine();
+       self->b.nr_entries = self->hists->nr_entries;
+       hist_browser__refresh_dimensions(self);
+       ui_browser__reset_index(&self->b);
 }
 
-static void ui_helpline__push(const char *msg)
+static char tree__folded_sign(bool unfolded)
 {
-       newtPushHelpLine(msg);
+       return unfolded ? '-' : '+';
 }
 
-static void ui_helpline__vpush(const char *fmt, va_list ap)
+static char map_symbol__folded(const struct map_symbol *self)
 {
-       char *s;
-
-       if (vasprintf(&s, fmt, ap) < 0)
-               vfprintf(stderr, fmt, ap);
-       else {
-               ui_helpline__push(s);
-               free(s);
-       }
+       return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
 }
 
-static void ui_helpline__fpush(const char *fmt, ...)
+static char hist_entry__folded(const struct hist_entry *self)
 {
-       va_list ap;
-
-       va_start(ap, fmt);
-       ui_helpline__vpush(fmt, ap);
-       va_end(ap);
+       return map_symbol__folded(&self->ms);
 }
 
-static void ui_helpline__puts(const char *msg)
+static char callchain_list__folded(const struct callchain_list *self)
 {
-       ui_helpline__pop();
-       ui_helpline__push(msg);
+       return map_symbol__folded(&self->ms);
 }
 
-static int ui_entry__read(const char *title, char *bf, size_t size, int width)
+static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
 {
-       struct newtExitStruct es;
-       newtComponent form, entry;
-       const char *result;
-       int err = -1;
-
-       newtCenteredWindow(width, 1, title);
-       form = newtForm(NULL, NULL, 0);
-       if (form == NULL)
-               return -1;
+       int n = 0;
+       struct rb_node *nd;
 
-       entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
-       if (entry == NULL)
-               goto out_free_form;
+       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 */
 
-       newtFormAddComponent(form, entry);
-       newtFormAddHotKey(form, NEWT_KEY_ENTER);
-       newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
-       newtFormAddHotKey(form, NEWT_KEY_LEFT);
-       newtFormAddHotKey(form, CTRL('c'));
-       newtFormRun(form, &es);
+               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 (result != NULL) {
-               strncpy(bf, result, size);
-               err = 0;
+               if (folded_sign == '-') /* Have children and they're unfolded */
+                       n += callchain_node__count_rows_rb_tree(child);
        }
-out_free_form:
-       newtPopWindow();
-       newtFormDestroy(form);
-       return 0;
-}
 
-static char browser__last_msg[1024];
+       return n;
+}
 
-int browser__show_help(const char *format, va_list ap)
+static int callchain_node__count_rows(struct callchain_node *node)
 {
-       int ret;
-       static int backlog;
-
-        ret = vsnprintf(browser__last_msg + backlog,
-                       sizeof(browser__last_msg) - backlog, format, ap);
-       backlog += ret;
+       struct callchain_list *chain;
+       bool unfolded = false;
+       int n = 0;
 
-       if (browser__last_msg[backlog - 1] == '\n') {
-               ui_helpline__puts(browser__last_msg);
-               newtRefresh();
-               backlog = 0;
+       list_for_each_entry(chain, &node->val, list) {
+               ++n;
+               unfolded = chain->ms.unfolded;
        }
 
-       return ret;
-}
-
-static void newt_form__set_exit_keys(newtComponent self)
-{
-       newtFormAddHotKey(self, NEWT_KEY_LEFT);
-       newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
-       newtFormAddHotKey(self, 'Q');
-       newtFormAddHotKey(self, 'q');
-       newtFormAddHotKey(self, CTRL('c'));
-}
+       if (unfolded)
+               n += callchain_node__count_rows_rb_tree(node);
 
-newtComponent newt_form__new(void)
-{
-       newtComponent self = newtForm(NULL, NULL, 0);
-       if (self)
-               newt_form__set_exit_keys(self);
-       return self;
+       return n;
 }
 
-static int popup_menu(int argc, char * const argv[])
+static int callchain__count_rows(struct rb_root *chain)
 {
-       struct newtExitStruct es;
-       int i, rc = -1, max_len = 5;
-       newtComponent listbox, form = newt_form__new();
-
-       if (form == NULL)
-               return -1;
-
-       listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
-       if (listbox == NULL)
-               goto out_destroy_form;
-
-       newtFormAddComponent(form, listbox);
+       struct rb_node *nd;
+       int n = 0;
 
-       for (i = 0; i < argc; ++i) {
-               int len = strlen(argv[i]);
-               if (len > max_len)
-                       max_len = len;
-               if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
-                       goto out_destroy_form;
+       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);
        }
 
-       newtCenteredWindow(max_len, argc, NULL);
-       newtFormRun(form, &es);
-       rc = newtListboxGetCurrent(listbox) - NULL;
-       if (es.reason == NEWT_EXIT_HOTKEY)
-               rc = -1;
-       newtPopWindow();
-out_destroy_form:
-       newtFormDestroy(form);
-       return rc;
+       return n;
 }
 
-static int ui__help_window(const char *text)
+static bool map_symbol__toggle_fold(struct map_symbol *self)
 {
-       struct newtExitStruct es;
-       newtComponent tb, form = newt_form__new();
-       int rc = -1;
-       int max_len = 0, nr_lines = 0;
-       const char *t;
-
-       if (form == NULL)
-               return -1;
-
-       t = text;
-       while (1) {
-               const char *sep = strchr(t, '\n');
-               int len;
-
-               if (sep == NULL)
-                       sep = strchr(t, '\0');
-               len = sep - t;
-               if (max_len < len)
-                       max_len = len;
-               ++nr_lines;
-               if (*sep == '\0')
-                       break;
-               t = sep + 1;
-       }
-
-       tb = newtTextbox(0, 0, max_len, nr_lines, 0);
-       if (tb == NULL)
-               goto out_destroy_form;
-
-       newtTextboxSetText(tb, text);
-       newtFormAddComponent(form, tb);
-       newtCenteredWindow(max_len, nr_lines, NULL);
-       newtFormRun(form, &es);
-       newtPopWindow();
-       rc = 0;
-out_destroy_form:
-       newtFormDestroy(form);
-       return rc;
-}
+       if (!self->has_children)
+               return false;
 
-static bool dialog_yesno(const char *msg)
-{
-       /* newtWinChoice should really be accepting const char pointers... */
-       char yes[] = "Yes", no[] = "No";
-       return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
+       self->unfolded = !self->unfolded;
+       return true;
 }
 
-static void ui__error_window(const char *fmt, ...)
+static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
 {
-       va_list ap;
+       struct rb_node *nd = rb_first(&self->rb_root);
 
-       va_start(ap, fmt);
-       newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
-       va_end(ap);
-}
+       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;
 
-static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
-{
-       struct objdump_line *ol = rb_entry(entry, struct objdump_line, node);
-       bool current_entry = ui_browser__is_current_entry(self, row);
-       int width = self->width;
-
-       if (ol->offset != -1) {
-               struct hist_entry *he = self->priv;
-               struct symbol *sym = he->ms.sym;
-               int len = he->ms.sym->end - he->ms.sym->start;
-               unsigned int hits = 0;
-               double percent = 0.0;
-               int color;
-               struct sym_priv *priv = symbol__priv(sym);
-               struct sym_ext *sym_ext = priv->ext;
-               struct sym_hist *h = priv->hist;
-               s64 offset = ol->offset;
-               struct objdump_line *next = objdump__get_next_ip_line(self->entries, ol);
-
-               while (offset < (s64)len &&
-                      (next == NULL || offset < next->offset)) {
-                       if (sym_ext) {
-                               percent += sym_ext[offset].percent;
+               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
-                               hits += h->ip[offset];
-
-                       ++offset;
+                               chain->ms.has_children = chain->list.next == &child->val &&
+                                                        rb_first(&child->rb_root) != NULL;
                }
 
-               if (sym_ext == NULL && h->sum)
-                       percent = 100.0 * hits / h->sum;
-
-               color = ui_browser__percent_color(percent, current_entry);
-               SLsmg_set_color(color);
-               slsmg_printf(" %7.2f ", percent);
-               if (!current_entry)
-                       SLsmg_set_color(HE_COLORSET_CODE);
-       } else {
-               int color = ui_browser__percent_color(0, current_entry);
-               SLsmg_set_color(color);
-               slsmg_write_nstring(" ", 9);
+               callchain_node__init_have_children_rb_tree(child);
        }
-
-       SLsmg_write_char(':');
-       slsmg_write_nstring(" ", 8);
-       if (!*ol->line)
-               slsmg_write_nstring(" ", width - 18);
-       else
-               slsmg_write_nstring(ol->line, width - 18);
 }
 
-static char *callchain_list__sym_name(struct callchain_list *self,
-                                     char *bf, size_t bfsize)
+static void callchain_node__init_have_children(struct callchain_node *self)
 {
-       if (self->ms.sym)
-               return self->ms.sym->name;
+       struct callchain_list *chain;
 
-       snprintf(bf, bfsize, "%#Lx", self->ip);
-       return bf;
+       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);
 }
 
-int hist_entry__tui_annotate(struct hist_entry *self)
+static void callchain__init_have_children(struct rb_root *self)
 {
-       struct newtExitStruct es;
-       struct objdump_line *pos, *n;
-       LIST_HEAD(head);
-       struct ui_browser browser = {
-               .entries = &head,
-               .refresh = ui_browser__list_head_refresh,
-               .seek    = ui_browser__list_head_seek,
-               .write   = annotate_browser__write,
-               .priv    = self,
-       };
-       int ret;
-
-       if (self->ms.sym == NULL)
-               return -1;
-
-       if (self->ms.map->dso->annotate_warned)
-               return -1;
-
-       if (hist_entry__annotate(self, &head) < 0) {
-               ui__error_window(browser__last_msg);
-               return -1;
-       }
-
-       ui_helpline__push("Press <- or ESC to exit");
+       struct rb_node *nd;
 
-       list_for_each_entry(pos, &head, node) {
-               size_t line_len = strlen(pos->line);
-               if (browser.width < line_len)
-                       browser.width = line_len;
-               ++browser.nr_entries;
+       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);
        }
+}
 
-       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();
-       list_for_each_entry_safe(pos, n, &head, node) {
-               list_del(&pos->node);
-               objdump_line__free(pos);
+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;
        }
-       ui_helpline__pop();
-       return ret;
 }
 
-/* -------------------------------------------------------------------- */
+static bool hist_browser__toggle_fold(struct hist_browser *self)
+{
+       if (map_symbol__toggle_fold(self->selection)) {
+               struct hist_entry *he = self->he_selection;
 
-struct map_browser {
-       struct ui_browser b;
-       struct map        *map;
-       u16               namelen;
-       u8                addrlen;
-};
+               hist_entry__init_have_children(he);
+               self->hists->nr_entries -= he->nr_rows;
 
-static void map_browser__write(struct ui_browser *self, void *nd, int row)
-{
-       struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
-       struct map_browser *mb = container_of(self, struct map_browser, b);
-       bool current_entry = ui_browser__is_current_entry(self, row);
-       int color = ui_browser__percent_color(0, current_entry);
-
-       SLsmg_set_color(color);
-       slsmg_printf("%*llx %*llx %c ",
-                    mb->addrlen, sym->start, mb->addrlen, sym->end,
-                    sym->binding == STB_GLOBAL ? 'g' :
-                    sym->binding == STB_LOCAL  ? 'l' : 'w');
-       slsmg_write_nstring(sym->name, mb->namelen);
-}
+               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;
 
-/* FIXME uber-kludgy, see comment on cmd_report... */
-static u32 *symbol__browser_index(struct symbol *self)
-{
-       return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
+               return true;
+       }
+
+       /* If it doesn't have children, no toggling performed */
+       return false;
 }
 
-static int map_browser__search(struct map_browser *self)
+static int hist_browser__run(struct hist_browser *self, const char *title,
+                            struct newtExitStruct *es)
 {
-       char target[512];
-       struct symbol *sym;
-       int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
-
-       if (err)
-               return err;
+       char str[256], unit;
+       unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
 
-       if (target[0] == '0' && tolower(target[1]) == 'x') {
-               u64 addr = strtoull(target, NULL, 16);
-               sym = map__find_symbol(self->map, addr, NULL);
-       } else
-               sym = map__find_symbol_by_name(self->map, target, NULL);
+       self->b.entries = &self->hists->entries;
+       self->b.nr_entries = self->hists->nr_entries;
 
-       if (sym != NULL) {
-               u32 *idx = symbol__browser_index(sym);
-                       
-               self->b.first_visible_entry = &sym->rb_node;
-               self->b.index = self->b.first_visible_entry_idx = *idx;
-       } else
-               ui_helpline__fpush("%s not found!", target);
+       hist_browser__refresh_dimensions(self);
 
-       return 0;
-}
+       nr_events = convert_unit(nr_events, &unit);
+       snprintf(str, sizeof(str), "Events: %lu%c                            ",
+                nr_events, unit);
+       newtDrawRootText(0, 0, str);
 
-static int map_browser__run(struct map_browser *self, struct newtExitStruct *es)
-{
-       if (ui_browser__show(&self->b, self->map->dso->long_name) < 0)
+       if (ui_browser__show(&self->b, title,
+                            "Press '?' for help on key bindings") < 0)
                return -1;
 
-       ui_helpline__fpush("Press <- or ESC to exit, %s / to search",
-                          verbose ? "" : "restart with -v to use");
+       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);
-       if (verbose)
-               newtFormAddHotKey(self->b.form, '/');
 
        while (1) {
                ui_browser__run(&self->b, es);
 
                if (es->reason != NEWT_EXIT_HOTKEY)
                        break;
-               if (verbose && es->u.key == '/')
-                       map_browser__search(self);
-               else
-                       break;
+               switch (es->u.key) {
+               case 'D': { /* Debug */
+                       static int seq;
+                       struct hist_entry *h = rb_entry(self->b.top,
+                                                       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.top_idx,
+                                          h->row_offset, h->nr_rows);
+               }
+                       continue;
+               case NEWT_KEY_ENTER:
+                       if (hist_browser__toggle_fold(self))
+                               break;
+                       /* fall thru */
+               default:
+                       return 0;
+               }
        }
 
-       newtFormDestroy(self->b.form);
-       newtPopWindow();
-       ui_helpline__pop();
+       ui_browser__hide(&self->b);
        return 0;
 }
 
-static int map__browse(struct map *self)
+static char *callchain_list__sym_name(struct callchain_list *self,
+                                     char *bf, size_t bfsize)
 {
-       struct map_browser mb = {
-               .b = {
-                       .entries = &self->dso->symbols[self->type],
-                       .refresh = ui_browser__rb_tree_refresh,
-                       .seek    = ui_browser__rb_tree_seek,
-                       .write   = map_browser__write,
-               },
-               .map = self,
-       };
-       struct newtExitStruct es;
-       struct rb_node *nd;
-       char tmp[BITS_PER_LONG / 4];
-       u64 maxaddr = 0;
-
-       for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
-               struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
-
-               if (mb.namelen < pos->namelen)
-                       mb.namelen = pos->namelen;
-               if (maxaddr < pos->end)
-                       maxaddr = pos->end;
-               if (verbose) {
-                       u32 *idx = symbol__browser_index(pos);
-                       *idx = mb.b.nr_entries;
-               }
-               ++mb.b.nr_entries;
-       }
+       if (self->ms.sym)
+               return self->ms.sym->name;
 
-       mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr);
-       mb.b.width += mb.addrlen * 2 + 4 + mb.namelen;
-       return map_browser__run(&mb, &es);
+       snprintf(bf, bfsize, "%#Lx", self->ip);
+       return bf;
 }
 
-/* -------------------------------------------------------------------- */
-
-struct hist_browser {
-       struct ui_browser   b;
-       struct hists        *hists;
-       struct hist_entry   *he_selection;
-       struct map_symbol   *selection;
-};
-
-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(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 = zalloc(sizeof(*self));
-
-       if (self) {
-               self->hists = hists;
-               self->b.refresh = hist_browser__refresh;
-               self->b.seek = ui_browser__hists_seek;
-       }
-
-       return self;
-}
-
-static void hist_browser__delete(struct hist_browser *self)
-{
-       newtFormDestroy(self->b.form);
-       newtPopWindow();
-       free(self);
-}
-
-static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
-{
-       return self->he_selection;
-}
-
-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)
-{
-       int printed = 0;
-
-       if (thread)
-               printed += snprintf(bf + printed, size - printed,
-                                   "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);
-}
-
-int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
-{
-       struct hist_browser *browser = hist_browser__new(self);
-       struct pstack *fstack;
-       const struct thread *thread_filter = NULL;
-       const struct dso *dso_filter = NULL;
-       struct newtExitStruct es;
-       char msg[160];
-       int key = -1;
-
-       if (browser == NULL)
-               return -1;
-
-       fstack = pstack__new(2);
-       if (fstack == NULL)
-               goto out;
-
-       ui_helpline__push(helpline);
-
-       hist_browser__title(msg, sizeof(msg), ev_name,
-                           dso_filter, thread_filter);
-
-       while (1) {
-               const struct thread *thread;
-               const struct dso *dso;
-               char *options[16];
-               int nr_options = 0, choice = 0, i,
-                   annotate = -2, zoom_dso = -2, zoom_thread = -2,
-                   browse_map = -2;
-
-               if (hist_browser__run(browser, msg, &es))
-                       break;
-
-               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:;
-                       }
-
-                       key = toupper(key);
-                       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");
-                               continue;
-                       default:;
-                       }
-                       if (is_exit_key(key)) {
-                               if (key == NEWT_KEY_ESCAPE) {
-                                       if (dialog_yesno("Do you really want to exit?"))
-                                               break;
-                                       else
-                                               continue;
-                               } else
-                                       break;
-                       }
-
-                       if (es.u.key == 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;
-                               continue;
-                       }
-               }
-
-               if (browser->selection->sym != NULL &&
-                   !browser->selection->map->dso->annotate_warned &&
-                   asprintf(&options[nr_options], "Annotate %s",
-                            browser->selection->sym->name) > 0)
-                       annotate = nr_options++;
-
-               if (thread != NULL &&
-                   asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
-                            (thread_filter ? "out of" : "into"),
-                            (thread->comm_set ? thread->comm : ""),
-                            thread->pid) > 0)
-                       zoom_thread = nr_options++;
-
-               if (dso != NULL &&
-                   asprintf(&options[nr_options], "Zoom %s %s DSO",
-                            (dso_filter ? "out of" : "into"),
-                            (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
-                       zoom_dso = nr_options++;
-
-               if (browser->selection->map != NULL &&
-                   asprintf(&options[nr_options], "Browse map details") > 0)
-                       browse_map = nr_options++;
-
-               options[nr_options++] = (char *)"Exit";
-
-               choice = popup_menu(nr_options, options);
-
-               for (i = 0; i < nr_options - 1; ++i)
-                       free(options[i]);
-
-               if (choice == nr_options - 1)
-                       break;
-
-               if (choice == -1)
-                       continue;
-
-               if (choice == annotate) {
-                       struct hist_entry *he;
-do_annotate:
-                       if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
-                               browser->selection->map->dso->annotate_warned = 1;
-                               ui_helpline__puts("No vmlinux file found, can't "
-                                                "annotate with just a "
-                                                "kallsyms file");
-                               continue;
-                       }
-
-                       he = hist_browser__selected_entry(browser);
-                       if (he == NULL)
-                               continue;
-
-                       hist_entry__tui_annotate(he);
-               } else if (choice == browse_map)
-                       map__browse(browser->selection->map);
-               else if (choice == zoom_dso) {
-zoom_dso:
-                       if (dso_filter) {
-                               pstack__remove(fstack, &dso_filter);
-zoom_out_dso:
-                               ui_helpline__pop();
-                               dso_filter = NULL;
-                       } else {
-                               if (dso == NULL)
-                                       continue;
-                               ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
-                                                  dso->kernel ? "the Kernel" : dso->short_name);
-                               dso_filter = 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);
-                       hist_browser__reset(browser);
-               } else if (choice == zoom_thread) {
-zoom_thread:
-                       if (thread_filter) {
-                               pstack__remove(fstack, &thread_filter);
-zoom_out_thread:
-                               ui_helpline__pop();
-                               thread_filter = NULL;
-                       } else {
-                               ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
-                                                  thread->comm_set ? thread->comm : "",
-                                                  thread->pid);
-                               thread_filter = 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);
-                       hist_browser__reset(browser);
-               }
-       }
-out_free_stack:
-       pstack__delete(fstack);
-out:
-       hist_browser__delete(browser);
-       return key;
-}
-
-int hists__tui_browse_tree(struct rb_root *self, const char *help)
-{
-       struct rb_node *first = rb_first(self), *nd = first, *next;
-       int key = 0;
-
-       while (nd) {
-               struct hists *hists = rb_entry(nd, struct hists, rb_node);
-               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);
-                       if (next)
-                               nd = next;
-                       break;
-               case NEWT_KEY_UNTAB:
-                       if (nd == first)
-                               continue;
-                       nd = rb_prev(nd);
-               default:
-                       break;
-               }
-       }
-
-       return key;
-}
-
-static void newt_suspend(void *d __used)
-{
-       newtSuspend();
-       raise(SIGTSTP);
-       newtResume();
-}
-
-void setup_browser(void)
-{
-       if (!isatty(1) || !use_browser || dump_trace) {
-               use_browser = 0;
-               setup_pager();
-               return;
-       }
-
-       use_browser = 1;
-       newtInit();
-       newtCls();
-       newtSetSuspendCallback(newt_suspend, NULL);
-       ui_helpline__puts(" ");
-       ui_browser__init();
-}
-
-void exit_browser(bool wait_for_ok)
-{
-       if (use_browser > 0) {
-               if (wait_for_ok) {
-                       char title[] = "Fatal Error", ok[] = "Ok";
-                       newtWinMessage(title, ok, browser__last_msg);
-               }
-               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
+#define LEVEL_OFFSET_STEP 3
 
 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
                                                     struct callchain_node *chain_node,
@@ -967,7 +342,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
                        }
 
                        SLsmg_set_color(color);
-                       SLsmg_gotorc(self->b.top + row, self->b.left);
+                       SLsmg_gotorc(self->b.y + row, self->b.x);
                        slsmg_write_nstring(" ", offset + extra_offset);
                        slsmg_printf("%c ", folded_sign);
                        slsmg_write_nstring(str, width);
@@ -1030,7 +405,7 @@ static int hist_browser__show_callchain_node(struct hist_browser *self,
                }
 
                s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
-               SLsmg_gotorc(self->b.top + row, self->b.left);
+               SLsmg_gotorc(self->b.y + row, self->b.x);
                SLsmg_set_color(color);
                slsmg_write_nstring(" ", offset);
                slsmg_printf("%c ", folded_sign);
@@ -1110,7 +485,7 @@ static int hist_browser__show_entry(struct hist_browser *self,
                }
 
                SLsmg_set_color(color);
-               SLsmg_gotorc(self->b.top + row, self->b.left);
+               SLsmg_gotorc(self->b.y + row, self->b.x);
                if (symbol_conf.use_callchain) {
                        slsmg_printf("%c ", folded_sign);
                        width -= 2;
@@ -1138,10 +513,10 @@ static unsigned int hist_browser__refresh(struct ui_browser *self)
        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);
+       if (self->top == NULL)
+               self->top = rb_first(&hb->hists->entries);
 
-       for (nd = self->first_visible_entry; nd; nd = rb_next(nd)) {
+       for (nd = self->top; nd; nd = rb_next(nd)) {
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 
                if (h->filtered)
@@ -1155,57 +530,6 @@ static unsigned int hist_browser__refresh(struct ui_browser *self)
        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) {
@@ -1244,7 +568,7 @@ static void ui_browser__hists_seek(struct ui_browser *self,
                nd = hists__filter_entries(rb_first(self->entries));
                break;
        case SEEK_CUR:
-               nd = self->first_visible_entry;
+               nd = self->top;
                goto do_offset;
        case SEEK_END:
                nd = hists__filter_prev_entries(rb_last(self->entries));
@@ -1258,7 +582,7 @@ static void ui_browser__hists_seek(struct ui_browser *self,
         * 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 = rb_entry(self->top, struct hist_entry, rb_node);
        h->row_offset = 0;
 
        /*
@@ -1286,7 +610,7 @@ do_offset:
                                } else {
                                        h->row_offset += offset;
                                        offset = 0;
-                                       self->first_visible_entry = nd;
+                                       self->top = nd;
                                        break;
                                }
                        }
@@ -1294,7 +618,7 @@ do_offset:
                        if (nd == NULL)
                                break;
                        --offset;
-                       self->first_visible_entry = nd;
+                       self->top = nd;
                } while (offset != 0);
        } else if (offset < 0) {
                while (1) {
@@ -1307,7 +631,7 @@ do_offset:
                                        } else {
                                                h->row_offset += offset;
                                                offset = 0;
-                                               self->first_visible_entry = nd;
+                                               self->top = nd;
                                                break;
                                        }
                                } else {
@@ -1317,7 +641,7 @@ do_offset:
                                        } else {
                                                h->row_offset = h->nr_rows + offset;
                                                offset = 0;
-                                               self->first_visible_entry = nd;
+                                               self->top = nd;
                                                break;
                                        }
                                }
@@ -1327,161 +651,298 @@ do_offset:
                        if (nd == NULL)
                                break;
                        ++offset;
-                       self->first_visible_entry = nd;
+                       self->top = 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;
+                               h = rb_entry(nd, struct hist_entry, rb_node);
+                               if (h->ms.unfolded)
+                                       h->row_offset = h->nr_rows;
+                               break;
+                       }
+                       first = false;
+               }
+       } else {
+               self->top = nd;
+               h = rb_entry(nd, struct hist_entry, rb_node);
+               h->row_offset = 0;
+       }
+}
+
+static struct hist_browser *hist_browser__new(struct hists *hists)
+{
+       struct hist_browser *self = zalloc(sizeof(*self));
+
+       if (self) {
+               self->hists = hists;
+               self->b.refresh = hist_browser__refresh;
+               self->b.seek = ui_browser__hists_seek;
+       }
+
+       return self;
+}
+
+static void hist_browser__delete(struct hist_browser *self)
+{
+       newtFormDestroy(self->b.form);
+       newtPopWindow();
+       free(self);
+}
+
+static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
+{
+       return self->he_selection;
+}
+
+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)
+{
+       int printed = 0;
+
+       if (thread)
+               printed += snprintf(bf + printed, size - printed,
+                                   "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);
+}
+
+int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
+{
+       struct hist_browser *browser = hist_browser__new(self);
+       struct pstack *fstack;
+       const struct thread *thread_filter = NULL;
+       const struct dso *dso_filter = NULL;
+       struct newtExitStruct es;
+       char msg[160];
+       int key = -1;
+
+       if (browser == NULL)
+               return -1;
+
+       fstack = pstack__new(2);
+       if (fstack == NULL)
+               goto out;
+
+       ui_helpline__push(helpline);
+
+       hist_browser__title(msg, sizeof(msg), ev_name,
+                           dso_filter, thread_filter);
+
+       while (1) {
+               const struct thread *thread;
+               const struct dso *dso;
+               char *options[16];
+               int nr_options = 0, choice = 0, i,
+                   annotate = -2, zoom_dso = -2, zoom_thread = -2,
+                   browse_map = -2;
+
+               if (hist_browser__run(browser, msg, &es))
+                       break;
+
+               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:;
                        }
-                       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;
+                       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");
+                               continue;
+                       default:;
+                       }
+                       if (is_exit_key(key)) {
+                               if (key == NEWT_KEY_ESCAPE &&
+                                   !ui__dialog_yesno("Do you really want to exit?"))
+                                       continue;
+                               break;
+                       }
 
-       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 */
+                       if (es.u.key == NEWT_KEY_LEFT) {
+                               const void *top;
 
-               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 (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;
+                       }
                }
 
-               if (folded_sign == '-') /* Have children and they're unfolded */
-                       n += callchain_node__count_rows_rb_tree(child);
-       }
-
-       return n;
-}
+               if (browser->selection->sym != NULL &&
+                   !browser->selection->map->dso->annotate_warned &&
+                   asprintf(&options[nr_options], "Annotate %s",
+                            browser->selection->sym->name) > 0)
+                       annotate = nr_options++;
 
-static int callchain_node__count_rows(struct callchain_node *node)
-{
-       struct callchain_list *chain;
-       bool unfolded = false;
-       int n = 0;
+               if (thread != NULL &&
+                   asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
+                            (thread_filter ? "out of" : "into"),
+                            (thread->comm_set ? thread->comm : ""),
+                            thread->pid) > 0)
+                       zoom_thread = nr_options++;
 
-       list_for_each_entry(chain, &node->val, list) {
-               ++n;
-               unfolded = chain->ms.unfolded;
-       }
+               if (dso != NULL &&
+                   asprintf(&options[nr_options], "Zoom %s %s DSO",
+                            (dso_filter ? "out of" : "into"),
+                            (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
+                       zoom_dso = nr_options++;
 
-       if (unfolded)
-               n += callchain_node__count_rows_rb_tree(node);
+               if (browser->selection->map != NULL &&
+                   asprintf(&options[nr_options], "Browse map details") > 0)
+                       browse_map = nr_options++;
 
-       return n;
-}
+               options[nr_options++] = (char *)"Exit";
 
-static int callchain__count_rows(struct rb_root *chain)
-{
-       struct rb_node *nd;
-       int n = 0;
+               choice = ui__popup_menu(nr_options, options);
 
-       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);
-       }
+               for (i = 0; i < nr_options - 1; ++i)
+                       free(options[i]);
 
-       return n;
-}
+               if (choice == nr_options - 1)
+                       break;
 
-static bool hist_browser__toggle_fold(struct hist_browser *self)
-{
-       if (map_symbol__toggle_fold(self->selection)) {
-               struct hist_entry *he = self->he_selection;
+               if (choice == -1)
+                       continue;
 
-               hist_entry__init_have_children(he);
-               self->hists->nr_entries -= he->nr_rows;
+               if (choice == annotate) {
+                       struct hist_entry *he;
+do_annotate:
+                       if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
+                               browser->selection->map->dso->annotate_warned = 1;
+                               ui_helpline__puts("No vmlinux file found, can't "
+                                                "annotate with just a "
+                                                "kallsyms file");
+                               continue;
+                       }
 
-               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;
+                       he = hist_browser__selected_entry(browser);
+                       if (he == NULL)
+                               continue;
 
-               return true;
+                       hist_entry__tui_annotate(he);
+               } else if (choice == browse_map)
+                       map__browse(browser->selection->map);
+               else if (choice == zoom_dso) {
+zoom_dso:
+                       if (dso_filter) {
+                               pstack__remove(fstack, &dso_filter);
+zoom_out_dso:
+                               ui_helpline__pop();
+                               dso_filter = NULL;
+                       } else {
+                               if (dso == NULL)
+                                       continue;
+                               ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
+                                                  dso->kernel ? "the Kernel" : dso->short_name);
+                               dso_filter = 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);
+                       hist_browser__reset(browser);
+               } else if (choice == zoom_thread) {
+zoom_thread:
+                       if (thread_filter) {
+                               pstack__remove(fstack, &thread_filter);
+zoom_out_thread:
+                               ui_helpline__pop();
+                               thread_filter = NULL;
+                       } else {
+                               ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
+                                                  thread->comm_set ? thread->comm : "",
+                                                  thread->pid);
+                               thread_filter = 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);
+                       hist_browser__reset(browser);
+               }
        }
-
-       /* If it doesn't have children, no toggling performed */
-       return false;
+out_free_stack:
+       pstack__delete(fstack);
+out:
+       hist_browser__delete(browser);
+       return key;
 }
 
-static int hist_browser__run(struct hist_browser *self, const char *title,
-                            struct newtExitStruct *es)
+int hists__tui_browse_tree(struct rb_root *self, const char *help)
 {
-       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;
+       struct rb_node *first = rb_first(self), *nd = first, *next;
+       int key = 0;
 
-       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');
+       while (nd) {
+               struct hists *hists = rb_entry(nd, struct hists, rb_node);
+               const char *ev_name = __event_name(hists->type, hists->config);
 
-       newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
-       newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
-       newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
+               key = hists__browse(hists, help, ev_name);
 
-       while (1) {
-               ui_browser__run(&self->b, es);
+               if (is_exit_key(key))
+                       break;
 
-               if (es->reason != NEWT_EXIT_HOTKEY)
+               switch (key) {
+               case NEWT_KEY_TAB:
+                       next = rb_next(nd);
+                       if (next)
+                               nd = next;
                        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 */
+               case NEWT_KEY_UNTAB:
+                       if (nd == first)
+                               continue;
+                       nd = rb_prev(nd);
                default:
-                       return 0;
+                       break;
                }
        }
-       return 0;
+
+       return key;
 }
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c
new file mode 100644 (file)
index 0000000..142b825
--- /dev/null
@@ -0,0 +1,161 @@
+#include "../libslang.h"
+#include <elf.h>
+#include <newt.h>
+#include <sys/ttydefaults.h>
+#include <ctype.h>
+#include <string.h>
+#include <linux/bitops.h>
+#include "../../debug.h"
+#include "../../symbol.h"
+#include "../browser.h"
+#include "../helpline.h"
+#include "map.h"
+
+static int ui_entry__read(const char *title, char *bf, size_t size, int width)
+{
+       struct newtExitStruct es;
+       newtComponent form, entry;
+       const char *result;
+       int err = -1;
+
+       newtCenteredWindow(width, 1, title);
+       form = newtForm(NULL, NULL, 0);
+       if (form == NULL)
+               return -1;
+
+       entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
+       if (entry == NULL)
+               goto out_free_form;
+
+       newtFormAddComponent(form, entry);
+       newtFormAddHotKey(form, NEWT_KEY_ENTER);
+       newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
+       newtFormAddHotKey(form, NEWT_KEY_LEFT);
+       newtFormAddHotKey(form, CTRL('c'));
+       newtFormRun(form, &es);
+
+       if (result != NULL) {
+               strncpy(bf, result, size);
+               err = 0;
+       }
+out_free_form:
+       newtPopWindow();
+       newtFormDestroy(form);
+       return 0;
+}
+
+struct map_browser {
+       struct ui_browser b;
+       struct map        *map;
+       u16               namelen;
+       u8                addrlen;
+};
+
+static void map_browser__write(struct ui_browser *self, void *nd, int row)
+{
+       struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+       struct map_browser *mb = container_of(self, struct map_browser, b);
+       bool current_entry = ui_browser__is_current_entry(self, row);
+       int color = ui_browser__percent_color(0, current_entry);
+
+       SLsmg_set_color(color);
+       slsmg_printf("%*llx %*llx %c ",
+                    mb->addrlen, sym->start, mb->addrlen, sym->end,
+                    sym->binding == STB_GLOBAL ? 'g' :
+                    sym->binding == STB_LOCAL  ? 'l' : 'w');
+       slsmg_write_nstring(sym->name, mb->namelen);
+}
+
+/* FIXME uber-kludgy, see comment on cmd_report... */
+static u32 *symbol__browser_index(struct symbol *self)
+{
+       return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
+}
+
+static int map_browser__search(struct map_browser *self)
+{
+       char target[512];
+       struct symbol *sym;
+       int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
+
+       if (err)
+               return err;
+
+       if (target[0] == '0' && tolower(target[1]) == 'x') {
+               u64 addr = strtoull(target, NULL, 16);
+               sym = map__find_symbol(self->map, addr, NULL);
+       } else
+               sym = map__find_symbol_by_name(self->map, target, NULL);
+
+       if (sym != NULL) {
+               u32 *idx = symbol__browser_index(sym);
+
+               self->b.top = &sym->rb_node;
+               self->b.index = self->b.top_idx = *idx;
+       } else
+               ui_helpline__fpush("%s not found!", target);
+
+       return 0;
+}
+
+static int map_browser__run(struct map_browser *self, struct newtExitStruct *es)
+{
+       if (ui_browser__show(&self->b, self->map->dso->long_name,
+                            "Press <- or ESC to exit, %s / to search",
+                            verbose ? "" : "restart with -v to use") < 0)
+               return -1;
+
+       newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
+       newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
+       if (verbose)
+               newtFormAddHotKey(self->b.form, '/');
+
+       while (1) {
+               ui_browser__run(&self->b, es);
+
+               if (es->reason != NEWT_EXIT_HOTKEY)
+                       break;
+               if (verbose && es->u.key == '/')
+                       map_browser__search(self);
+               else
+                       break;
+       }
+
+       ui_browser__hide(&self->b);
+       return 0;
+}
+
+int map__browse(struct map *self)
+{
+       struct map_browser mb = {
+               .b = {
+                       .entries = &self->dso->symbols[self->type],
+                       .refresh = ui_browser__rb_tree_refresh,
+                       .seek    = ui_browser__rb_tree_seek,
+                       .write   = map_browser__write,
+               },
+               .map = self,
+       };
+       struct newtExitStruct es;
+       struct rb_node *nd;
+       char tmp[BITS_PER_LONG / 4];
+       u64 maxaddr = 0;
+
+       for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
+               struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
+
+               if (mb.namelen < pos->namelen)
+                       mb.namelen = pos->namelen;
+               if (maxaddr < pos->end)
+                       maxaddr = pos->end;
+               if (verbose) {
+                       u32 *idx = symbol__browser_index(pos);
+                       *idx = mb.b.nr_entries;
+               }
+               ++mb.b.nr_entries;
+       }
+
+       mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr);
+       mb.b.width += mb.addrlen * 2 + 4 + mb.namelen;
+       return map_browser__run(&mb, &es);
+}
diff --git a/tools/perf/util/ui/browsers/map.h b/tools/perf/util/ui/browsers/map.h
new file mode 100644 (file)
index 0000000..df8581a
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _PERF_UI_MAP_BROWSER_H_
+#define _PERF_UI_MAP_BROWSER_H_ 1
+struct map;
+
+int map__browse(struct map *self);
+#endif /* _PERF_UI_MAP_BROWSER_H_ */
diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c
new file mode 100644 (file)
index 0000000..8d79daa
--- /dev/null
@@ -0,0 +1,69 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <newt.h>
+
+#include "../debug.h"
+#include "helpline.h"
+
+void ui_helpline__pop(void)
+{
+       newtPopHelpLine();
+}
+
+void ui_helpline__push(const char *msg)
+{
+       newtPushHelpLine(msg);
+}
+
+void ui_helpline__vpush(const char *fmt, va_list ap)
+{
+       char *s;
+
+       if (vasprintf(&s, fmt, ap) < 0)
+               vfprintf(stderr, fmt, ap);
+       else {
+               ui_helpline__push(s);
+               free(s);
+       }
+}
+
+void ui_helpline__fpush(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       ui_helpline__vpush(fmt, ap);
+       va_end(ap);
+}
+
+void ui_helpline__puts(const char *msg)
+{
+       ui_helpline__pop();
+       ui_helpline__push(msg);
+}
+
+void ui_helpline__init(void)
+{
+       ui_helpline__puts(" ");
+}
+
+char ui_helpline__last_msg[1024];
+
+int ui_helpline__show_help(const char *format, va_list ap)
+{
+       int ret;
+       static int backlog;
+
+        ret = vsnprintf(ui_helpline__last_msg + backlog,
+                       sizeof(ui_helpline__last_msg) - backlog, format, ap);
+       backlog += ret;
+
+       if (ui_helpline__last_msg[backlog - 1] == '\n') {
+               ui_helpline__puts(ui_helpline__last_msg);
+               newtRefresh();
+               backlog = 0;
+       }
+
+       return ret;
+}
diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h
new file mode 100644 (file)
index 0000000..ab6028d
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef _PERF_UI_HELPLINE_H_
+#define _PERF_UI_HELPLINE_H_ 1
+
+void ui_helpline__init(void);
+void ui_helpline__pop(void);
+void ui_helpline__push(const char *msg);
+void ui_helpline__vpush(const char *fmt, va_list ap);
+void ui_helpline__fpush(const char *fmt, ...);
+void ui_helpline__puts(const char *msg);
+
+#endif /* _PERF_UI_HELPLINE_H_ */
diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h
new file mode 100644 (file)
index 0000000..5623da8
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef _PERF_UI_SLANG_H_
+#define _PERF_UI_SLANG_H_ 1
+/*
+ * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
+ * the build if it isn't defined. Use the equivalent one that glibc
+ * has on features.h.
+ */
+#include <features.h>
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
+#endif
+#include <slang.h>
+
+#if SLANG_VERSION < 20104
+#define slsmg_printf(msg, args...) \
+       SLsmg_printf((char *)msg, ##args)
+#define slsmg_write_nstring(msg, len) \
+       SLsmg_write_nstring((char *)msg, len)
+#define sltt_set_color(obj, name, fg, bg) \
+       SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg)
+#else
+#define slsmg_printf SLsmg_printf
+#define slsmg_write_nstring SLsmg_write_nstring
+#define sltt_set_color SLtt_set_color
+#endif
+
+#endif /* _PERF_UI_SLANG_H_ */
diff --git a/tools/perf/util/ui/progress.c b/tools/perf/util/ui/progress.c
new file mode 100644 (file)
index 0000000..d7fc399
--- /dev/null
@@ -0,0 +1,60 @@
+#include <stdlib.h>
+#include <newt.h>
+#include "../cache.h"
+#include "progress.h"
+
+struct ui_progress {
+       newtComponent form, scale;
+};
+
+struct ui_progress *ui_progress__new(const char *title, u64 total)
+{
+       struct ui_progress *self = malloc(sizeof(*self));
+
+       if (self != NULL) {
+               int cols;
+
+               if (use_browser <= 0)
+                       return self;
+               newtGetScreenSize(&cols, NULL);
+               cols -= 4;
+               newtCenteredWindow(cols, 1, title);
+               self->form  = newtForm(NULL, NULL, 0);
+               if (self->form == NULL)
+                       goto out_free_self;
+               self->scale = newtScale(0, 0, cols, total);
+               if (self->scale == NULL)
+                       goto out_free_form;
+               newtFormAddComponent(self->form, self->scale);
+               newtRefresh();
+       }
+
+       return self;
+
+out_free_form:
+       newtFormDestroy(self->form);
+out_free_self:
+       free(self);
+       return NULL;
+}
+
+void ui_progress__update(struct ui_progress *self, u64 curr)
+{
+       /*
+        * FIXME: We should have a per UI backend way of showing progress,
+        * stdio will just show a percentage as NN%, etc.
+        */
+       if (use_browser <= 0)
+               return;
+       newtScaleSet(self->scale, curr);
+       newtRefresh();
+}
+
+void ui_progress__delete(struct ui_progress *self)
+{
+       if (use_browser > 0) {
+               newtFormDestroy(self->form);
+               newtPopWindow();
+       }
+       free(self);
+}
diff --git a/tools/perf/util/ui/progress.h b/tools/perf/util/ui/progress.h
new file mode 100644 (file)
index 0000000..a3820a0
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef _PERF_UI_PROGRESS_H_
+#define _PERF_UI_PROGRESS_H_ 1
+
+struct ui_progress;
+
+struct ui_progress *ui_progress__new(const char *title, u64 total);
+void ui_progress__delete(struct ui_progress *self);
+
+void ui_progress__update(struct ui_progress *self, u64 curr);
+
+#endif
diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c
new file mode 100644 (file)
index 0000000..6620850
--- /dev/null
@@ -0,0 +1,42 @@
+#include <newt.h>
+#include <signal.h>
+#include <stdbool.h>
+
+#include "../cache.h"
+#include "../debug.h"
+#include "browser.h"
+#include "helpline.h"
+
+static void newt_suspend(void *d __used)
+{
+       newtSuspend();
+       raise(SIGTSTP);
+       newtResume();
+}
+
+void setup_browser(void)
+{
+       if (!isatty(1) || !use_browser || dump_trace) {
+               use_browser = 0;
+               setup_pager();
+               return;
+       }
+
+       use_browser = 1;
+       newtInit();
+       newtCls();
+       newtSetSuspendCallback(newt_suspend, NULL);
+       ui_helpline__init();
+       ui_browser__init();
+}
+
+void exit_browser(bool wait_for_ok)
+{
+       if (use_browser > 0) {
+               if (wait_for_ok) {
+                       char title[] = "Fatal Error", ok[] = "Ok";
+                       newtWinMessage(title, ok, ui_helpline__last_msg);
+               }
+               newtFinished();
+       }
+}
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c
new file mode 100644 (file)
index 0000000..04600e2
--- /dev/null
@@ -0,0 +1,114 @@
+#include <newt.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/ttydefaults.h>
+
+#include "../cache.h"
+#include "../debug.h"
+#include "browser.h"
+#include "helpline.h"
+#include "util.h"
+
+newtComponent newt_form__new(void);
+
+static void newt_form__set_exit_keys(newtComponent self)
+{
+       newtFormAddHotKey(self, NEWT_KEY_LEFT);
+       newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
+       newtFormAddHotKey(self, 'Q');
+       newtFormAddHotKey(self, 'q');
+       newtFormAddHotKey(self, CTRL('c'));
+}
+
+newtComponent newt_form__new(void)
+{
+       newtComponent self = newtForm(NULL, NULL, 0);
+       if (self)
+               newt_form__set_exit_keys(self);
+       return self;
+}
+
+int ui__popup_menu(int argc, char * const argv[])
+{
+       struct newtExitStruct es;
+       int i, rc = -1, max_len = 5;
+       newtComponent listbox, form = newt_form__new();
+
+       if (form == NULL)
+               return -1;
+
+       listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
+       if (listbox == NULL)
+               goto out_destroy_form;
+
+       newtFormAddComponent(form, listbox);
+
+       for (i = 0; i < argc; ++i) {
+               int len = strlen(argv[i]);
+               if (len > max_len)
+                       max_len = len;
+               if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
+                       goto out_destroy_form;
+       }
+
+       newtCenteredWindow(max_len, argc, NULL);
+       newtFormRun(form, &es);
+       rc = newtListboxGetCurrent(listbox) - NULL;
+       if (es.reason == NEWT_EXIT_HOTKEY)
+               rc = -1;
+       newtPopWindow();
+out_destroy_form:
+       newtFormDestroy(form);
+       return rc;
+}
+
+int ui__help_window(const char *text)
+{
+       struct newtExitStruct es;
+       newtComponent tb, form = newt_form__new();
+       int rc = -1;
+       int max_len = 0, nr_lines = 0;
+       const char *t;
+
+       if (form == NULL)
+               return -1;
+
+       t = text;
+       while (1) {
+               const char *sep = strchr(t, '\n');
+               int len;
+
+               if (sep == NULL)
+                       sep = strchr(t, '\0');
+               len = sep - t;
+               if (max_len < len)
+                       max_len = len;
+               ++nr_lines;
+               if (*sep == '\0')
+                       break;
+               t = sep + 1;
+       }
+
+       tb = newtTextbox(0, 0, max_len, nr_lines, 0);
+       if (tb == NULL)
+               goto out_destroy_form;
+
+       newtTextboxSetText(tb, text);
+       newtFormAddComponent(form, tb);
+       newtCenteredWindow(max_len, nr_lines, NULL);
+       newtFormRun(form, &es);
+       newtPopWindow();
+       rc = 0;
+out_destroy_form:
+       newtFormDestroy(form);
+       return rc;
+}
+
+bool ui__dialog_yesno(const char *msg)
+{
+       /* newtWinChoice should really be accepting const char pointers... */
+       char yes[] = "Yes", no[] = "No";
+       return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
+}
diff --git a/tools/perf/util/ui/util.h b/tools/perf/util/ui/util.h
new file mode 100644 (file)
index 0000000..afcbc1d
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _PERF_UI_UTIL_H_
+#define _PERF_UI_UTIL_H_ 1
+
+#include <stdbool.h>
+
+int ui__popup_menu(int argc, char * const argv[]);
+int ui__help_window(const char *text);
+bool ui__dialog_yesno(const char *msg);
+
+#endif /* _PERF_UI_UTIL_H_ */