# 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/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/keysyms.h
- LIB_H += util/ui/libslang.h
- LIB_H += util/ui/progress.h
- LIB_H += util/ui/util.h
- LIB_H += util/ui/ui.h
+ LIB_OBJS += $(OUTPUT)ui/setup.o
+ LIB_OBJS += $(OUTPUT)ui/browser.o
+ LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o
+ LIB_OBJS += $(OUTPUT)ui/browsers/hists.o
+ LIB_OBJS += $(OUTPUT)ui/browsers/map.o
+ LIB_OBJS += $(OUTPUT)ui/helpline.o
+ LIB_OBJS += $(OUTPUT)ui/progress.o
+ LIB_OBJS += $(OUTPUT)ui/util.o
+ LIB_H += ui/browser.h
+ LIB_H += ui/browsers/map.h
+ LIB_H += ui/helpline.h
+ LIB_H += ui/keysyms.h
+ LIB_H += ui/libslang.h
+ LIB_H += ui/progress.h
+ LIB_H += ui/util.h
+ LIB_H += ui/ui.h
endif
endif
$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
-$(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS
+$(OUTPUT)ui/browser.o: ui/browser.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
-$(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS
+$(OUTPUT)ui/browsers/annotate.o: 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
+$(OUTPUT)ui/browsers/hists.o: 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
+$(OUTPUT)ui/browsers/map.o: 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
--- /dev/null
+#include "../util.h"
+#include "../cache.h"
+#include "../../perf.h"
+#include "libslang.h"
+#include <newt.h>
+#include "ui.h"
+#include "util.h"
+#include <linux/compiler.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <stdlib.h>
+#include <sys/ttydefaults.h>
+#include "browser.h"
+#include "helpline.h"
+#include "keysyms.h"
+#include "../color.h"
+
+static int ui_browser__percent_color(struct ui_browser *browser,
+ double percent, bool current)
+{
+ if (current && (!browser->use_navkeypressed || browser->navkeypressed))
+ return HE_COLORSET_SELECTED;
+ if (percent >= MIN_RED)
+ return HE_COLORSET_TOP;
+ if (percent >= MIN_GREEN)
+ return HE_COLORSET_MEDIUM;
+ return HE_COLORSET_NORMAL;
+}
+
+int ui_browser__set_color(struct ui_browser *browser, int color)
+{
+ int ret = browser->current_color;
+ browser->current_color = color;
+ SLsmg_set_color(color);
+ return ret;
+}
+
+void ui_browser__set_percent_color(struct ui_browser *self,
+ double percent, bool current)
+{
+ int color = ui_browser__percent_color(self, percent, current);
+ ui_browser__set_color(self, color);
+}
+
+void ui_browser__gotorc(struct ui_browser *self, int y, int x)
+{
+ SLsmg_gotorc(self->y + y, self->x + x);
+}
+
+static struct list_head *
+ui_browser__list_head_filter_entries(struct ui_browser *browser,
+ struct list_head *pos)
+{
+ do {
+ if (!browser->filter || !browser->filter(browser, pos))
+ return pos;
+ pos = pos->next;
+ } while (pos != browser->entries);
+
+ return NULL;
+}
+
+static struct list_head *
+ui_browser__list_head_filter_prev_entries(struct ui_browser *browser,
+ struct list_head *pos)
+{
+ do {
+ if (!browser->filter || !browser->filter(browser, pos))
+ return pos;
+ pos = pos->prev;
+ } while (pos != browser->entries);
+
+ return NULL;
+}
+
+void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence)
+{
+ struct list_head *head = self->entries;
+ struct list_head *pos;
+
+ if (self->nr_entries == 0)
+ return;
+
+ switch (whence) {
+ case SEEK_SET:
+ pos = ui_browser__list_head_filter_entries(self, head->next);
+ break;
+ case SEEK_CUR:
+ pos = self->top;
+ break;
+ case SEEK_END:
+ pos = ui_browser__list_head_filter_prev_entries(self, head->prev);
+ break;
+ default:
+ return;
+ }
+
+ assert(pos != NULL);
+
+ if (offset > 0) {
+ while (offset-- != 0)
+ pos = ui_browser__list_head_filter_entries(self, pos->next);
+ } else {
+ while (offset++ != 0)
+ pos = ui_browser__list_head_filter_prev_entries(self, pos->prev);
+ }
+
+ self->top = pos;
+}
+
+void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
+{
+ struct rb_root *root = self->entries;
+ struct rb_node *nd;
+
+ switch (whence) {
+ case SEEK_SET:
+ nd = rb_first(root);
+ break;
+ case SEEK_CUR:
+ nd = self->top;
+ break;
+ case SEEK_END:
+ nd = rb_last(root);
+ break;
+ default:
+ return;
+ }
+
+ if (offset > 0) {
+ while (offset-- != 0)
+ nd = rb_next(nd);
+ } else {
+ while (offset++ != 0)
+ nd = rb_prev(nd);
+ }
+
+ self->top = nd;
+}
+
+unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
+{
+ struct rb_node *nd;
+ int row = 0;
+
+ if (self->top == NULL)
+ self->top = rb_first(self->entries);
+
+ nd = self->top;
+
+ while (nd != NULL) {
+ ui_browser__gotorc(self, row, 0);
+ self->write(self, nd, row);
+ if (++row == self->height)
+ break;
+ nd = rb_next(nd);
+ }
+
+ return row;
+}
+
+bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
+{
+ return self->top_idx + row == self->index;
+}
+
+void ui_browser__refresh_dimensions(struct ui_browser *self)
+{
+ self->width = SLtt_Screen_Cols - 1;
+ self->height = SLtt_Screen_Rows - 2;
+ self->y = 1;
+ self->x = 0;
+}
+
+void ui_browser__handle_resize(struct ui_browser *browser)
+{
+ ui__refresh_dimensions(false);
+ ui_browser__show(browser, browser->title, ui_helpline__current);
+ ui_browser__refresh(browser);
+}
+
+int ui_browser__warning(struct ui_browser *browser, int timeout,
+ const char *format, ...)
+{
+ va_list args;
+ char *text;
+ int key = 0, err;
+
+ va_start(args, format);
+ err = vasprintf(&text, format, args);
+ va_end(args);
+
+ if (err < 0) {
+ va_start(args, format);
+ ui_helpline__vpush(format, args);
+ va_end(args);
+ } else {
+ while ((key == ui__question_window("Warning!", text,
+ "Press any key...",
+ timeout)) == K_RESIZE)
+ ui_browser__handle_resize(browser);
+ free(text);
+ }
+
+ return key;
+}
+
+int ui_browser__help_window(struct ui_browser *browser, const char *text)
+{
+ int key;
+
+ while ((key = ui__help_window(text)) == K_RESIZE)
+ ui_browser__handle_resize(browser);
+
+ return key;
+}
+
+bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text)
+{
+ int key;
+
+ while ((key = ui__dialog_yesno(text)) == K_RESIZE)
+ ui_browser__handle_resize(browser);
+
+ return key == K_ENTER || toupper(key) == 'Y';
+}
+
+void ui_browser__reset_index(struct ui_browser *self)
+{
+ self->index = self->top_idx = 0;
+ self->seek(self, 0, SEEK_SET);
+}
+
+void __ui_browser__show_title(struct ui_browser *browser, const char *title)
+{
+ SLsmg_gotorc(0, 0);
+ ui_browser__set_color(browser, NEWT_COLORSET_ROOT);
+ slsmg_write_nstring(title, browser->width + 1);
+}
+
+void ui_browser__show_title(struct ui_browser *browser, const char *title)
+{
+ pthread_mutex_lock(&ui__lock);
+ __ui_browser__show_title(browser, title);
+ pthread_mutex_unlock(&ui__lock);
+}
+
+int ui_browser__show(struct ui_browser *self, const char *title,
+ const char *helpline, ...)
+{
+ int err;
+ va_list ap;
+
+ ui_browser__refresh_dimensions(self);
+
+ pthread_mutex_lock(&ui__lock);
+ __ui_browser__show_title(self, title);
+
+ self->title = title;
+ free(self->helpline);
+ self->helpline = NULL;
+
+ va_start(ap, helpline);
+ err = vasprintf(&self->helpline, helpline, ap);
+ va_end(ap);
+ if (err > 0)
+ ui_helpline__push(self->helpline);
+ pthread_mutex_unlock(&ui__lock);
+ return err ? 0 : -1;
+}
+
+void ui_browser__hide(struct ui_browser *browser __used)
+{
+ pthread_mutex_lock(&ui__lock);
+ ui_helpline__pop();
+ pthread_mutex_unlock(&ui__lock);
+}
+
+static void ui_browser__scrollbar_set(struct ui_browser *browser)
+{
+ int height = browser->height, h = 0, pct = 0,
+ col = browser->width,
+ row = browser->y - 1;
+
+ if (browser->nr_entries > 1) {
+ pct = ((browser->index * (browser->height - 1)) /
+ (browser->nr_entries - 1));
+ }
+
+ SLsmg_set_char_set(1);
+
+ while (h < height) {
+ ui_browser__gotorc(browser, row++, col);
+ SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR);
+ ++h;
+ }
+
+ SLsmg_set_char_set(0);
+}
+
+static int __ui_browser__refresh(struct ui_browser *browser)
+{
+ int row;
+ int width = browser->width;
+
+ row = browser->refresh(browser);
+ ui_browser__set_color(browser, HE_COLORSET_NORMAL);
+
+ if (!browser->use_navkeypressed || browser->navkeypressed)
+ ui_browser__scrollbar_set(browser);
+ else
+ width += 1;
+
+ SLsmg_fill_region(browser->y + row, browser->x,
+ browser->height - row, width, ' ');
+
+ return 0;
+}
+
+int ui_browser__refresh(struct ui_browser *browser)
+{
+ pthread_mutex_lock(&ui__lock);
+ __ui_browser__refresh(browser);
+ pthread_mutex_unlock(&ui__lock);
+
+ return 0;
+}
+
+/*
+ * Here we're updating nr_entries _after_ we started browsing, i.e. we have to
+ * forget about any reference to any entry in the underlying data structure,
+ * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser
+ * after an output_resort and hist decay.
+ */
+void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
+{
+ off_t offset = nr_entries - browser->nr_entries;
+
+ browser->nr_entries = nr_entries;
+
+ if (offset < 0) {
+ if (browser->top_idx < (u64)-offset)
+ offset = -browser->top_idx;
+
+ browser->index += offset;
+ browser->top_idx += offset;
+ }
+
+ browser->top = NULL;
+ browser->seek(browser, browser->top_idx, SEEK_SET);
+}
+
+int ui_browser__run(struct ui_browser *self, int delay_secs)
+{
+ int err, key;
+
+ while (1) {
+ off_t offset;
+
+ pthread_mutex_lock(&ui__lock);
+ err = __ui_browser__refresh(self);
+ SLsmg_refresh();
+ pthread_mutex_unlock(&ui__lock);
+ if (err < 0)
+ break;
+
+ key = ui__getch(delay_secs);
+
+ if (key == K_RESIZE) {
+ ui__refresh_dimensions(false);
+ ui_browser__refresh_dimensions(self);
+ __ui_browser__show_title(self, self->title);
+ ui_helpline__puts(self->helpline);
+ continue;
+ }
+
+ if (self->use_navkeypressed && !self->navkeypressed) {
+ if (key == K_DOWN || key == K_UP ||
+ key == K_PGDN || key == K_PGUP ||
+ key == K_HOME || key == K_END ||
+ key == ' ') {
+ self->navkeypressed = true;
+ continue;
+ } else
+ return key;
+ }
+
+ switch (key) {
+ case K_DOWN:
+ if (self->index == self->nr_entries - 1)
+ break;
+ ++self->index;
+ if (self->index == self->top_idx + self->height) {
+ ++self->top_idx;
+ self->seek(self, +1, SEEK_CUR);
+ }
+ break;
+ case K_UP:
+ if (self->index == 0)
+ break;
+ --self->index;
+ if (self->index < self->top_idx) {
+ --self->top_idx;
+ self->seek(self, -1, SEEK_CUR);
+ }
+ break;
+ case K_PGDN:
+ case ' ':
+ 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->top_idx += offset;
+ self->seek(self, +offset, SEEK_CUR);
+ break;
+ case K_PGUP:
+ if (self->top_idx == 0)
+ break;
+
+ if (self->top_idx < self->height)
+ offset = self->top_idx;
+ else
+ offset = self->height;
+
+ self->index -= offset;
+ self->top_idx -= offset;
+ self->seek(self, -offset, SEEK_CUR);
+ break;
+ case K_HOME:
+ ui_browser__reset_index(self);
+ break;
+ case K_END:
+ offset = self->height - 1;
+ if (offset >= self->nr_entries)
+ offset = self->nr_entries - 1;
+
+ self->index = self->nr_entries - 1;
+ self->top_idx = self->index - offset;
+ self->seek(self, -offset, SEEK_END);
+ break;
+ default:
+ return key;
+ }
+ }
+ return -1;
+}
+
+unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
+{
+ struct list_head *pos;
+ struct list_head *head = self->entries;
+ int row = 0;
+
+ if (self->top == NULL || self->top == self->entries)
+ self->top = ui_browser__list_head_filter_entries(self, head->next);
+
+ pos = self->top;
+
+ list_for_each_from(pos, head) {
+ if (!self->filter || !self->filter(self, pos)) {
+ ui_browser__gotorc(self, row, 0);
+ self->write(self, pos, row);
+ if (++row == self->height)
+ break;
+ }
+ }
+
+ return row;
+}
+
+static struct ui_browser__colorset {
+ const char *name, *fg, *bg;
+ int colorset;
+} ui_browser__colorsets[] = {
+ {
+ .colorset = HE_COLORSET_TOP,
+ .name = "top",
+ .fg = "red",
+ .bg = "default",
+ },
+ {
+ .colorset = HE_COLORSET_MEDIUM,
+ .name = "medium",
+ .fg = "green",
+ .bg = "default",
+ },
+ {
+ .colorset = HE_COLORSET_NORMAL,
+ .name = "normal",
+ .fg = "default",
+ .bg = "default",
+ },
+ {
+ .colorset = HE_COLORSET_SELECTED,
+ .name = "selected",
+ .fg = "black",
+ .bg = "lightgray",
+ },
+ {
+ .colorset = HE_COLORSET_CODE,
+ .name = "code",
+ .fg = "blue",
+ .bg = "default",
+ },
+ {
+ .colorset = HE_COLORSET_ADDR,
+ .name = "addr",
+ .fg = "magenta",
+ .bg = "default",
+ },
+ {
+ .name = NULL,
+ }
+};
+
+
+static int ui_browser__color_config(const char *var, const char *value,
+ void *data __used)
+{
+ char *fg = NULL, *bg;
+ int i;
+
+ /* same dir for all commands */
+ if (prefixcmp(var, "colors.") != 0)
+ return 0;
+
+ for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
+ const char *name = var + 7;
+
+ if (strcmp(ui_browser__colorsets[i].name, name) != 0)
+ continue;
+
+ fg = strdup(value);
+ if (fg == NULL)
+ break;
+
+ bg = strchr(fg, ',');
+ if (bg == NULL)
+ break;
+
+ *bg = '\0';
+ while (isspace(*++bg));
+ ui_browser__colorsets[i].bg = bg;
+ ui_browser__colorsets[i].fg = fg;
+ return 0;
+ }
+
+ free(fg);
+ return -1;
+}
+
+void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
+{
+ switch (whence) {
+ case SEEK_SET:
+ browser->top = browser->entries;
+ break;
+ case SEEK_CUR:
+ browser->top = browser->top + browser->top_idx + offset;
+ break;
+ case SEEK_END:
+ browser->top = browser->top + browser->nr_entries + offset;
+ break;
+ default:
+ return;
+ }
+}
+
+unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
+{
+ unsigned int row = 0, idx = browser->top_idx;
+ char **pos;
+
+ if (browser->top == NULL)
+ browser->top = browser->entries;
+
+ pos = (char **)browser->top;
+ while (idx < browser->nr_entries) {
+ if (!browser->filter || !browser->filter(browser, *pos)) {
+ ui_browser__gotorc(browser, row, 0);
+ browser->write(browser, pos, row);
+ if (++row == browser->height)
+ break;
+ }
+
+ ++idx;
+ ++pos;
+ }
+
+ return row;
+}
+
+void ui_browser__init(void)
+{
+ int i = 0;
+
+ perf_config(ui_browser__color_config, NULL);
+
+ while (ui_browser__colorsets[i].name) {
+ struct ui_browser__colorset *c = &ui_browser__colorsets[i++];
+ sltt_set_color(c->colorset, c->name, c->fg, c->bg);
+ }
+}
--- /dev/null
+#ifndef _PERF_UI_BROWSER_H_
+#define _PERF_UI_BROWSER_H_ 1
+
+#include <stdbool.h>
+#include <sys/types.h>
+#include "../types.h"
+
+#define HE_COLORSET_TOP 50
+#define HE_COLORSET_MEDIUM 51
+#define HE_COLORSET_NORMAL 52
+#define HE_COLORSET_SELECTED 53
+#define HE_COLORSET_CODE 54
+#define HE_COLORSET_ADDR 55
+
+struct ui_browser {
+ u64 index, top_idx;
+ void *top, *entries;
+ u16 y, x, width, height;
+ int current_color;
+ void *priv;
+ const char *title;
+ char *helpline;
+ unsigned int (*refresh)(struct ui_browser *self);
+ void (*write)(struct ui_browser *self, void *entry, int row);
+ void (*seek)(struct ui_browser *self, off_t offset, int whence);
+ bool (*filter)(struct ui_browser *self, void *entry);
+ u32 nr_entries;
+ bool navkeypressed;
+ bool use_navkeypressed;
+};
+
+int ui_browser__set_color(struct ui_browser *browser, int color);
+void ui_browser__set_percent_color(struct ui_browser *self,
+ double percent, bool current);
+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);
+
+void ui_browser__gotorc(struct ui_browser *self, int y, int x);
+void __ui_browser__show_title(struct ui_browser *browser, const char *title);
+void ui_browser__show_title(struct ui_browser *browser, 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 *browser, int delay_secs);
+void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries);
+void ui_browser__handle_resize(struct ui_browser *browser);
+
+int ui_browser__warning(struct ui_browser *browser, int timeout,
+ const char *format, ...);
+int ui_browser__help_window(struct ui_browser *browser, const char *text);
+bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text);
+int ui_browser__input_window(const char *title, const char *text, char *input,
+ const char *exit_msg, int delay_sec);
+
+void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence);
+unsigned int ui_browser__argv_refresh(struct ui_browser *browser);
+
+void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence);
+unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self);
+
+void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence);
+unsigned int ui_browser__list_head_refresh(struct ui_browser *self);
+
+void ui_browser__init(void);
+#endif /* _PERF_UI_BROWSER_H_ */
--- /dev/null
+#include "../../util/util.h"
+#include "../browser.h"
+#include "../helpline.h"
+#include "../libslang.h"
+#include "../ui.h"
+#include "../util.h"
+#include "../../util/annotate.h"
+#include "../../util/hist.h"
+#include "../../util/sort.h"
+#include "../../util/symbol.h"
+#include <pthread.h>
+#include <newt.h>
+
+struct annotate_browser {
+ struct ui_browser b;
+ struct rb_root entries;
+ struct rb_node *curr_hot;
+ struct objdump_line *selection;
+ u64 start;
+ int nr_asm_entries;
+ int nr_entries;
+ bool hide_src_code;
+ bool use_offset;
+ bool searching_backwards;
+ char search_bf[128];
+};
+
+struct objdump_line_rb_node {
+ struct rb_node rb_node;
+ double percent;
+ u32 idx;
+ int idx_asm;
+};
+
+static inline
+struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
+{
+ return (struct objdump_line_rb_node *)(self + 1);
+}
+
+static bool objdump_line__filter(struct ui_browser *browser, void *entry)
+{
+ struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
+
+ if (ab->hide_src_code) {
+ struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
+ return ol->offset == -1;
+ }
+
+ return false;
+}
+
+static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
+{
+ struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
+ struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
+ bool current_entry = ui_browser__is_current_entry(self, row);
+ bool change_color = (!ab->hide_src_code &&
+ (!current_entry || (self->use_navkeypressed &&
+ !self->navkeypressed)));
+ int width = self->width;
+
+ if (ol->offset != -1) {
+ struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
+ ui_browser__set_percent_color(self, olrb->percent, current_entry);
+ slsmg_printf(" %7.2f ", olrb->percent);
+ } else {
+ ui_browser__set_percent_color(self, 0, current_entry);
+ slsmg_write_nstring(" ", 9);
+ }
+
+ SLsmg_write_char(':');
+ slsmg_write_nstring(" ", 8);
+
+ /* The scroll bar isn't being used */
+ if (!self->navkeypressed)
+ width += 1;
+
+ if (ol->offset != -1 && change_color)
+ ui_browser__set_color(self, HE_COLORSET_CODE);
+
+ if (!*ol->line)
+ slsmg_write_nstring(" ", width - 18);
+ else if (ol->offset == -1)
+ slsmg_write_nstring(ol->line, width - 18);
+ else {
+ char bf[64];
+ u64 addr = ol->offset;
+ int printed, color = -1;
+
+ if (!ab->use_offset)
+ addr += ab->start;
+
+ printed = scnprintf(bf, sizeof(bf), " %" PRIx64 ":", addr);
+ if (change_color)
+ color = ui_browser__set_color(self, HE_COLORSET_ADDR);
+ slsmg_write_nstring(bf, printed);
+ if (change_color)
+ ui_browser__set_color(self, color);
+ slsmg_write_nstring(ol->line, width - 18 - printed);
+ }
+
+ if (current_entry)
+ ab->selection = ol;
+}
+
+static double objdump_line__calc_percent(struct objdump_line *self,
+ struct symbol *sym, int evidx)
+{
+ double percent = 0.0;
+
+ if (self->offset != -1) {
+ int len = sym->end - sym->start;
+ unsigned int hits = 0;
+ struct annotation *notes = symbol__annotation(sym);
+ struct source_line *src_line = notes->src->lines;
+ struct sym_hist *h = annotation__histogram(notes, evidx);
+ s64 offset = self->offset;
+ struct objdump_line *next;
+
+ next = objdump__get_next_ip_line(¬es->src->source, self);
+ while (offset < (s64)len &&
+ (next == NULL || offset < next->offset)) {
+ if (src_line) {
+ percent += src_line[offset].percent;
+ } else
+ hits += h->addr[offset];
+
+ ++offset;
+ }
+ /*
+ * If the percentage wasn't already calculated in
+ * symbol__get_source_line, do it now:
+ */
+ if (src_line == 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 objdump_line *pos, u32 idx)
+{
+ unsigned back;
+
+ ui_browser__refresh_dimensions(&self->b);
+ back = self->b.height / 2;
+ self->b.top_idx = self->b.index = idx;
+
+ while (self->b.top_idx != 0 && back != 0) {
+ pos = list_entry(pos->node.prev, struct objdump_line, node);
+
+ if (objdump_line__filter(&self->b, &pos->node))
+ continue;
+
+ --self->b.top_idx;
+ --back;
+ }
+
+ self->b.top = pos;
+ self->b.navkeypressed = true;
+}
+
+static void annotate_browser__set_rb_top(struct annotate_browser *browser,
+ struct rb_node *nd)
+{
+ struct objdump_line_rb_node *rbpos;
+ struct objdump_line *pos;
+
+ rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
+ pos = ((struct objdump_line *)rbpos) - 1;
+ annotate_browser__set_top(browser, pos, rbpos->idx);
+ browser->curr_hot = nd;
+}
+
+static void annotate_browser__calc_percent(struct annotate_browser *browser,
+ int evidx)
+{
+ struct map_symbol *ms = browser->b.priv;
+ struct symbol *sym = ms->sym;
+ struct annotation *notes = symbol__annotation(sym);
+ struct objdump_line *pos;
+
+ browser->entries = RB_ROOT;
+
+ pthread_mutex_lock(¬es->lock);
+
+ list_for_each_entry(pos, ¬es->src->source, node) {
+ struct objdump_line_rb_node *rbpos = objdump_line__rb(pos);
+ rbpos->percent = objdump_line__calc_percent(pos, sym, evidx);
+ if (rbpos->percent < 0.01) {
+ RB_CLEAR_NODE(&rbpos->rb_node);
+ continue;
+ }
+ objdump__insert_line(&browser->entries, rbpos);
+ }
+ pthread_mutex_unlock(¬es->lock);
+
+ browser->curr_hot = rb_last(&browser->entries);
+}
+
+static bool annotate_browser__toggle_source(struct annotate_browser *browser)
+{
+ struct objdump_line *ol;
+ struct objdump_line_rb_node *olrb;
+ off_t offset = browser->b.index - browser->b.top_idx;
+
+ browser->b.seek(&browser->b, offset, SEEK_CUR);
+ ol = list_entry(browser->b.top, struct objdump_line, node);
+ olrb = objdump_line__rb(ol);
+
+ if (browser->hide_src_code) {
+ if (olrb->idx_asm < offset)
+ offset = olrb->idx;
+
+ browser->b.nr_entries = browser->nr_entries;
+ browser->hide_src_code = false;
+ browser->b.seek(&browser->b, -offset, SEEK_CUR);
+ browser->b.top_idx = olrb->idx - offset;
+ browser->b.index = olrb->idx;
+ } else {
+ if (olrb->idx_asm < 0) {
+ ui_helpline__puts("Only available for assembly lines.");
+ browser->b.seek(&browser->b, -offset, SEEK_CUR);
+ return false;
+ }
+
+ if (olrb->idx_asm < offset)
+ offset = olrb->idx_asm;
+
+ browser->b.nr_entries = browser->nr_asm_entries;
+ browser->hide_src_code = true;
+ browser->b.seek(&browser->b, -offset, SEEK_CUR);
+ browser->b.top_idx = olrb->idx_asm - offset;
+ browser->b.index = olrb->idx_asm;
+ }
+
+ return true;
+}
+
+static bool annotate_browser__callq(struct annotate_browser *browser,
+ int evidx, void (*timer)(void *arg),
+ void *arg, int delay_secs)
+{
+ struct map_symbol *ms = browser->b.priv;
+ struct symbol *sym = ms->sym;
+ struct annotation *notes;
+ struct symbol *target;
+ char *s = strstr(browser->selection->line, "callq ");
+ u64 ip;
+
+ if (s == NULL)
+ return false;
+
+ s = strchr(s, ' ');
+ if (s++ == NULL) {
+ ui_helpline__puts("Invallid callq instruction.");
+ return true;
+ }
+
+ ip = strtoull(s, NULL, 16);
+ ip = ms->map->map_ip(ms->map, ip);
+ target = map__find_symbol(ms->map, ip, NULL);
+ if (target == NULL) {
+ ui_helpline__puts("The called function was not found.");
+ return true;
+ }
+
+ notes = symbol__annotation(target);
+ pthread_mutex_lock(¬es->lock);
+
+ if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
+ pthread_mutex_unlock(¬es->lock);
+ ui__warning("Not enough memory for annotating '%s' symbol!\n",
+ target->name);
+ return true;
+ }
+
+ pthread_mutex_unlock(¬es->lock);
+ symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs);
+ ui_browser__show_title(&browser->b, sym->name);
+ return true;
+}
+
+static struct objdump_line *
+ annotate_browser__find_offset(struct annotate_browser *browser,
+ s64 offset, s64 *idx)
+{
+ struct map_symbol *ms = browser->b.priv;
+ struct symbol *sym = ms->sym;
+ struct annotation *notes = symbol__annotation(sym);
+ struct objdump_line *pos;
+
+ *idx = 0;
+ list_for_each_entry(pos, ¬es->src->source, node) {
+ if (pos->offset == offset)
+ return pos;
+ if (!objdump_line__filter(&browser->b, &pos->node))
+ ++*idx;
+ }
+
+ return NULL;
+}
+
+static bool annotate_browser__jump(struct annotate_browser *browser)
+{
+ const char *jumps[] = { "je ", "jne ", "ja ", "jmpq ", "js ", "jmp ", NULL };
+ struct objdump_line *line;
+ s64 idx, offset;
+ char *s = NULL;
+ int i = 0;
+
+ while (jumps[i]) {
+ s = strstr(browser->selection->line, jumps[i++]);
+ if (s)
+ break;
+ }
+
+ if (s == NULL)
+ return false;
+
+ s = strchr(s, '+');
+ if (s++ == NULL) {
+ ui_helpline__puts("Invallid jump instruction.");
+ return true;
+ }
+
+ offset = strtoll(s, NULL, 16);
+ line = annotate_browser__find_offset(browser, offset, &idx);
+ if (line == NULL) {
+ ui_helpline__puts("Invallid jump offset");
+ return true;
+ }
+
+ annotate_browser__set_top(browser, line, idx);
+
+ return true;
+}
+
+static struct objdump_line *
+ annotate_browser__find_string(struct annotate_browser *browser,
+ char *s, s64 *idx)
+{
+ struct map_symbol *ms = browser->b.priv;
+ struct symbol *sym = ms->sym;
+ struct annotation *notes = symbol__annotation(sym);
+ struct objdump_line *pos = browser->selection;
+
+ *idx = browser->b.index;
+ list_for_each_entry_continue(pos, ¬es->src->source, node) {
+ if (objdump_line__filter(&browser->b, &pos->node))
+ continue;
+
+ ++*idx;
+
+ if (pos->line && strstr(pos->line, s) != NULL)
+ return pos;
+ }
+
+ return NULL;
+}
+
+static bool __annotate_browser__search(struct annotate_browser *browser)
+{
+ struct objdump_line *line;
+ s64 idx;
+
+ line = annotate_browser__find_string(browser, browser->search_bf, &idx);
+ if (line == NULL) {
+ ui_helpline__puts("String not found!");
+ return false;
+ }
+
+ annotate_browser__set_top(browser, line, idx);
+ browser->searching_backwards = false;
+ return true;
+}
+
+static struct objdump_line *
+ annotate_browser__find_string_reverse(struct annotate_browser *browser,
+ char *s, s64 *idx)
+{
+ struct map_symbol *ms = browser->b.priv;
+ struct symbol *sym = ms->sym;
+ struct annotation *notes = symbol__annotation(sym);
+ struct objdump_line *pos = browser->selection;
+
+ *idx = browser->b.index;
+ list_for_each_entry_continue_reverse(pos, ¬es->src->source, node) {
+ if (objdump_line__filter(&browser->b, &pos->node))
+ continue;
+
+ --*idx;
+
+ if (pos->line && strstr(pos->line, s) != NULL)
+ return pos;
+ }
+
+ return NULL;
+}
+
+static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
+{
+ struct objdump_line *line;
+ s64 idx;
+
+ line = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
+ if (line == NULL) {
+ ui_helpline__puts("String not found!");
+ return false;
+ }
+
+ annotate_browser__set_top(browser, line, idx);
+ browser->searching_backwards = true;
+ return true;
+}
+
+static bool annotate_browser__search_window(struct annotate_browser *browser,
+ int delay_secs)
+{
+ if (ui_browser__input_window("Search", "String: ", browser->search_bf,
+ "ENTER: OK, ESC: Cancel",
+ delay_secs * 2) != K_ENTER ||
+ !*browser->search_bf)
+ return false;
+
+ return true;
+}
+
+static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
+{
+ if (annotate_browser__search_window(browser, delay_secs))
+ return __annotate_browser__search(browser);
+
+ return false;
+}
+
+static bool annotate_browser__continue_search(struct annotate_browser *browser,
+ int delay_secs)
+{
+ if (!*browser->search_bf)
+ return annotate_browser__search(browser, delay_secs);
+
+ return __annotate_browser__search(browser);
+}
+
+static bool annotate_browser__search_reverse(struct annotate_browser *browser,
+ int delay_secs)
+{
+ if (annotate_browser__search_window(browser, delay_secs))
+ return __annotate_browser__search_reverse(browser);
+
+ return false;
+}
+
+static
+bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
+ int delay_secs)
+{
+ if (!*browser->search_bf)
+ return annotate_browser__search_reverse(browser, delay_secs);
+
+ return __annotate_browser__search_reverse(browser);
+}
+
+static int annotate_browser__run(struct annotate_browser *self, int evidx,
+ void(*timer)(void *arg),
+ void *arg, int delay_secs)
+{
+ struct rb_node *nd = NULL;
+ struct map_symbol *ms = self->b.priv;
+ struct symbol *sym = ms->sym;
+ const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
+ "H: Go to hottest line, ->/ENTER: Line action, "
+ "O: Toggle offset view, "
+ "S: Toggle source code view";
+ int key;
+
+ if (ui_browser__show(&self->b, sym->name, help) < 0)
+ return -1;
+
+ annotate_browser__calc_percent(self, evidx);
+
+ if (self->curr_hot) {
+ annotate_browser__set_rb_top(self, self->curr_hot);
+ self->b.navkeypressed = false;
+ }
+
+ nd = self->curr_hot;
+
+ while (1) {
+ key = ui_browser__run(&self->b, delay_secs);
+
+ if (delay_secs != 0) {
+ annotate_browser__calc_percent(self, evidx);
+ /*
+ * Current line focus got out of the list of most active
+ * lines, NULL it so that if TAB|UNTAB is pressed, we
+ * move to curr_hot (current hottest line).
+ */
+ if (nd != NULL && RB_EMPTY_NODE(nd))
+ nd = NULL;
+ }
+
+ switch (key) {
+ case K_TIMER:
+ if (timer != NULL)
+ timer(arg);
+
+ if (delay_secs != 0)
+ symbol__annotate_decay_histogram(sym, evidx);
+ continue;
+ case K_TAB:
+ if (nd != NULL) {
+ nd = rb_prev(nd);
+ if (nd == NULL)
+ nd = rb_last(&self->entries);
+ } else
+ nd = self->curr_hot;
+ break;
+ case K_UNTAB:
+ if (nd != NULL)
+ nd = rb_next(nd);
+ if (nd == NULL)
+ nd = rb_first(&self->entries);
+ else
+ nd = self->curr_hot;
+ break;
+ case 'H':
+ case 'h':
+ nd = self->curr_hot;
+ break;
+ case 'S':
+ case 's':
+ if (annotate_browser__toggle_source(self))
+ ui_helpline__puts(help);
+ continue;
+ case 'O':
+ case 'o':
+ self->use_offset = !self->use_offset;
+ continue;
+ case '/':
+ if (annotate_browser__search(self, delay_secs)) {
+show_help:
+ ui_helpline__puts(help);
+ }
+ continue;
+ case 'n':
+ if (self->searching_backwards ?
+ annotate_browser__continue_search_reverse(self, delay_secs) :
+ annotate_browser__continue_search(self, delay_secs))
+ goto show_help;
+ continue;
+ case '?':
+ if (annotate_browser__search_reverse(self, delay_secs))
+ goto show_help;
+ continue;
+ case K_ENTER:
+ case K_RIGHT:
+ if (self->selection == NULL)
+ ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
+ else if (self->selection->offset == -1)
+ ui_helpline__puts("Actions are only available for assembly lines.");
+ else if (!(annotate_browser__jump(self) ||
+ annotate_browser__callq(self, evidx, timer, arg, delay_secs)))
+ ui_helpline__puts("Actions are only available for the 'callq' and jump instructions.");
+ continue;
+ case K_LEFT:
+ case K_ESC:
+ case 'q':
+ case CTRL('c'):
+ goto out;
+ default:
+ continue;
+ }
+
+ if (nd != NULL)
+ annotate_browser__set_rb_top(self, nd);
+ }
+out:
+ ui_browser__hide(&self->b);
+ return key;
+}
+
+int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
+ void(*timer)(void *arg), void *arg, int delay_secs)
+{
+ return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
+ timer, arg, delay_secs);
+}
+
+int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
+ void(*timer)(void *arg), void *arg,
+ int delay_secs)
+{
+ struct objdump_line *pos, *n;
+ struct annotation *notes;
+ struct map_symbol ms = {
+ .map = map,
+ .sym = sym,
+ };
+ struct annotate_browser browser = {
+ .b = {
+ .refresh = ui_browser__list_head_refresh,
+ .seek = ui_browser__list_head_seek,
+ .write = annotate_browser__write,
+ .filter = objdump_line__filter,
+ .priv = &ms,
+ .use_navkeypressed = true,
+ },
+ };
+ int ret;
+
+ if (sym == NULL)
+ return -1;
+
+ if (map->dso->annotate_warned)
+ return -1;
+
+ if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) {
+ ui__error("%s", ui_helpline__last_msg);
+ return -1;
+ }
+
+ ui_helpline__push("Press <- or ESC to exit");
+
+ notes = symbol__annotation(sym);
+ browser.start = map__rip_2objdump(map, sym->start);
+
+ list_for_each_entry(pos, ¬es->src->source, node) {
+ struct objdump_line_rb_node *rbpos;
+ 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.nr_entries++;
+ if (pos->offset != -1)
+ rbpos->idx_asm = browser.nr_asm_entries++;
+ else
+ rbpos->idx_asm = -1;
+ }
+
+ browser.b.nr_entries = browser.nr_entries;
+ browser.b.entries = ¬es->src->source,
+ browser.b.width += 18; /* Percentage */
+ ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
+ list_for_each_entry_safe(pos, n, ¬es->src->source, node) {
+ list_del(&pos->node);
+ objdump_line__free(pos);
+ }
+ return ret;
+}
--- /dev/null
+#include <stdio.h>
+#include "../libslang.h"
+#include <stdlib.h>
+#include <string.h>
+#include <newt.h>
+#include <linux/rbtree.h>
+
+#include "../../util/evsel.h"
+#include "../../util/evlist.h"
+#include "../../util/hist.h"
+#include "../../util/pstack.h"
+#include "../../util/sort.h"
+#include "../../util/util.h"
+
+#include "../browser.h"
+#include "../helpline.h"
+#include "../util.h"
+#include "../ui.h"
+#include "map.h"
+
+struct hist_browser {
+ struct ui_browser b;
+ struct hists *hists;
+ struct hist_entry *he_selection;
+ struct map_symbol *selection;
+ bool has_symbols;
+};
+
+static int hists__browser_title(struct hists *self, char *bf, size_t size,
+ const char *ev_name);
+
+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 void map_symbol__set_folding(struct map_symbol *self, bool unfold)
+{
+ self->unfolded = unfold ? self->has_children : false;
+}
+
+static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
+{
+ int n = 0;
+ struct rb_node *nd;
+
+ for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
+ struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
+ struct callchain_list *chain;
+ char folded_sign = ' '; /* No children */
+
+ list_for_each_entry(chain, &child->val, list) {
+ ++n;
+ /* We need this because we may not have children */
+ folded_sign = callchain_list__folded(chain);
+ if (folded_sign == '+')
+ break;
+ }
+
+ if (folded_sign == '-') /* Have children and they're unfolded */
+ n += callchain_node__count_rows_rb_tree(child);
+ }
+
+ return n;
+}
+
+static int callchain_node__count_rows(struct callchain_node *node)
+{
+ struct callchain_list *chain;
+ bool unfolded = false;
+ int n = 0;
+
+ list_for_each_entry(chain, &node->val, list) {
+ ++n;
+ unfolded = chain->ms.unfolded;
+ }
+
+ if (unfolded)
+ n += callchain_node__count_rows_rb_tree(node);
+
+ return n;
+}
+
+static int callchain__count_rows(struct rb_root *chain)
+{
+ struct rb_node *nd;
+ int n = 0;
+
+ for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
+ struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
+ n += callchain_node__count_rows(node);
+ }
+
+ return n;
+}
+
+static bool map_symbol__toggle_fold(struct map_symbol *self)
+{
+ if (!self)
+ return false;
+
+ if (!self->has_children)
+ return false;
+
+ self->unfolded = !self->unfolded;
+ return true;
+}
+
+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;
+ bool first = true;
+
+ list_for_each_entry(chain, &child->val, list) {
+ if (first) {
+ first = false;
+ chain->ms.has_children = chain->list.next != &child->val ||
+ !RB_EMPTY_ROOT(&child->rb_root);
+ } else
+ chain->ms.has_children = chain->list.next == &child->val &&
+ !RB_EMPTY_ROOT(&child->rb_root);
+ }
+
+ 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_EMPTY_ROOT(&self->rb_root);
+
+ 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) {
+ self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain);
+ callchain__init_have_children(&self->sorted_chain);
+ self->init_have_children = true;
+ }
+}
+
+static bool hist_browser__toggle_fold(struct hist_browser *self)
+{
+ if (map_symbol__toggle_fold(self->selection)) {
+ struct hist_entry *he = self->he_selection;
+
+ hist_entry__init_have_children(he);
+ self->hists->nr_entries -= he->nr_rows;
+
+ if (he->ms.unfolded)
+ he->nr_rows = callchain__count_rows(&he->sorted_chain);
+ else
+ he->nr_rows = 0;
+ self->hists->nr_entries += he->nr_rows;
+ self->b.nr_entries = self->hists->nr_entries;
+
+ return true;
+ }
+
+ /* If it doesn't have children, no toggling performed */
+ return false;
+}
+
+static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold)
+{
+ int n = 0;
+ struct rb_node *nd;
+
+ for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
+ struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
+ struct callchain_list *chain;
+ bool has_children = false;
+
+ list_for_each_entry(chain, &child->val, list) {
+ ++n;
+ map_symbol__set_folding(&chain->ms, unfold);
+ has_children = chain->ms.has_children;
+ }
+
+ if (has_children)
+ n += callchain_node__set_folding_rb_tree(child, unfold);
+ }
+
+ return n;
+}
+
+static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
+{
+ struct callchain_list *chain;
+ bool has_children = false;
+ int n = 0;
+
+ list_for_each_entry(chain, &node->val, list) {
+ ++n;
+ map_symbol__set_folding(&chain->ms, unfold);
+ has_children = chain->ms.has_children;
+ }
+
+ if (has_children)
+ n += callchain_node__set_folding_rb_tree(node, unfold);
+
+ return n;
+}
+
+static int callchain__set_folding(struct rb_root *chain, bool unfold)
+{
+ struct rb_node *nd;
+ int n = 0;
+
+ for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
+ struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
+ n += callchain_node__set_folding(node, unfold);
+ }
+
+ return n;
+}
+
+static void hist_entry__set_folding(struct hist_entry *self, bool unfold)
+{
+ hist_entry__init_have_children(self);
+ map_symbol__set_folding(&self->ms, unfold);
+
+ if (self->ms.has_children) {
+ int n = callchain__set_folding(&self->sorted_chain, unfold);
+ self->nr_rows = unfold ? n : 0;
+ } else
+ self->nr_rows = 0;
+}
+
+static void hists__set_folding(struct hists *self, bool unfold)
+{
+ struct rb_node *nd;
+
+ self->nr_entries = 0;
+
+ for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
+ hist_entry__set_folding(he, unfold);
+ self->nr_entries += 1 + he->nr_rows;
+ }
+}
+
+static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
+{
+ hists__set_folding(self->hists, unfold);
+ self->b.nr_entries = self->hists->nr_entries;
+ /* Go to the start, we may be way after valid entries after a collapse */
+ ui_browser__reset_index(&self->b);
+}
+
+static void ui_browser__warn_lost_events(struct ui_browser *browser)
+{
+ ui_browser__warning(browser, 4,
+ "Events are being lost, check IO/CPU overload!\n\n"
+ "You may want to run 'perf' using a RT scheduler policy:\n\n"
+ " perf top -r 80\n\n"
+ "Or reduce the sampling frequency.");
+}
+
+static int hist_browser__run(struct hist_browser *self, const char *ev_name,
+ void(*timer)(void *arg), void *arg, int delay_secs)
+{
+ int key;
+ char title[160];
+
+ self->b.entries = &self->hists->entries;
+ self->b.nr_entries = self->hists->nr_entries;
+
+ hist_browser__refresh_dimensions(self);
+ hists__browser_title(self->hists, title, sizeof(title), ev_name);
+
+ if (ui_browser__show(&self->b, title,
+ "Press '?' for help on key bindings") < 0)
+ return -1;
+
+ while (1) {
+ key = ui_browser__run(&self->b, delay_secs);
+
+ switch (key) {
+ case K_TIMER:
+ timer(arg);
+ ui_browser__update_nr_entries(&self->b, self->hists->nr_entries);
+
+ if (self->hists->stats.nr_lost_warned !=
+ self->hists->stats.nr_events[PERF_RECORD_LOST]) {
+ self->hists->stats.nr_lost_warned =
+ self->hists->stats.nr_events[PERF_RECORD_LOST];
+ ui_browser__warn_lost_events(&self->b);
+ }
+
+ hists__browser_title(self->hists, title, sizeof(title), ev_name);
+ ui_browser__show_title(&self->b, title);
+ continue;
+ 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);
+ }
+ break;
+ case 'C':
+ /* Collapse the whole world. */
+ hist_browser__set_folding(self, false);
+ break;
+ case 'E':
+ /* Expand the whole world. */
+ hist_browser__set_folding(self, true);
+ break;
+ case K_ENTER:
+ if (hist_browser__toggle_fold(self))
+ break;
+ /* fall thru */
+ default:
+ goto out;
+ }
+ }
+out:
+ ui_browser__hide(&self->b);
+ return key;
+}
+
+static char *callchain_list__sym_name(struct callchain_list *self,
+ char *bf, size_t bfsize)
+{
+ if (self->ms.sym)
+ return self->ms.sym->name;
+
+ snprintf(bf, bfsize, "%#" PRIx64, self->ip);
+ return bf;
+}
+
+#define LEVEL_OFFSET_STEP 3
+
+static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
+ struct callchain_node *chain_node,
+ u64 total, int level,
+ unsigned short row,
+ off_t *row_offset,
+ bool *is_current_entry)
+{
+ struct rb_node *node;
+ int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
+ u64 new_total, remaining;
+
+ if (callchain_param.mode == CHAIN_GRAPH_REL)
+ new_total = chain_node->children_hit;
+ else
+ new_total = total;
+
+ remaining = new_total;
+ node = rb_first(&chain_node->rb_root);
+ while (node) {
+ struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
+ struct rb_node *next = rb_next(node);
+ u64 cumul = callchain_cumul_hits(child);
+ struct callchain_list *chain;
+ char folded_sign = ' ';
+ int first = true;
+ int extra_offset = 0;
+
+ remaining -= cumul;
+
+ list_for_each_entry(chain, &child->val, list) {
+ char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
+ const char *str;
+ int color;
+ bool was_first = first;
+
+ if (first)
+ first = false;
+ else
+ extra_offset = LEVEL_OFFSET_STEP;
+
+ folded_sign = callchain_list__folded(chain);
+ if (*row_offset != 0) {
+ --*row_offset;
+ goto do_next;
+ }
+
+ alloc_str = NULL;
+ str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
+ if (was_first) {
+ double percent = cumul * 100.0 / new_total;
+
+ if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
+ str = "Not enough memory!";
+ else
+ str = alloc_str;
+ }
+
+ color = HE_COLORSET_NORMAL;
+ width = self->b.width - (offset + extra_offset + 2);
+ if (ui_browser__is_current_entry(&self->b, row)) {
+ self->selection = &chain->ms;
+ color = HE_COLORSET_SELECTED;
+ *is_current_entry = true;
+ }
+
+ ui_browser__set_color(&self->b, color);
+ ui_browser__gotorc(&self->b, row, 0);
+ slsmg_write_nstring(" ", offset + extra_offset);
+ slsmg_printf("%c ", folded_sign);
+ slsmg_write_nstring(str, width);
+ free(alloc_str);
+
+ if (++row == self->b.height)
+ goto out;
+do_next:
+ if (folded_sign == '+')
+ break;
+ }
+
+ if (folded_sign == '-') {
+ const int new_level = level + (extra_offset ? 2 : 1);
+ row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
+ new_level, row, row_offset,
+ is_current_entry);
+ }
+ if (row == self->b.height)
+ goto out;
+ node = next;
+ }
+out:
+ return row - first_row;
+}
+
+static int hist_browser__show_callchain_node(struct hist_browser *self,
+ struct callchain_node *node,
+ int level, unsigned short row,
+ off_t *row_offset,
+ bool *is_current_entry)
+{
+ struct callchain_list *chain;
+ int first_row = row,
+ offset = level * LEVEL_OFFSET_STEP,
+ width = self->b.width - offset;
+ char folded_sign = ' ';
+
+ list_for_each_entry(chain, &node->val, list) {
+ char ipstr[BITS_PER_LONG / 4 + 1], *s;
+ int color;
+
+ folded_sign = callchain_list__folded(chain);
+
+ if (*row_offset != 0) {
+ --*row_offset;
+ continue;
+ }
+
+ color = HE_COLORSET_NORMAL;
+ if (ui_browser__is_current_entry(&self->b, row)) {
+ self->selection = &chain->ms;
+ color = HE_COLORSET_SELECTED;
+ *is_current_entry = true;
+ }
+
+ s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
+ ui_browser__gotorc(&self->b, row, 0);
+ ui_browser__set_color(&self->b, color);
+ slsmg_write_nstring(" ", offset);
+ slsmg_printf("%c ", folded_sign);
+ slsmg_write_nstring(s, width - 2);
+
+ if (++row == self->b.height)
+ goto out;
+ }
+
+ if (folded_sign == '-')
+ row += hist_browser__show_callchain_node_rb_tree(self, node,
+ self->hists->stats.total_period,
+ level + 1, row,
+ row_offset,
+ is_current_entry);
+out:
+ return row - first_row;
+}
+
+static int hist_browser__show_callchain(struct hist_browser *self,
+ struct rb_root *chain,
+ int level, unsigned short row,
+ off_t *row_offset,
+ bool *is_current_entry)
+{
+ struct rb_node *nd;
+ int first_row = row;
+
+ for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
+ struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
+
+ row += hist_browser__show_callchain_node(self, node, level,
+ row, row_offset,
+ is_current_entry);
+ if (row == self->b.height)
+ break;
+ }
+
+ return row - first_row;
+}
+
+static int hist_browser__show_entry(struct hist_browser *self,
+ struct hist_entry *entry,
+ unsigned short row)
+{
+ char s[256];
+ double percent;
+ int printed = 0;
+ int width = self->b.width - 6; /* The percentage */
+ char folded_sign = ' ';
+ bool current_entry = ui_browser__is_current_entry(&self->b, row);
+ off_t row_offset = entry->row_offset;
+
+ if (current_entry) {
+ self->he_selection = entry;
+ self->selection = &entry->ms;
+ }
+
+ if (symbol_conf.use_callchain) {
+ hist_entry__init_have_children(entry);
+ folded_sign = hist_entry__folded(entry);
+ }
+
+ if (row_offset == 0) {
+ hist_entry__snprintf(entry, s, sizeof(s), self->hists);
+ percent = (entry->period * 100.0) / self->hists->stats.total_period;
+
+ ui_browser__set_percent_color(&self->b, percent, current_entry);
+ ui_browser__gotorc(&self->b, row, 0);
+ if (symbol_conf.use_callchain) {
+ slsmg_printf("%c ", folded_sign);
+ width -= 2;
+ }
+
+ slsmg_printf(" %5.2f%%", percent);
+
+ /* The scroll bar isn't being used */
+ if (!self->b.navkeypressed)
+ width += 1;
+
+ if (!current_entry || !self->b.navkeypressed)
+ ui_browser__set_color(&self->b, HE_COLORSET_NORMAL);
+
+ if (symbol_conf.show_nr_samples) {
+ slsmg_printf(" %11u", entry->nr_events);
+ width -= 12;
+ }
+
+ if (symbol_conf.show_total_period) {
+ slsmg_printf(" %12" PRIu64, entry->period);
+ width -= 13;
+ }
+
+ slsmg_write_nstring(s, width);
+ ++row;
+ ++printed;
+ } else
+ --row_offset;
+
+ if (folded_sign == '-' && row != self->b.height) {
+ printed += hist_browser__show_callchain(self, &entry->sorted_chain,
+ 1, row, &row_offset,
+ ¤t_entry);
+ if (current_entry)
+ self->he_selection = entry;
+ }
+
+ return printed;
+}
+
+static void ui_browser__hists_init_top(struct ui_browser *browser)
+{
+ if (browser->top == NULL) {
+ struct hist_browser *hb;
+
+ hb = container_of(browser, struct hist_browser, b);
+ browser->top = rb_first(&hb->hists->entries);
+ }
+}
+
+static unsigned int hist_browser__refresh(struct ui_browser *self)
+{
+ unsigned row = 0;
+ struct rb_node *nd;
+ struct hist_browser *hb = container_of(self, struct hist_browser, b);
+
+ ui_browser__hists_init_top(self);
+
+ for (nd = self->top; nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+
+ if (h->filtered)
+ continue;
+
+ row += hist_browser__show_entry(hb, h, row);
+ if (row == self->height)
+ break;
+ }
+
+ return row;
+}
+
+static struct rb_node *hists__filter_entries(struct rb_node *nd)
+{
+ while (nd != NULL) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+ if (!h->filtered)
+ return nd;
+
+ nd = rb_next(nd);
+ }
+
+ return NULL;
+}
+
+static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
+{
+ while (nd != NULL) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+ if (!h->filtered)
+ return nd;
+
+ nd = rb_prev(nd);
+ }
+
+ return NULL;
+}
+
+static void ui_browser__hists_seek(struct ui_browser *self,
+ off_t offset, int whence)
+{
+ struct hist_entry *h;
+ struct rb_node *nd;
+ bool first = true;
+
+ if (self->nr_entries == 0)
+ return;
+
+ ui_browser__hists_init_top(self);
+
+ switch (whence) {
+ case SEEK_SET:
+ nd = hists__filter_entries(rb_first(self->entries));
+ break;
+ case SEEK_CUR:
+ nd = self->top;
+ goto do_offset;
+ case SEEK_END:
+ nd = hists__filter_prev_entries(rb_last(self->entries));
+ first = false;
+ break;
+ default:
+ return;
+ }
+
+ /*
+ * Moves not relative to the first visible entry invalidates its
+ * row_offset:
+ */
+ h = rb_entry(self->top, struct hist_entry, rb_node);
+ h->row_offset = 0;
+
+ /*
+ * Here we have to check if nd is expanded (+), if it is we can't go
+ * the next top level hist_entry, instead we must compute an offset of
+ * what _not_ to show and not change the first visible entry.
+ *
+ * This offset increments when we are going from top to bottom and
+ * decreases when we're going from bottom to top.
+ *
+ * As we don't have backpointers to the top level in the callchains
+ * structure, we need to always print the whole hist_entry callchain,
+ * skipping the first ones that are before the first visible entry
+ * and stop when we printed enough lines to fill the screen.
+ */
+do_offset:
+ if (offset > 0) {
+ do {
+ h = rb_entry(nd, struct hist_entry, rb_node);
+ if (h->ms.unfolded) {
+ u16 remaining = h->nr_rows - h->row_offset;
+ if (offset > remaining) {
+ offset -= remaining;
+ h->row_offset = 0;
+ } else {
+ h->row_offset += offset;
+ offset = 0;
+ self->top = nd;
+ break;
+ }
+ }
+ nd = hists__filter_entries(rb_next(nd));
+ if (nd == NULL)
+ break;
+ --offset;
+ self->top = nd;
+ } while (offset != 0);
+ } else if (offset < 0) {
+ while (1) {
+ h = rb_entry(nd, struct hist_entry, rb_node);
+ if (h->ms.unfolded) {
+ if (first) {
+ if (-offset > h->row_offset) {
+ offset += h->row_offset;
+ h->row_offset = 0;
+ } else {
+ h->row_offset += offset;
+ offset = 0;
+ self->top = nd;
+ break;
+ }
+ } else {
+ if (-offset > h->nr_rows) {
+ offset += h->nr_rows;
+ h->row_offset = 0;
+ } else {
+ h->row_offset = h->nr_rows + offset;
+ offset = 0;
+ self->top = nd;
+ break;
+ }
+ }
+ }
+
+ nd = hists__filter_prev_entries(rb_prev(nd));
+ if (nd == NULL)
+ break;
+ ++offset;
+ 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;
+ }
+ 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;
+ self->b.use_navkeypressed = true;
+ if (sort__branch_mode == 1)
+ self->has_symbols = sort_sym_from.list.next != NULL;
+ else
+ self->has_symbols = sort_sym.list.next != NULL;
+ }
+
+ return self;
+}
+
+static void hist_browser__delete(struct hist_browser *self)
+{
+ 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 hists__browser_title(struct hists *self, char *bf, size_t size,
+ const char *ev_name)
+{
+ char unit;
+ int printed;
+ const struct dso *dso = self->dso_filter;
+ const struct thread *thread = self->thread_filter;
+ unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE];
+ u64 nr_events = self->stats.total_period;
+
+ nr_samples = convert_unit(nr_samples, &unit);
+ printed = scnprintf(bf, size,
+ "Samples: %lu%c of event '%s', Event count (approx.): %lu",
+ nr_samples, unit, ev_name, nr_events);
+
+
+ if (self->uid_filter_str)
+ printed += snprintf(bf + printed, size - printed,
+ ", UID: %s", self->uid_filter_str);
+ if (thread)
+ printed += scnprintf(bf + printed, size - printed,
+ ", Thread: %s(%d)",
+ (thread->comm_set ? thread->comm : ""),
+ thread->pid);
+ if (dso)
+ printed += scnprintf(bf + printed, size - printed,
+ ", DSO: %s", dso->short_name);
+ return printed;
+}
+
+static inline void free_popup_options(char **options, int n)
+{
+ int i;
+
+ for (i = 0; i < n; ++i) {
+ free(options[i]);
+ options[i] = NULL;
+ }
+}
+
+static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
+ const char *helpline, const char *ev_name,
+ bool left_exits,
+ void(*timer)(void *arg), void *arg,
+ int delay_secs)
+{
+ struct hists *self = &evsel->hists;
+ struct hist_browser *browser = hist_browser__new(self);
+ struct branch_info *bi;
+ struct pstack *fstack;
+ char *options[16];
+ int nr_options = 0;
+ int key = -1;
+ char buf[64];
+
+ if (browser == NULL)
+ return -1;
+
+ fstack = pstack__new(2);
+ if (fstack == NULL)
+ goto out;
+
+ ui_helpline__push(helpline);
+
+ memset(options, 0, sizeof(options));
+
+ while (1) {
+ const struct thread *thread = NULL;
+ const struct dso *dso = NULL;
+ int choice = 0,
+ annotate = -2, zoom_dso = -2, zoom_thread = -2,
+ annotate_f = -2, annotate_t = -2, browse_map = -2;
+
+ nr_options = 0;
+
+ key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
+
+ if (browser->he_selection != NULL) {
+ thread = hist_browser__selected_thread(browser);
+ dso = browser->selection->map ? browser->selection->map->dso : NULL;
+ }
+ switch (key) {
+ case K_TAB:
+ case K_UNTAB:
+ if (nr_events == 1)
+ continue;
+ /*
+ * Exit the browser, let hists__browser_tree
+ * go to the next or previous
+ */
+ goto out_free_stack;
+ case 'a':
+ if (!browser->has_symbols) {
+ ui_browser__warning(&browser->b, delay_secs * 2,
+ "Annotation is only available for symbolic views, "
+ "include \"sym*\" in --sort to use it.");
+ continue;
+ }
+
+ if (browser->selection == NULL ||
+ browser->selection->sym == NULL ||
+ browser->selection->map->dso->annotate_warned)
+ continue;
+ goto do_annotate;
+ case 'd':
+ goto zoom_dso;
+ case 't':
+ goto zoom_thread;
+ case 's':
+ if (ui_browser__input_window("Symbol to show",
+ "Please enter the name of symbol you want to see",
+ buf, "ENTER: OK, ESC: Cancel",
+ delay_secs * 2) == K_ENTER) {
+ self->symbol_filter_str = *buf ? buf : NULL;
+ hists__filter_by_symbol(self);
+ hist_browser__reset(browser);
+ }
+ continue;
+ case K_F1:
+ case 'h':
+ case '?':
+ ui_browser__help_window(&browser->b,
+ "h/?/F1 Show this window\n"
+ "UP/DOWN/PGUP\n"
+ "PGDN/SPACE Navigate\n"
+ "q/ESC/CTRL+C Exit browser\n\n"
+ "For multiple event sessions:\n\n"
+ "TAB/UNTAB Switch events\n\n"
+ "For symbolic views (--sort has sym):\n\n"
+ "-> Zoom into DSO/Threads & Annotate current symbol\n"
+ "<- Zoom out\n"
+ "a Annotate current symbol\n"
+ "C Collapse all callchains\n"
+ "E Expand all callchains\n"
+ "d Zoom into current DSO\n"
+ "t Zoom into current Thread\n"
+ "s Filter symbol by name");
+ continue;
+ case K_ENTER:
+ case K_RIGHT:
+ /* menu */
+ break;
+ case K_LEFT: {
+ const void *top;
+
+ if (pstack__empty(fstack)) {
+ /*
+ * Go back to the perf_evsel_menu__run or other user
+ */
+ if (left_exits)
+ goto out_free_stack;
+ continue;
+ }
+ top = pstack__pop(fstack);
+ if (top == &browser->hists->dso_filter)
+ goto zoom_out_dso;
+ if (top == &browser->hists->thread_filter)
+ goto zoom_out_thread;
+ continue;
+ }
+ case K_ESC:
+ if (!left_exits &&
+ !ui_browser__dialog_yesno(&browser->b,
+ "Do you really want to exit?"))
+ continue;
+ /* Fall thru */
+ case 'q':
+ case CTRL('c'):
+ goto out_free_stack;
+ default:
+ continue;
+ }
+
+ if (!browser->has_symbols)
+ goto add_exit_option;
+
+ if (sort__branch_mode == 1) {
+ bi = browser->he_selection->branch_info;
+ if (browser->selection != NULL &&
+ bi &&
+ bi->from.sym != NULL &&
+ !bi->from.map->dso->annotate_warned &&
+ asprintf(&options[nr_options], "Annotate %s",
+ bi->from.sym->name) > 0)
+ annotate_f = nr_options++;
+
+ if (browser->selection != NULL &&
+ bi &&
+ bi->to.sym != NULL &&
+ !bi->to.map->dso->annotate_warned &&
+ (bi->to.sym != bi->from.sym ||
+ bi->to.map->dso != bi->from.map->dso) &&
+ asprintf(&options[nr_options], "Annotate %s",
+ bi->to.sym->name) > 0)
+ annotate_t = nr_options++;
+ } else {
+
+ if (browser->selection != NULL &&
+ 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",
+ (browser->hists->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",
+ (browser->hists->dso_filter ? "out of" : "into"),
+ (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
+ zoom_dso = nr_options++;
+
+ if (browser->selection != NULL &&
+ browser->selection->map != NULL &&
+ asprintf(&options[nr_options], "Browse map details") > 0)
+ browse_map = nr_options++;
+add_exit_option:
+ options[nr_options++] = (char *)"Exit";
+retry_popup_menu:
+ choice = ui__popup_menu(nr_options, options);
+
+ if (choice == nr_options - 1)
+ break;
+
+ if (choice == -1) {
+ free_popup_options(options, nr_options - 1);
+ continue;
+ }
+
+ if (choice == annotate || choice == annotate_t || choice == annotate_f) {
+ struct hist_entry *he;
+ int err;
+do_annotate:
+ he = hist_browser__selected_entry(browser);
+ if (he == NULL)
+ continue;
+
+ /*
+ * we stash the branch_info symbol + map into the
+ * the ms so we don't have to rewrite all the annotation
+ * code to use branch_info.
+ * in branch mode, the ms struct is not used
+ */
+ if (choice == annotate_f) {
+ he->ms.sym = he->branch_info->from.sym;
+ he->ms.map = he->branch_info->from.map;
+ } else if (choice == annotate_t) {
+ he->ms.sym = he->branch_info->to.sym;
+ he->ms.map = he->branch_info->to.map;
+ }
+
+ /*
+ * Don't let this be freed, say, by hists__decay_entry.
+ */
+ he->used = true;
+ err = hist_entry__tui_annotate(he, evsel->idx,
+ timer, arg, delay_secs);
+ he->used = false;
+ /*
+ * offer option to annotate the other branch source or target
+ * (if they exists) when returning from annotate
+ */
+ if ((err == 'q' || err == CTRL('c'))
+ && annotate_t != -2 && annotate_f != -2)
+ goto retry_popup_menu;
+
+ ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
+ if (err)
+ ui_browser__handle_resize(&browser->b);
+
+ } else if (choice == browse_map)
+ map__browse(browser->selection->map);
+ else if (choice == zoom_dso) {
+zoom_dso:
+ if (browser->hists->dso_filter) {
+ pstack__remove(fstack, &browser->hists->dso_filter);
+zoom_out_dso:
+ ui_helpline__pop();
+ browser->hists->dso_filter = NULL;
+ sort_dso.elide = false;
+ } 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);
+ browser->hists->dso_filter = dso;
+ sort_dso.elide = true;
+ pstack__push(fstack, &browser->hists->dso_filter);
+ }
+ hists__filter_by_dso(self);
+ hist_browser__reset(browser);
+ } else if (choice == zoom_thread) {
+zoom_thread:
+ if (browser->hists->thread_filter) {
+ pstack__remove(fstack, &browser->hists->thread_filter);
+zoom_out_thread:
+ ui_helpline__pop();
+ browser->hists->thread_filter = NULL;
+ sort_thread.elide = false;
+ } else {
+ ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
+ thread->comm_set ? thread->comm : "",
+ thread->pid);
+ browser->hists->thread_filter = thread;
+ sort_thread.elide = true;
+ pstack__push(fstack, &browser->hists->thread_filter);
+ }
+ hists__filter_by_thread(self);
+ hist_browser__reset(browser);
+ }
+ }
+out_free_stack:
+ pstack__delete(fstack);
+out:
+ hist_browser__delete(browser);
+ free_popup_options(options, nr_options - 1);
+ return key;
+}
+
+struct perf_evsel_menu {
+ struct ui_browser b;
+ struct perf_evsel *selection;
+ bool lost_events, lost_events_warned;
+};
+
+static void perf_evsel_menu__write(struct ui_browser *browser,
+ void *entry, int row)
+{
+ struct perf_evsel_menu *menu = container_of(browser,
+ struct perf_evsel_menu, b);
+ struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
+ bool current_entry = ui_browser__is_current_entry(browser, row);
+ unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
+ const char *ev_name = event_name(evsel);
+ char bf[256], unit;
+ const char *warn = " ";
+ size_t printed;
+
+ ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
+ HE_COLORSET_NORMAL);
+
+ nr_events = convert_unit(nr_events, &unit);
+ printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
+ unit, unit == ' ' ? "" : " ", ev_name);
+ slsmg_printf("%s", bf);
+
+ nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
+ if (nr_events != 0) {
+ menu->lost_events = true;
+ if (!current_entry)
+ ui_browser__set_color(browser, HE_COLORSET_TOP);
+ nr_events = convert_unit(nr_events, &unit);
+ printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
+ nr_events, unit, unit == ' ' ? "" : " ");
+ warn = bf;
+ }
+
+ slsmg_write_nstring(warn, browser->width - printed);
+
+ if (current_entry)
+ menu->selection = evsel;
+}
+
+static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
+ int nr_events, const char *help,
+ void(*timer)(void *arg), void *arg, int delay_secs)
+{
+ struct perf_evlist *evlist = menu->b.priv;
+ struct perf_evsel *pos;
+ const char *ev_name, *title = "Available samples";
+ int key;
+
+ if (ui_browser__show(&menu->b, title,
+ "ESC: exit, ENTER|->: Browse histograms") < 0)
+ return -1;
+
+ while (1) {
+ key = ui_browser__run(&menu->b, delay_secs);
+
+ switch (key) {
+ case K_TIMER:
+ timer(arg);
+
+ if (!menu->lost_events_warned && menu->lost_events) {
+ ui_browser__warn_lost_events(&menu->b);
+ menu->lost_events_warned = true;
+ }
+ continue;
+ case K_RIGHT:
+ case K_ENTER:
+ if (!menu->selection)
+ continue;
+ pos = menu->selection;
+browse_hists:
+ perf_evlist__set_selected(evlist, pos);
+ /*
+ * Give the calling tool a chance to populate the non
+ * default evsel resorted hists tree.
+ */
+ if (timer)
+ timer(arg);
+ ev_name = event_name(pos);
+ key = perf_evsel__hists_browse(pos, nr_events, help,
+ ev_name, true, timer,
+ arg, delay_secs);
+ ui_browser__show_title(&menu->b, title);
+ switch (key) {
+ case K_TAB:
+ if (pos->node.next == &evlist->entries)
+ pos = list_entry(evlist->entries.next, struct perf_evsel, node);
+ else
+ pos = list_entry(pos->node.next, struct perf_evsel, node);
+ goto browse_hists;
+ case K_UNTAB:
+ if (pos->node.prev == &evlist->entries)
+ pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
+ else
+ pos = list_entry(pos->node.prev, struct perf_evsel, node);
+ goto browse_hists;
+ case K_ESC:
+ if (!ui_browser__dialog_yesno(&menu->b,
+ "Do you really want to exit?"))
+ continue;
+ /* Fall thru */
+ case 'q':
+ case CTRL('c'):
+ goto out;
+ default:
+ continue;
+ }
+ case K_LEFT:
+ continue;
+ case K_ESC:
+ if (!ui_browser__dialog_yesno(&menu->b,
+ "Do you really want to exit?"))
+ continue;
+ /* Fall thru */
+ case 'q':
+ case CTRL('c'):
+ goto out;
+ default:
+ continue;
+ }
+ }
+
+out:
+ ui_browser__hide(&menu->b);
+ return key;
+}
+
+static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
+ const char *help,
+ void(*timer)(void *arg), void *arg,
+ int delay_secs)
+{
+ struct perf_evsel *pos;
+ struct perf_evsel_menu menu = {
+ .b = {
+ .entries = &evlist->entries,
+ .refresh = ui_browser__list_head_refresh,
+ .seek = ui_browser__list_head_seek,
+ .write = perf_evsel_menu__write,
+ .nr_entries = evlist->nr_entries,
+ .priv = evlist,
+ },
+ };
+
+ ui_helpline__push("Press ESC to exit");
+
+ list_for_each_entry(pos, &evlist->entries, node) {
+ const char *ev_name = event_name(pos);
+ size_t line_len = strlen(ev_name) + 7;
+
+ if (menu.b.width < line_len)
+ menu.b.width = line_len;
+ /*
+ * Cache the evsel name, tracepoints have a _high_ cost per
+ * event_name() call.
+ */
+ if (pos->name == NULL)
+ pos->name = strdup(ev_name);
+ }
+
+ return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
+ arg, delay_secs);
+}
+
+int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
+ void(*timer)(void *arg), void *arg,
+ int delay_secs)
+{
+
+ if (evlist->nr_entries == 1) {
+ struct perf_evsel *first = list_entry(evlist->entries.next,
+ struct perf_evsel, node);
+ const char *ev_name = event_name(first);
+ return perf_evsel__hists_browse(first, evlist->nr_entries, help,
+ ev_name, false, timer, arg,
+ delay_secs);
+ }
+
+ return __perf_evlist__tui_browse_hists(evlist, help,
+ timer, arg, delay_secs);
+}
--- /dev/null
+#include "../libslang.h"
+#include <elf.h>
+#include <newt.h>
+#include <inttypes.h>
+#include <sys/ttydefaults.h>
+#include <string.h>
+#include <linux/bitops.h>
+#include "../../util/util.h"
+#include "../../util/debug.h"
+#include "../../util/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 err;
+}
+
+struct map_browser {
+ struct ui_browser b;
+ struct map *map;
+ 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 width;
+
+ ui_browser__set_percent_color(self, 0, current_entry);
+ slsmg_printf("%*" PRIx64 " %*" PRIx64 " %c ",
+ mb->addrlen, sym->start, mb->addrlen, sym->end,
+ sym->binding == STB_GLOBAL ? 'g' :
+ sym->binding == STB_LOCAL ? 'l' : 'w');
+ width = self->width - ((mb->addrlen * 2) + 4);
+ if (width > 0)
+ slsmg_write_nstring(sym->name, width);
+}
+
+/* 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)
+{
+ int key;
+
+ 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;
+
+ while (1) {
+ key = ui_browser__run(&self->b, 0);
+
+ if (verbose && key == '/')
+ map_browser__search(self);
+ else
+ break;
+ }
+
+ ui_browser__hide(&self->b);
+ return key;
+}
+
+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 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 (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), "%" PRIx64, maxaddr);
+ return map_browser__run(&mb);
+}
--- /dev/null
+#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_ */
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../debug.h"
+#include "helpline.h"
+#include "ui.h"
+#include "libslang.h"
+
+void ui_helpline__pop(void)
+{
+}
+
+char ui_helpline__current[512];
+
+void ui_helpline__push(const char *msg)
+{
+ const size_t sz = sizeof(ui_helpline__current);
+
+ SLsmg_gotorc(SLtt_Screen_Rows - 1, 0);
+ SLsmg_set_color(0);
+ SLsmg_write_nstring((char *)msg, SLtt_Screen_Cols);
+ SLsmg_refresh();
+ strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0';
+}
+
+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;
+
+ pthread_mutex_lock(&ui__lock);
+ ret = vscnprintf(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);
+ SLsmg_refresh();
+ backlog = 0;
+ }
+ pthread_mutex_unlock(&ui__lock);
+
+ return ret;
+}
--- /dev/null
+#ifndef _PERF_UI_HELPLINE_H_
+#define _PERF_UI_HELPLINE_H_ 1
+
+#include <stdio.h>
+#include <stdarg.h>
+
+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);
+
+extern char ui_helpline__current[];
+
+#endif /* _PERF_UI_HELPLINE_H_ */
--- /dev/null
+#ifndef _PERF_KEYSYMS_H_
+#define _PERF_KEYSYMS_H_ 1
+
+#include "libslang.h"
+
+#define K_DOWN SL_KEY_DOWN
+#define K_END SL_KEY_END
+#define K_ENTER '\r'
+#define K_ESC 033
+#define K_F1 SL_KEY_F(1)
+#define K_HOME SL_KEY_HOME
+#define K_LEFT SL_KEY_LEFT
+#define K_PGDN SL_KEY_NPAGE
+#define K_PGUP SL_KEY_PPAGE
+#define K_RIGHT SL_KEY_RIGHT
+#define K_TAB '\t'
+#define K_UNTAB SL_KEY_UNTAB
+#define K_UP SL_KEY_UP
+#define K_BKSPC 0x7f
+#define K_DEL SL_KEY_DELETE
+
+/* Not really keys */
+#define K_TIMER -1
+#define K_ERROR -2
+#define K_RESIZE -3
+
+#endif /* _PERF_KEYSYMS_H_ */
--- /dev/null
+#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
+
+#define SL_KEY_UNTAB 0x1000
+
+#endif /* _PERF_UI_SLANG_H_ */
--- /dev/null
+#include "../cache.h"
+#include "progress.h"
+#include "libslang.h"
+#include "ui.h"
+#include "browser.h"
+
+void ui_progress__update(u64 curr, u64 total, const char *title)
+{
+ int bar, y;
+ /*
+ * 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;
+
+ if (total == 0)
+ return;
+
+ ui__refresh_dimensions(true);
+ pthread_mutex_lock(&ui__lock);
+ y = SLtt_Screen_Rows / 2 - 2;
+ SLsmg_set_color(0);
+ SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols);
+ SLsmg_gotorc(y++, 1);
+ SLsmg_write_string((char *)title);
+ SLsmg_set_color(HE_COLORSET_SELECTED);
+ bar = ((SLtt_Screen_Cols - 2) * curr) / total;
+ SLsmg_fill_region(y, 1, 1, bar, ' ');
+ SLsmg_refresh();
+ pthread_mutex_unlock(&ui__lock);
+}
--- /dev/null
+#ifndef _PERF_UI_PROGRESS_H_
+#define _PERF_UI_PROGRESS_H_ 1
+
+#include <../types.h>
+
+void ui_progress__update(u64 curr, u64 total, const char *title);
+
+#endif
--- /dev/null
+#include <newt.h>
+#include <signal.h>
+#include <stdbool.h>
+
+#include "../cache.h"
+#include "../debug.h"
+#include "browser.h"
+#include "helpline.h"
+#include "ui.h"
+#include "util.h"
+#include "libslang.h"
+#include "keysyms.h"
+
+pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
+
+static volatile int ui__need_resize;
+
+void ui__refresh_dimensions(bool force)
+{
+ if (force || ui__need_resize) {
+ ui__need_resize = 0;
+ pthread_mutex_lock(&ui__lock);
+ SLtt_get_screen_size();
+ SLsmg_reinit_smg();
+ pthread_mutex_unlock(&ui__lock);
+ }
+}
+
+static void ui__sigwinch(int sig __used)
+{
+ ui__need_resize = 1;
+}
+
+static void ui__setup_sigwinch(void)
+{
+ static bool done;
+
+ if (done)
+ return;
+
+ done = true;
+ pthread__unblock_sigwinch();
+ signal(SIGWINCH, ui__sigwinch);
+}
+
+int ui__getch(int delay_secs)
+{
+ struct timeval timeout, *ptimeout = delay_secs ? &timeout : NULL;
+ fd_set read_set;
+ int err, key;
+
+ ui__setup_sigwinch();
+
+ FD_ZERO(&read_set);
+ FD_SET(0, &read_set);
+
+ if (delay_secs) {
+ timeout.tv_sec = delay_secs;
+ timeout.tv_usec = 0;
+ }
+
+ err = select(1, &read_set, NULL, NULL, ptimeout);
+
+ if (err == 0)
+ return K_TIMER;
+
+ if (err == -1) {
+ if (errno == EINTR)
+ return K_RESIZE;
+ return K_ERROR;
+ }
+
+ key = SLang_getkey();
+ if (key != K_ESC)
+ return key;
+
+ FD_ZERO(&read_set);
+ FD_SET(0, &read_set);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 20;
+ err = select(1, &read_set, NULL, NULL, &timeout);
+ if (err == 0)
+ return K_ESC;
+
+ SLang_ungetkey(key);
+ return SLkp_getkey();
+}
+
+static void newt_suspend(void *d __used)
+{
+ newtSuspend();
+ raise(SIGTSTP);
+ newtResume();
+}
+
+static int ui__init(void)
+{
+ int err = SLkp_init();
+
+ if (err < 0)
+ goto out;
+
+ SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB);
+out:
+ return err;
+}
+
+static void ui__exit(void)
+{
+ SLtt_set_cursor_visibility(1);
+ SLsmg_refresh();
+ SLsmg_reset_smg();
+ SLang_reset_tty();
+}
+
+static void ui__signal(int sig)
+{
+ ui__exit();
+ psignal(sig, "perf");
+ exit(0);
+}
+
+void setup_browser(bool fallback_to_pager)
+{
+ if (!isatty(1) || !use_browser || dump_trace) {
+ use_browser = 0;
+ if (fallback_to_pager)
+ setup_pager();
+ return;
+ }
+
+ use_browser = 1;
+ newtInit();
+ ui__init();
+ newtSetSuspendCallback(newt_suspend, NULL);
+ ui_helpline__init();
+ ui_browser__init();
+
+ signal(SIGSEGV, ui__signal);
+ signal(SIGFPE, ui__signal);
+ signal(SIGINT, ui__signal);
+ signal(SIGQUIT, ui__signal);
+ signal(SIGTERM, ui__signal);
+}
+
+void exit_browser(bool wait_for_ok)
+{
+ if (use_browser > 0) {
+ if (wait_for_ok)
+ ui__question_window("Fatal Error",
+ ui_helpline__last_msg,
+ "Press any key...", 0);
+ ui__exit();
+ }
+}
--- /dev/null
+#ifndef _PERF_UI_H_
+#define _PERF_UI_H_ 1
+
+#include <pthread.h>
+#include <stdbool.h>
+
+extern pthread_mutex_t ui__lock;
+
+void ui__refresh_dimensions(bool force);
+
+#endif /* _PERF_UI_H_ */
--- /dev/null
+#include "../util.h"
+#include <signal.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/ttydefaults.h>
+
+#include "../cache.h"
+#include "../debug.h"
+#include "browser.h"
+#include "keysyms.h"
+#include "helpline.h"
+#include "ui.h"
+#include "util.h"
+#include "libslang.h"
+
+static void ui_browser__argv_write(struct ui_browser *browser,
+ void *entry, int row)
+{
+ char **arg = entry;
+ bool current_entry = ui_browser__is_current_entry(browser, row);
+
+ ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
+ HE_COLORSET_NORMAL);
+ slsmg_write_nstring(*arg, browser->width);
+}
+
+static int popup_menu__run(struct ui_browser *menu)
+{
+ int key;
+
+ if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0)
+ return -1;
+
+ while (1) {
+ key = ui_browser__run(menu, 0);
+
+ switch (key) {
+ case K_RIGHT:
+ case K_ENTER:
+ key = menu->index;
+ break;
+ case K_LEFT:
+ case K_ESC:
+ case 'q':
+ case CTRL('c'):
+ key = -1;
+ break;
+ default:
+ continue;
+ }
+
+ break;
+ }
+
+ ui_browser__hide(menu);
+ return key;
+}
+
+int ui__popup_menu(int argc, char * const argv[])
+{
+ struct ui_browser menu = {
+ .entries = (void *)argv,
+ .refresh = ui_browser__argv_refresh,
+ .seek = ui_browser__argv_seek,
+ .write = ui_browser__argv_write,
+ .nr_entries = argc,
+ };
+
+ return popup_menu__run(&menu);
+}
+
+int ui_browser__input_window(const char *title, const char *text, char *input,
+ const char *exit_msg, int delay_secs)
+{
+ int x, y, len, key;
+ int max_len = 60, nr_lines = 0;
+ static char buf[50];
+ const char *t;
+
+ t = text;
+ while (1) {
+ const char *sep = strchr(t, '\n');
+
+ 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;
+ }
+
+ max_len += 2;
+ nr_lines += 8;
+ y = SLtt_Screen_Rows / 2 - nr_lines / 2;
+ x = SLtt_Screen_Cols / 2 - max_len / 2;
+
+ SLsmg_set_color(0);
+ SLsmg_draw_box(y, x++, nr_lines, max_len);
+ if (title) {
+ SLsmg_gotorc(y, x + 1);
+ SLsmg_write_string((char *)title);
+ }
+ SLsmg_gotorc(++y, x);
+ nr_lines -= 7;
+ max_len -= 2;
+ SLsmg_write_wrapped_string((unsigned char *)text, y, x,
+ nr_lines, max_len, 1);
+ y += nr_lines;
+ len = 5;
+ while (len--) {
+ SLsmg_gotorc(y + len - 1, x);
+ SLsmg_write_nstring((char *)" ", max_len);
+ }
+ SLsmg_draw_box(y++, x + 1, 3, max_len - 2);
+
+ SLsmg_gotorc(y + 3, x);
+ SLsmg_write_nstring((char *)exit_msg, max_len);
+ SLsmg_refresh();
+
+ x += 2;
+ len = 0;
+ key = ui__getch(delay_secs);
+ while (key != K_TIMER && key != K_ENTER && key != K_ESC) {
+ if (key == K_BKSPC) {
+ if (len == 0)
+ goto next_key;
+ SLsmg_gotorc(y, x + --len);
+ SLsmg_write_char(' ');
+ } else {
+ buf[len] = key;
+ SLsmg_gotorc(y, x + len++);
+ SLsmg_write_char(key);
+ }
+ SLsmg_refresh();
+
+ /* XXX more graceful overflow handling needed */
+ if (len == sizeof(buf) - 1) {
+ ui_helpline__push("maximum size of symbol name reached!");
+ key = K_ENTER;
+ break;
+ }
+next_key:
+ key = ui__getch(delay_secs);
+ }
+
+ buf[len] = '\0';
+ strncpy(input, buf, len+1);
+ return key;
+}
+
+int ui__question_window(const char *title, const char *text,
+ const char *exit_msg, int delay_secs)
+{
+ int x, y;
+ int max_len = 0, nr_lines = 0;
+ const char *t;
+
+ 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;
+ }
+
+ max_len += 2;
+ nr_lines += 4;
+ y = SLtt_Screen_Rows / 2 - nr_lines / 2,
+ x = SLtt_Screen_Cols / 2 - max_len / 2;
+
+ SLsmg_set_color(0);
+ SLsmg_draw_box(y, x++, nr_lines, max_len);
+ if (title) {
+ SLsmg_gotorc(y, x + 1);
+ SLsmg_write_string((char *)title);
+ }
+ SLsmg_gotorc(++y, x);
+ nr_lines -= 2;
+ max_len -= 2;
+ SLsmg_write_wrapped_string((unsigned char *)text, y, x,
+ nr_lines, max_len, 1);
+ SLsmg_gotorc(y + nr_lines - 2, x);
+ SLsmg_write_nstring((char *)" ", max_len);
+ SLsmg_gotorc(y + nr_lines - 1, x);
+ SLsmg_write_nstring((char *)exit_msg, max_len);
+ SLsmg_refresh();
+ return ui__getch(delay_secs);
+}
+
+int ui__help_window(const char *text)
+{
+ return ui__question_window("Help", text, "Press any key...", 0);
+}
+
+int ui__dialog_yesno(const char *msg)
+{
+ return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0);
+}
+
+int __ui__warning(const char *title, const char *format, va_list args)
+{
+ char *s;
+
+ if (use_browser > 0 && vasprintf(&s, format, args) > 0) {
+ int key;
+
+ pthread_mutex_lock(&ui__lock);
+ key = ui__question_window(title, s, "Press any key...", 0);
+ pthread_mutex_unlock(&ui__lock);
+ free(s);
+ return key;
+ }
+
+ fprintf(stderr, "%s:\n", title);
+ vfprintf(stderr, format, args);
+ return K_ESC;
+}
+
+int ui__warning(const char *format, ...)
+{
+ int key;
+ va_list args;
+
+ va_start(args, format);
+ key = __ui__warning("Warning", format, args);
+ va_end(args);
+ return key;
+}
+
+int ui__error(const char *format, ...)
+{
+ int key;
+ va_list args;
+
+ va_start(args, format);
+ key = __ui__warning("Error", format, args);
+ va_end(args);
+ return key;
+}
--- /dev/null
+#ifndef _PERF_UI_UTIL_H_
+#define _PERF_UI_UTIL_H_ 1
+
+#include <stdarg.h>
+
+int ui__getch(int delay_secs);
+int ui__popup_menu(int argc, char * const argv[]);
+int ui__help_window(const char *text);
+int ui__dialog_yesno(const char *msg);
+int ui__question_window(const char *title, const char *text,
+ const char *exit_msg, int delay_secs);
+int __ui__warning(const char *title, const char *format, va_list args);
+
+#endif /* _PERF_UI_UTIL_H_ */
#else
extern char ui_helpline__last_msg[];
int ui_helpline__show_help(const char *format, va_list ap);
-#include "ui/progress.h"
+#include "../ui/progress.h"
int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2)));
#endif
#define K_LEFT -1
#define K_RIGHT -2
#else
-#include "ui/keysyms.h"
+#include "../ui/keysyms.h"
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
void(*timer)(void *arg), void *arg, int delay_secs);
+++ /dev/null
-#include "../util.h"
-#include "../cache.h"
-#include "../../perf.h"
-#include "libslang.h"
-#include <newt.h>
-#include "ui.h"
-#include "util.h"
-#include <linux/compiler.h>
-#include <linux/list.h>
-#include <linux/rbtree.h>
-#include <stdlib.h>
-#include <sys/ttydefaults.h>
-#include "browser.h"
-#include "helpline.h"
-#include "keysyms.h"
-#include "../color.h"
-
-static int ui_browser__percent_color(struct ui_browser *browser,
- double percent, bool current)
-{
- if (current && (!browser->use_navkeypressed || browser->navkeypressed))
- return HE_COLORSET_SELECTED;
- if (percent >= MIN_RED)
- return HE_COLORSET_TOP;
- if (percent >= MIN_GREEN)
- return HE_COLORSET_MEDIUM;
- return HE_COLORSET_NORMAL;
-}
-
-int ui_browser__set_color(struct ui_browser *browser, int color)
-{
- int ret = browser->current_color;
- browser->current_color = color;
- SLsmg_set_color(color);
- return ret;
-}
-
-void ui_browser__set_percent_color(struct ui_browser *self,
- double percent, bool current)
-{
- int color = ui_browser__percent_color(self, percent, current);
- ui_browser__set_color(self, color);
-}
-
-void ui_browser__gotorc(struct ui_browser *self, int y, int x)
-{
- SLsmg_gotorc(self->y + y, self->x + x);
-}
-
-static struct list_head *
-ui_browser__list_head_filter_entries(struct ui_browser *browser,
- struct list_head *pos)
-{
- do {
- if (!browser->filter || !browser->filter(browser, pos))
- return pos;
- pos = pos->next;
- } while (pos != browser->entries);
-
- return NULL;
-}
-
-static struct list_head *
-ui_browser__list_head_filter_prev_entries(struct ui_browser *browser,
- struct list_head *pos)
-{
- do {
- if (!browser->filter || !browser->filter(browser, pos))
- return pos;
- pos = pos->prev;
- } while (pos != browser->entries);
-
- return NULL;
-}
-
-void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence)
-{
- struct list_head *head = self->entries;
- struct list_head *pos;
-
- if (self->nr_entries == 0)
- return;
-
- switch (whence) {
- case SEEK_SET:
- pos = ui_browser__list_head_filter_entries(self, head->next);
- break;
- case SEEK_CUR:
- pos = self->top;
- break;
- case SEEK_END:
- pos = ui_browser__list_head_filter_prev_entries(self, head->prev);
- break;
- default:
- return;
- }
-
- assert(pos != NULL);
-
- if (offset > 0) {
- while (offset-- != 0)
- pos = ui_browser__list_head_filter_entries(self, pos->next);
- } else {
- while (offset++ != 0)
- pos = ui_browser__list_head_filter_prev_entries(self, pos->prev);
- }
-
- self->top = pos;
-}
-
-void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
-{
- struct rb_root *root = self->entries;
- struct rb_node *nd;
-
- switch (whence) {
- case SEEK_SET:
- nd = rb_first(root);
- break;
- case SEEK_CUR:
- nd = self->top;
- break;
- case SEEK_END:
- nd = rb_last(root);
- break;
- default:
- return;
- }
-
- if (offset > 0) {
- while (offset-- != 0)
- nd = rb_next(nd);
- } else {
- while (offset++ != 0)
- nd = rb_prev(nd);
- }
-
- self->top = nd;
-}
-
-unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
-{
- struct rb_node *nd;
- int row = 0;
-
- if (self->top == NULL)
- self->top = rb_first(self->entries);
-
- nd = self->top;
-
- while (nd != NULL) {
- ui_browser__gotorc(self, row, 0);
- self->write(self, nd, row);
- if (++row == self->height)
- break;
- nd = rb_next(nd);
- }
-
- return row;
-}
-
-bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
-{
- return self->top_idx + row == self->index;
-}
-
-void ui_browser__refresh_dimensions(struct ui_browser *self)
-{
- self->width = SLtt_Screen_Cols - 1;
- self->height = SLtt_Screen_Rows - 2;
- self->y = 1;
- self->x = 0;
-}
-
-void ui_browser__handle_resize(struct ui_browser *browser)
-{
- ui__refresh_dimensions(false);
- ui_browser__show(browser, browser->title, ui_helpline__current);
- ui_browser__refresh(browser);
-}
-
-int ui_browser__warning(struct ui_browser *browser, int timeout,
- const char *format, ...)
-{
- va_list args;
- char *text;
- int key = 0, err;
-
- va_start(args, format);
- err = vasprintf(&text, format, args);
- va_end(args);
-
- if (err < 0) {
- va_start(args, format);
- ui_helpline__vpush(format, args);
- va_end(args);
- } else {
- while ((key == ui__question_window("Warning!", text,
- "Press any key...",
- timeout)) == K_RESIZE)
- ui_browser__handle_resize(browser);
- free(text);
- }
-
- return key;
-}
-
-int ui_browser__help_window(struct ui_browser *browser, const char *text)
-{
- int key;
-
- while ((key = ui__help_window(text)) == K_RESIZE)
- ui_browser__handle_resize(browser);
-
- return key;
-}
-
-bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text)
-{
- int key;
-
- while ((key = ui__dialog_yesno(text)) == K_RESIZE)
- ui_browser__handle_resize(browser);
-
- return key == K_ENTER || toupper(key) == 'Y';
-}
-
-void ui_browser__reset_index(struct ui_browser *self)
-{
- self->index = self->top_idx = 0;
- self->seek(self, 0, SEEK_SET);
-}
-
-void __ui_browser__show_title(struct ui_browser *browser, const char *title)
-{
- SLsmg_gotorc(0, 0);
- ui_browser__set_color(browser, NEWT_COLORSET_ROOT);
- slsmg_write_nstring(title, browser->width + 1);
-}
-
-void ui_browser__show_title(struct ui_browser *browser, const char *title)
-{
- pthread_mutex_lock(&ui__lock);
- __ui_browser__show_title(browser, title);
- pthread_mutex_unlock(&ui__lock);
-}
-
-int ui_browser__show(struct ui_browser *self, const char *title,
- const char *helpline, ...)
-{
- int err;
- va_list ap;
-
- ui_browser__refresh_dimensions(self);
-
- pthread_mutex_lock(&ui__lock);
- __ui_browser__show_title(self, title);
-
- self->title = title;
- free(self->helpline);
- self->helpline = NULL;
-
- va_start(ap, helpline);
- err = vasprintf(&self->helpline, helpline, ap);
- va_end(ap);
- if (err > 0)
- ui_helpline__push(self->helpline);
- pthread_mutex_unlock(&ui__lock);
- return err ? 0 : -1;
-}
-
-void ui_browser__hide(struct ui_browser *browser __used)
-{
- pthread_mutex_lock(&ui__lock);
- ui_helpline__pop();
- pthread_mutex_unlock(&ui__lock);
-}
-
-static void ui_browser__scrollbar_set(struct ui_browser *browser)
-{
- int height = browser->height, h = 0, pct = 0,
- col = browser->width,
- row = browser->y - 1;
-
- if (browser->nr_entries > 1) {
- pct = ((browser->index * (browser->height - 1)) /
- (browser->nr_entries - 1));
- }
-
- SLsmg_set_char_set(1);
-
- while (h < height) {
- ui_browser__gotorc(browser, row++, col);
- SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR);
- ++h;
- }
-
- SLsmg_set_char_set(0);
-}
-
-static int __ui_browser__refresh(struct ui_browser *browser)
-{
- int row;
- int width = browser->width;
-
- row = browser->refresh(browser);
- ui_browser__set_color(browser, HE_COLORSET_NORMAL);
-
- if (!browser->use_navkeypressed || browser->navkeypressed)
- ui_browser__scrollbar_set(browser);
- else
- width += 1;
-
- SLsmg_fill_region(browser->y + row, browser->x,
- browser->height - row, width, ' ');
-
- return 0;
-}
-
-int ui_browser__refresh(struct ui_browser *browser)
-{
- pthread_mutex_lock(&ui__lock);
- __ui_browser__refresh(browser);
- pthread_mutex_unlock(&ui__lock);
-
- return 0;
-}
-
-/*
- * Here we're updating nr_entries _after_ we started browsing, i.e. we have to
- * forget about any reference to any entry in the underlying data structure,
- * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser
- * after an output_resort and hist decay.
- */
-void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
-{
- off_t offset = nr_entries - browser->nr_entries;
-
- browser->nr_entries = nr_entries;
-
- if (offset < 0) {
- if (browser->top_idx < (u64)-offset)
- offset = -browser->top_idx;
-
- browser->index += offset;
- browser->top_idx += offset;
- }
-
- browser->top = NULL;
- browser->seek(browser, browser->top_idx, SEEK_SET);
-}
-
-int ui_browser__run(struct ui_browser *self, int delay_secs)
-{
- int err, key;
-
- while (1) {
- off_t offset;
-
- pthread_mutex_lock(&ui__lock);
- err = __ui_browser__refresh(self);
- SLsmg_refresh();
- pthread_mutex_unlock(&ui__lock);
- if (err < 0)
- break;
-
- key = ui__getch(delay_secs);
-
- if (key == K_RESIZE) {
- ui__refresh_dimensions(false);
- ui_browser__refresh_dimensions(self);
- __ui_browser__show_title(self, self->title);
- ui_helpline__puts(self->helpline);
- continue;
- }
-
- if (self->use_navkeypressed && !self->navkeypressed) {
- if (key == K_DOWN || key == K_UP ||
- key == K_PGDN || key == K_PGUP ||
- key == K_HOME || key == K_END ||
- key == ' ') {
- self->navkeypressed = true;
- continue;
- } else
- return key;
- }
-
- switch (key) {
- case K_DOWN:
- if (self->index == self->nr_entries - 1)
- break;
- ++self->index;
- if (self->index == self->top_idx + self->height) {
- ++self->top_idx;
- self->seek(self, +1, SEEK_CUR);
- }
- break;
- case K_UP:
- if (self->index == 0)
- break;
- --self->index;
- if (self->index < self->top_idx) {
- --self->top_idx;
- self->seek(self, -1, SEEK_CUR);
- }
- break;
- case K_PGDN:
- case ' ':
- 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->top_idx += offset;
- self->seek(self, +offset, SEEK_CUR);
- break;
- case K_PGUP:
- if (self->top_idx == 0)
- break;
-
- if (self->top_idx < self->height)
- offset = self->top_idx;
- else
- offset = self->height;
-
- self->index -= offset;
- self->top_idx -= offset;
- self->seek(self, -offset, SEEK_CUR);
- break;
- case K_HOME:
- ui_browser__reset_index(self);
- break;
- case K_END:
- offset = self->height - 1;
- if (offset >= self->nr_entries)
- offset = self->nr_entries - 1;
-
- self->index = self->nr_entries - 1;
- self->top_idx = self->index - offset;
- self->seek(self, -offset, SEEK_END);
- break;
- default:
- return key;
- }
- }
- return -1;
-}
-
-unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
-{
- struct list_head *pos;
- struct list_head *head = self->entries;
- int row = 0;
-
- if (self->top == NULL || self->top == self->entries)
- self->top = ui_browser__list_head_filter_entries(self, head->next);
-
- pos = self->top;
-
- list_for_each_from(pos, head) {
- if (!self->filter || !self->filter(self, pos)) {
- ui_browser__gotorc(self, row, 0);
- self->write(self, pos, row);
- if (++row == self->height)
- break;
- }
- }
-
- return row;
-}
-
-static struct ui_browser__colorset {
- const char *name, *fg, *bg;
- int colorset;
-} ui_browser__colorsets[] = {
- {
- .colorset = HE_COLORSET_TOP,
- .name = "top",
- .fg = "red",
- .bg = "default",
- },
- {
- .colorset = HE_COLORSET_MEDIUM,
- .name = "medium",
- .fg = "green",
- .bg = "default",
- },
- {
- .colorset = HE_COLORSET_NORMAL,
- .name = "normal",
- .fg = "default",
- .bg = "default",
- },
- {
- .colorset = HE_COLORSET_SELECTED,
- .name = "selected",
- .fg = "black",
- .bg = "lightgray",
- },
- {
- .colorset = HE_COLORSET_CODE,
- .name = "code",
- .fg = "blue",
- .bg = "default",
- },
- {
- .colorset = HE_COLORSET_ADDR,
- .name = "addr",
- .fg = "magenta",
- .bg = "default",
- },
- {
- .name = NULL,
- }
-};
-
-
-static int ui_browser__color_config(const char *var, const char *value,
- void *data __used)
-{
- char *fg = NULL, *bg;
- int i;
-
- /* same dir for all commands */
- if (prefixcmp(var, "colors.") != 0)
- return 0;
-
- for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
- const char *name = var + 7;
-
- if (strcmp(ui_browser__colorsets[i].name, name) != 0)
- continue;
-
- fg = strdup(value);
- if (fg == NULL)
- break;
-
- bg = strchr(fg, ',');
- if (bg == NULL)
- break;
-
- *bg = '\0';
- while (isspace(*++bg));
- ui_browser__colorsets[i].bg = bg;
- ui_browser__colorsets[i].fg = fg;
- return 0;
- }
-
- free(fg);
- return -1;
-}
-
-void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
-{
- switch (whence) {
- case SEEK_SET:
- browser->top = browser->entries;
- break;
- case SEEK_CUR:
- browser->top = browser->top + browser->top_idx + offset;
- break;
- case SEEK_END:
- browser->top = browser->top + browser->nr_entries + offset;
- break;
- default:
- return;
- }
-}
-
-unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
-{
- unsigned int row = 0, idx = browser->top_idx;
- char **pos;
-
- if (browser->top == NULL)
- browser->top = browser->entries;
-
- pos = (char **)browser->top;
- while (idx < browser->nr_entries) {
- if (!browser->filter || !browser->filter(browser, *pos)) {
- ui_browser__gotorc(browser, row, 0);
- browser->write(browser, pos, row);
- if (++row == browser->height)
- break;
- }
-
- ++idx;
- ++pos;
- }
-
- return row;
-}
-
-void ui_browser__init(void)
-{
- int i = 0;
-
- perf_config(ui_browser__color_config, NULL);
-
- while (ui_browser__colorsets[i].name) {
- struct ui_browser__colorset *c = &ui_browser__colorsets[i++];
- sltt_set_color(c->colorset, c->name, c->fg, c->bg);
- }
-}
+++ /dev/null
-#ifndef _PERF_UI_BROWSER_H_
-#define _PERF_UI_BROWSER_H_ 1
-
-#include <stdbool.h>
-#include <sys/types.h>
-#include "../types.h"
-
-#define HE_COLORSET_TOP 50
-#define HE_COLORSET_MEDIUM 51
-#define HE_COLORSET_NORMAL 52
-#define HE_COLORSET_SELECTED 53
-#define HE_COLORSET_CODE 54
-#define HE_COLORSET_ADDR 55
-
-struct ui_browser {
- u64 index, top_idx;
- void *top, *entries;
- u16 y, x, width, height;
- int current_color;
- void *priv;
- const char *title;
- char *helpline;
- unsigned int (*refresh)(struct ui_browser *self);
- void (*write)(struct ui_browser *self, void *entry, int row);
- void (*seek)(struct ui_browser *self, off_t offset, int whence);
- bool (*filter)(struct ui_browser *self, void *entry);
- u32 nr_entries;
- bool navkeypressed;
- bool use_navkeypressed;
-};
-
-int ui_browser__set_color(struct ui_browser *browser, int color);
-void ui_browser__set_percent_color(struct ui_browser *self,
- double percent, bool current);
-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);
-
-void ui_browser__gotorc(struct ui_browser *self, int y, int x);
-void __ui_browser__show_title(struct ui_browser *browser, const char *title);
-void ui_browser__show_title(struct ui_browser *browser, 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 *browser, int delay_secs);
-void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries);
-void ui_browser__handle_resize(struct ui_browser *browser);
-
-int ui_browser__warning(struct ui_browser *browser, int timeout,
- const char *format, ...);
-int ui_browser__help_window(struct ui_browser *browser, const char *text);
-bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text);
-int ui_browser__input_window(const char *title, const char *text, char *input,
- const char *exit_msg, int delay_sec);
-
-void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence);
-unsigned int ui_browser__argv_refresh(struct ui_browser *browser);
-
-void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence);
-unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self);
-
-void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence);
-unsigned int ui_browser__list_head_refresh(struct ui_browser *self);
-
-void ui_browser__init(void);
-#endif /* _PERF_UI_BROWSER_H_ */
+++ /dev/null
-#include "../../util.h"
-#include "../browser.h"
-#include "../helpline.h"
-#include "../libslang.h"
-#include "../ui.h"
-#include "../util.h"
-#include "../../annotate.h"
-#include "../../hist.h"
-#include "../../sort.h"
-#include "../../symbol.h"
-#include <pthread.h>
-#include <newt.h>
-
-struct annotate_browser {
- struct ui_browser b;
- struct rb_root entries;
- struct rb_node *curr_hot;
- struct objdump_line *selection;
- u64 start;
- int nr_asm_entries;
- int nr_entries;
- bool hide_src_code;
- bool use_offset;
- bool searching_backwards;
- char search_bf[128];
-};
-
-struct objdump_line_rb_node {
- struct rb_node rb_node;
- double percent;
- u32 idx;
- int idx_asm;
-};
-
-static inline
-struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
-{
- return (struct objdump_line_rb_node *)(self + 1);
-}
-
-static bool objdump_line__filter(struct ui_browser *browser, void *entry)
-{
- struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
-
- if (ab->hide_src_code) {
- struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
- return ol->offset == -1;
- }
-
- return false;
-}
-
-static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
-{
- struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
- struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
- bool current_entry = ui_browser__is_current_entry(self, row);
- bool change_color = (!ab->hide_src_code &&
- (!current_entry || (self->use_navkeypressed &&
- !self->navkeypressed)));
- int width = self->width;
-
- if (ol->offset != -1) {
- struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
- ui_browser__set_percent_color(self, olrb->percent, current_entry);
- slsmg_printf(" %7.2f ", olrb->percent);
- } else {
- ui_browser__set_percent_color(self, 0, current_entry);
- slsmg_write_nstring(" ", 9);
- }
-
- SLsmg_write_char(':');
- slsmg_write_nstring(" ", 8);
-
- /* The scroll bar isn't being used */
- if (!self->navkeypressed)
- width += 1;
-
- if (ol->offset != -1 && change_color)
- ui_browser__set_color(self, HE_COLORSET_CODE);
-
- if (!*ol->line)
- slsmg_write_nstring(" ", width - 18);
- else if (ol->offset == -1)
- slsmg_write_nstring(ol->line, width - 18);
- else {
- char bf[64];
- u64 addr = ol->offset;
- int printed, color = -1;
-
- if (!ab->use_offset)
- addr += ab->start;
-
- printed = scnprintf(bf, sizeof(bf), " %" PRIx64 ":", addr);
- if (change_color)
- color = ui_browser__set_color(self, HE_COLORSET_ADDR);
- slsmg_write_nstring(bf, printed);
- if (change_color)
- ui_browser__set_color(self, color);
- slsmg_write_nstring(ol->line, width - 18 - printed);
- }
-
- if (current_entry)
- ab->selection = ol;
-}
-
-static double objdump_line__calc_percent(struct objdump_line *self,
- struct symbol *sym, int evidx)
-{
- double percent = 0.0;
-
- if (self->offset != -1) {
- int len = sym->end - sym->start;
- unsigned int hits = 0;
- struct annotation *notes = symbol__annotation(sym);
- struct source_line *src_line = notes->src->lines;
- struct sym_hist *h = annotation__histogram(notes, evidx);
- s64 offset = self->offset;
- struct objdump_line *next;
-
- next = objdump__get_next_ip_line(¬es->src->source, self);
- while (offset < (s64)len &&
- (next == NULL || offset < next->offset)) {
- if (src_line) {
- percent += src_line[offset].percent;
- } else
- hits += h->addr[offset];
-
- ++offset;
- }
- /*
- * If the percentage wasn't already calculated in
- * symbol__get_source_line, do it now:
- */
- if (src_line == 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 objdump_line *pos, u32 idx)
-{
- unsigned back;
-
- ui_browser__refresh_dimensions(&self->b);
- back = self->b.height / 2;
- self->b.top_idx = self->b.index = idx;
-
- while (self->b.top_idx != 0 && back != 0) {
- pos = list_entry(pos->node.prev, struct objdump_line, node);
-
- if (objdump_line__filter(&self->b, &pos->node))
- continue;
-
- --self->b.top_idx;
- --back;
- }
-
- self->b.top = pos;
- self->b.navkeypressed = true;
-}
-
-static void annotate_browser__set_rb_top(struct annotate_browser *browser,
- struct rb_node *nd)
-{
- struct objdump_line_rb_node *rbpos;
- struct objdump_line *pos;
-
- rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
- pos = ((struct objdump_line *)rbpos) - 1;
- annotate_browser__set_top(browser, pos, rbpos->idx);
- browser->curr_hot = nd;
-}
-
-static void annotate_browser__calc_percent(struct annotate_browser *browser,
- int evidx)
-{
- struct map_symbol *ms = browser->b.priv;
- struct symbol *sym = ms->sym;
- struct annotation *notes = symbol__annotation(sym);
- struct objdump_line *pos;
-
- browser->entries = RB_ROOT;
-
- pthread_mutex_lock(¬es->lock);
-
- list_for_each_entry(pos, ¬es->src->source, node) {
- struct objdump_line_rb_node *rbpos = objdump_line__rb(pos);
- rbpos->percent = objdump_line__calc_percent(pos, sym, evidx);
- if (rbpos->percent < 0.01) {
- RB_CLEAR_NODE(&rbpos->rb_node);
- continue;
- }
- objdump__insert_line(&browser->entries, rbpos);
- }
- pthread_mutex_unlock(¬es->lock);
-
- browser->curr_hot = rb_last(&browser->entries);
-}
-
-static bool annotate_browser__toggle_source(struct annotate_browser *browser)
-{
- struct objdump_line *ol;
- struct objdump_line_rb_node *olrb;
- off_t offset = browser->b.index - browser->b.top_idx;
-
- browser->b.seek(&browser->b, offset, SEEK_CUR);
- ol = list_entry(browser->b.top, struct objdump_line, node);
- olrb = objdump_line__rb(ol);
-
- if (browser->hide_src_code) {
- if (olrb->idx_asm < offset)
- offset = olrb->idx;
-
- browser->b.nr_entries = browser->nr_entries;
- browser->hide_src_code = false;
- browser->b.seek(&browser->b, -offset, SEEK_CUR);
- browser->b.top_idx = olrb->idx - offset;
- browser->b.index = olrb->idx;
- } else {
- if (olrb->idx_asm < 0) {
- ui_helpline__puts("Only available for assembly lines.");
- browser->b.seek(&browser->b, -offset, SEEK_CUR);
- return false;
- }
-
- if (olrb->idx_asm < offset)
- offset = olrb->idx_asm;
-
- browser->b.nr_entries = browser->nr_asm_entries;
- browser->hide_src_code = true;
- browser->b.seek(&browser->b, -offset, SEEK_CUR);
- browser->b.top_idx = olrb->idx_asm - offset;
- browser->b.index = olrb->idx_asm;
- }
-
- return true;
-}
-
-static bool annotate_browser__callq(struct annotate_browser *browser,
- int evidx, void (*timer)(void *arg),
- void *arg, int delay_secs)
-{
- struct map_symbol *ms = browser->b.priv;
- struct symbol *sym = ms->sym;
- struct annotation *notes;
- struct symbol *target;
- char *s = strstr(browser->selection->line, "callq ");
- u64 ip;
-
- if (s == NULL)
- return false;
-
- s = strchr(s, ' ');
- if (s++ == NULL) {
- ui_helpline__puts("Invallid callq instruction.");
- return true;
- }
-
- ip = strtoull(s, NULL, 16);
- ip = ms->map->map_ip(ms->map, ip);
- target = map__find_symbol(ms->map, ip, NULL);
- if (target == NULL) {
- ui_helpline__puts("The called function was not found.");
- return true;
- }
-
- notes = symbol__annotation(target);
- pthread_mutex_lock(¬es->lock);
-
- if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
- pthread_mutex_unlock(¬es->lock);
- ui__warning("Not enough memory for annotating '%s' symbol!\n",
- target->name);
- return true;
- }
-
- pthread_mutex_unlock(¬es->lock);
- symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs);
- ui_browser__show_title(&browser->b, sym->name);
- return true;
-}
-
-static struct objdump_line *
- annotate_browser__find_offset(struct annotate_browser *browser,
- s64 offset, s64 *idx)
-{
- struct map_symbol *ms = browser->b.priv;
- struct symbol *sym = ms->sym;
- struct annotation *notes = symbol__annotation(sym);
- struct objdump_line *pos;
-
- *idx = 0;
- list_for_each_entry(pos, ¬es->src->source, node) {
- if (pos->offset == offset)
- return pos;
- if (!objdump_line__filter(&browser->b, &pos->node))
- ++*idx;
- }
-
- return NULL;
-}
-
-static bool annotate_browser__jump(struct annotate_browser *browser)
-{
- const char *jumps[] = { "je ", "jne ", "ja ", "jmpq ", "js ", "jmp ", NULL };
- struct objdump_line *line;
- s64 idx, offset;
- char *s = NULL;
- int i = 0;
-
- while (jumps[i]) {
- s = strstr(browser->selection->line, jumps[i++]);
- if (s)
- break;
- }
-
- if (s == NULL)
- return false;
-
- s = strchr(s, '+');
- if (s++ == NULL) {
- ui_helpline__puts("Invallid jump instruction.");
- return true;
- }
-
- offset = strtoll(s, NULL, 16);
- line = annotate_browser__find_offset(browser, offset, &idx);
- if (line == NULL) {
- ui_helpline__puts("Invallid jump offset");
- return true;
- }
-
- annotate_browser__set_top(browser, line, idx);
-
- return true;
-}
-
-static struct objdump_line *
- annotate_browser__find_string(struct annotate_browser *browser,
- char *s, s64 *idx)
-{
- struct map_symbol *ms = browser->b.priv;
- struct symbol *sym = ms->sym;
- struct annotation *notes = symbol__annotation(sym);
- struct objdump_line *pos = browser->selection;
-
- *idx = browser->b.index;
- list_for_each_entry_continue(pos, ¬es->src->source, node) {
- if (objdump_line__filter(&browser->b, &pos->node))
- continue;
-
- ++*idx;
-
- if (pos->line && strstr(pos->line, s) != NULL)
- return pos;
- }
-
- return NULL;
-}
-
-static bool __annotate_browser__search(struct annotate_browser *browser)
-{
- struct objdump_line *line;
- s64 idx;
-
- line = annotate_browser__find_string(browser, browser->search_bf, &idx);
- if (line == NULL) {
- ui_helpline__puts("String not found!");
- return false;
- }
-
- annotate_browser__set_top(browser, line, idx);
- browser->searching_backwards = false;
- return true;
-}
-
-static struct objdump_line *
- annotate_browser__find_string_reverse(struct annotate_browser *browser,
- char *s, s64 *idx)
-{
- struct map_symbol *ms = browser->b.priv;
- struct symbol *sym = ms->sym;
- struct annotation *notes = symbol__annotation(sym);
- struct objdump_line *pos = browser->selection;
-
- *idx = browser->b.index;
- list_for_each_entry_continue_reverse(pos, ¬es->src->source, node) {
- if (objdump_line__filter(&browser->b, &pos->node))
- continue;
-
- --*idx;
-
- if (pos->line && strstr(pos->line, s) != NULL)
- return pos;
- }
-
- return NULL;
-}
-
-static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
-{
- struct objdump_line *line;
- s64 idx;
-
- line = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
- if (line == NULL) {
- ui_helpline__puts("String not found!");
- return false;
- }
-
- annotate_browser__set_top(browser, line, idx);
- browser->searching_backwards = true;
- return true;
-}
-
-static bool annotate_browser__search_window(struct annotate_browser *browser,
- int delay_secs)
-{
- if (ui_browser__input_window("Search", "String: ", browser->search_bf,
- "ENTER: OK, ESC: Cancel",
- delay_secs * 2) != K_ENTER ||
- !*browser->search_bf)
- return false;
-
- return true;
-}
-
-static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
-{
- if (annotate_browser__search_window(browser, delay_secs))
- return __annotate_browser__search(browser);
-
- return false;
-}
-
-static bool annotate_browser__continue_search(struct annotate_browser *browser,
- int delay_secs)
-{
- if (!*browser->search_bf)
- return annotate_browser__search(browser, delay_secs);
-
- return __annotate_browser__search(browser);
-}
-
-static bool annotate_browser__search_reverse(struct annotate_browser *browser,
- int delay_secs)
-{
- if (annotate_browser__search_window(browser, delay_secs))
- return __annotate_browser__search_reverse(browser);
-
- return false;
-}
-
-static
-bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
- int delay_secs)
-{
- if (!*browser->search_bf)
- return annotate_browser__search_reverse(browser, delay_secs);
-
- return __annotate_browser__search_reverse(browser);
-}
-
-static int annotate_browser__run(struct annotate_browser *self, int evidx,
- void(*timer)(void *arg),
- void *arg, int delay_secs)
-{
- struct rb_node *nd = NULL;
- struct map_symbol *ms = self->b.priv;
- struct symbol *sym = ms->sym;
- const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
- "H: Go to hottest line, ->/ENTER: Line action, "
- "O: Toggle offset view, "
- "S: Toggle source code view";
- int key;
-
- if (ui_browser__show(&self->b, sym->name, help) < 0)
- return -1;
-
- annotate_browser__calc_percent(self, evidx);
-
- if (self->curr_hot) {
- annotate_browser__set_rb_top(self, self->curr_hot);
- self->b.navkeypressed = false;
- }
-
- nd = self->curr_hot;
-
- while (1) {
- key = ui_browser__run(&self->b, delay_secs);
-
- if (delay_secs != 0) {
- annotate_browser__calc_percent(self, evidx);
- /*
- * Current line focus got out of the list of most active
- * lines, NULL it so that if TAB|UNTAB is pressed, we
- * move to curr_hot (current hottest line).
- */
- if (nd != NULL && RB_EMPTY_NODE(nd))
- nd = NULL;
- }
-
- switch (key) {
- case K_TIMER:
- if (timer != NULL)
- timer(arg);
-
- if (delay_secs != 0)
- symbol__annotate_decay_histogram(sym, evidx);
- continue;
- case K_TAB:
- if (nd != NULL) {
- nd = rb_prev(nd);
- if (nd == NULL)
- nd = rb_last(&self->entries);
- } else
- nd = self->curr_hot;
- break;
- case K_UNTAB:
- if (nd != NULL)
- nd = rb_next(nd);
- if (nd == NULL)
- nd = rb_first(&self->entries);
- else
- nd = self->curr_hot;
- break;
- case 'H':
- case 'h':
- nd = self->curr_hot;
- break;
- case 'S':
- case 's':
- if (annotate_browser__toggle_source(self))
- ui_helpline__puts(help);
- continue;
- case 'O':
- case 'o':
- self->use_offset = !self->use_offset;
- continue;
- case '/':
- if (annotate_browser__search(self, delay_secs)) {
-show_help:
- ui_helpline__puts(help);
- }
- continue;
- case 'n':
- if (self->searching_backwards ?
- annotate_browser__continue_search_reverse(self, delay_secs) :
- annotate_browser__continue_search(self, delay_secs))
- goto show_help;
- continue;
- case '?':
- if (annotate_browser__search_reverse(self, delay_secs))
- goto show_help;
- continue;
- case K_ENTER:
- case K_RIGHT:
- if (self->selection == NULL)
- ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
- else if (self->selection->offset == -1)
- ui_helpline__puts("Actions are only available for assembly lines.");
- else if (!(annotate_browser__jump(self) ||
- annotate_browser__callq(self, evidx, timer, arg, delay_secs)))
- ui_helpline__puts("Actions are only available for the 'callq' and jump instructions.");
- continue;
- case K_LEFT:
- case K_ESC:
- case 'q':
- case CTRL('c'):
- goto out;
- default:
- continue;
- }
-
- if (nd != NULL)
- annotate_browser__set_rb_top(self, nd);
- }
-out:
- ui_browser__hide(&self->b);
- return key;
-}
-
-int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
- void(*timer)(void *arg), void *arg, int delay_secs)
-{
- return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
- timer, arg, delay_secs);
-}
-
-int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
- void(*timer)(void *arg), void *arg,
- int delay_secs)
-{
- struct objdump_line *pos, *n;
- struct annotation *notes;
- struct map_symbol ms = {
- .map = map,
- .sym = sym,
- };
- struct annotate_browser browser = {
- .b = {
- .refresh = ui_browser__list_head_refresh,
- .seek = ui_browser__list_head_seek,
- .write = annotate_browser__write,
- .filter = objdump_line__filter,
- .priv = &ms,
- .use_navkeypressed = true,
- },
- };
- int ret;
-
- if (sym == NULL)
- return -1;
-
- if (map->dso->annotate_warned)
- return -1;
-
- if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) {
- ui__error("%s", ui_helpline__last_msg);
- return -1;
- }
-
- ui_helpline__push("Press <- or ESC to exit");
-
- notes = symbol__annotation(sym);
- browser.start = map__rip_2objdump(map, sym->start);
-
- list_for_each_entry(pos, ¬es->src->source, node) {
- struct objdump_line_rb_node *rbpos;
- 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.nr_entries++;
- if (pos->offset != -1)
- rbpos->idx_asm = browser.nr_asm_entries++;
- else
- rbpos->idx_asm = -1;
- }
-
- browser.b.nr_entries = browser.nr_entries;
- browser.b.entries = ¬es->src->source,
- browser.b.width += 18; /* Percentage */
- ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
- list_for_each_entry_safe(pos, n, ¬es->src->source, node) {
- list_del(&pos->node);
- objdump_line__free(pos);
- }
- return ret;
-}
+++ /dev/null
-#include <stdio.h>
-#include "../libslang.h"
-#include <stdlib.h>
-#include <string.h>
-#include <newt.h>
-#include <linux/rbtree.h>
-
-#include "../../evsel.h"
-#include "../../evlist.h"
-#include "../../hist.h"
-#include "../../pstack.h"
-#include "../../sort.h"
-#include "../../util.h"
-
-#include "../browser.h"
-#include "../helpline.h"
-#include "../util.h"
-#include "../ui.h"
-#include "map.h"
-
-struct hist_browser {
- struct ui_browser b;
- struct hists *hists;
- struct hist_entry *he_selection;
- struct map_symbol *selection;
- bool has_symbols;
-};
-
-static int hists__browser_title(struct hists *self, char *bf, size_t size,
- const char *ev_name);
-
-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 void map_symbol__set_folding(struct map_symbol *self, bool unfold)
-{
- self->unfolded = unfold ? self->has_children : false;
-}
-
-static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
-{
- int n = 0;
- struct rb_node *nd;
-
- for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
- struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
- struct callchain_list *chain;
- char folded_sign = ' '; /* No children */
-
- list_for_each_entry(chain, &child->val, list) {
- ++n;
- /* We need this because we may not have children */
- folded_sign = callchain_list__folded(chain);
- if (folded_sign == '+')
- break;
- }
-
- if (folded_sign == '-') /* Have children and they're unfolded */
- n += callchain_node__count_rows_rb_tree(child);
- }
-
- return n;
-}
-
-static int callchain_node__count_rows(struct callchain_node *node)
-{
- struct callchain_list *chain;
- bool unfolded = false;
- int n = 0;
-
- list_for_each_entry(chain, &node->val, list) {
- ++n;
- unfolded = chain->ms.unfolded;
- }
-
- if (unfolded)
- n += callchain_node__count_rows_rb_tree(node);
-
- return n;
-}
-
-static int callchain__count_rows(struct rb_root *chain)
-{
- struct rb_node *nd;
- int n = 0;
-
- for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
- struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
- n += callchain_node__count_rows(node);
- }
-
- return n;
-}
-
-static bool map_symbol__toggle_fold(struct map_symbol *self)
-{
- if (!self)
- return false;
-
- if (!self->has_children)
- return false;
-
- self->unfolded = !self->unfolded;
- return true;
-}
-
-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;
- bool first = true;
-
- list_for_each_entry(chain, &child->val, list) {
- if (first) {
- first = false;
- chain->ms.has_children = chain->list.next != &child->val ||
- !RB_EMPTY_ROOT(&child->rb_root);
- } else
- chain->ms.has_children = chain->list.next == &child->val &&
- !RB_EMPTY_ROOT(&child->rb_root);
- }
-
- 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_EMPTY_ROOT(&self->rb_root);
-
- 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) {
- self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain);
- callchain__init_have_children(&self->sorted_chain);
- self->init_have_children = true;
- }
-}
-
-static bool hist_browser__toggle_fold(struct hist_browser *self)
-{
- if (map_symbol__toggle_fold(self->selection)) {
- struct hist_entry *he = self->he_selection;
-
- hist_entry__init_have_children(he);
- self->hists->nr_entries -= he->nr_rows;
-
- if (he->ms.unfolded)
- he->nr_rows = callchain__count_rows(&he->sorted_chain);
- else
- he->nr_rows = 0;
- self->hists->nr_entries += he->nr_rows;
- self->b.nr_entries = self->hists->nr_entries;
-
- return true;
- }
-
- /* If it doesn't have children, no toggling performed */
- return false;
-}
-
-static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold)
-{
- int n = 0;
- struct rb_node *nd;
-
- for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
- struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
- struct callchain_list *chain;
- bool has_children = false;
-
- list_for_each_entry(chain, &child->val, list) {
- ++n;
- map_symbol__set_folding(&chain->ms, unfold);
- has_children = chain->ms.has_children;
- }
-
- if (has_children)
- n += callchain_node__set_folding_rb_tree(child, unfold);
- }
-
- return n;
-}
-
-static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
-{
- struct callchain_list *chain;
- bool has_children = false;
- int n = 0;
-
- list_for_each_entry(chain, &node->val, list) {
- ++n;
- map_symbol__set_folding(&chain->ms, unfold);
- has_children = chain->ms.has_children;
- }
-
- if (has_children)
- n += callchain_node__set_folding_rb_tree(node, unfold);
-
- return n;
-}
-
-static int callchain__set_folding(struct rb_root *chain, bool unfold)
-{
- struct rb_node *nd;
- int n = 0;
-
- for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
- struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
- n += callchain_node__set_folding(node, unfold);
- }
-
- return n;
-}
-
-static void hist_entry__set_folding(struct hist_entry *self, bool unfold)
-{
- hist_entry__init_have_children(self);
- map_symbol__set_folding(&self->ms, unfold);
-
- if (self->ms.has_children) {
- int n = callchain__set_folding(&self->sorted_chain, unfold);
- self->nr_rows = unfold ? n : 0;
- } else
- self->nr_rows = 0;
-}
-
-static void hists__set_folding(struct hists *self, bool unfold)
-{
- struct rb_node *nd;
-
- self->nr_entries = 0;
-
- for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
- struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
- hist_entry__set_folding(he, unfold);
- self->nr_entries += 1 + he->nr_rows;
- }
-}
-
-static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
-{
- hists__set_folding(self->hists, unfold);
- self->b.nr_entries = self->hists->nr_entries;
- /* Go to the start, we may be way after valid entries after a collapse */
- ui_browser__reset_index(&self->b);
-}
-
-static void ui_browser__warn_lost_events(struct ui_browser *browser)
-{
- ui_browser__warning(browser, 4,
- "Events are being lost, check IO/CPU overload!\n\n"
- "You may want to run 'perf' using a RT scheduler policy:\n\n"
- " perf top -r 80\n\n"
- "Or reduce the sampling frequency.");
-}
-
-static int hist_browser__run(struct hist_browser *self, const char *ev_name,
- void(*timer)(void *arg), void *arg, int delay_secs)
-{
- int key;
- char title[160];
-
- self->b.entries = &self->hists->entries;
- self->b.nr_entries = self->hists->nr_entries;
-
- hist_browser__refresh_dimensions(self);
- hists__browser_title(self->hists, title, sizeof(title), ev_name);
-
- if (ui_browser__show(&self->b, title,
- "Press '?' for help on key bindings") < 0)
- return -1;
-
- while (1) {
- key = ui_browser__run(&self->b, delay_secs);
-
- switch (key) {
- case K_TIMER:
- timer(arg);
- ui_browser__update_nr_entries(&self->b, self->hists->nr_entries);
-
- if (self->hists->stats.nr_lost_warned !=
- self->hists->stats.nr_events[PERF_RECORD_LOST]) {
- self->hists->stats.nr_lost_warned =
- self->hists->stats.nr_events[PERF_RECORD_LOST];
- ui_browser__warn_lost_events(&self->b);
- }
-
- hists__browser_title(self->hists, title, sizeof(title), ev_name);
- ui_browser__show_title(&self->b, title);
- continue;
- 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);
- }
- break;
- case 'C':
- /* Collapse the whole world. */
- hist_browser__set_folding(self, false);
- break;
- case 'E':
- /* Expand the whole world. */
- hist_browser__set_folding(self, true);
- break;
- case K_ENTER:
- if (hist_browser__toggle_fold(self))
- break;
- /* fall thru */
- default:
- goto out;
- }
- }
-out:
- ui_browser__hide(&self->b);
- return key;
-}
-
-static char *callchain_list__sym_name(struct callchain_list *self,
- char *bf, size_t bfsize)
-{
- if (self->ms.sym)
- return self->ms.sym->name;
-
- snprintf(bf, bfsize, "%#" PRIx64, self->ip);
- return bf;
-}
-
-#define LEVEL_OFFSET_STEP 3
-
-static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
- struct callchain_node *chain_node,
- u64 total, int level,
- unsigned short row,
- off_t *row_offset,
- bool *is_current_entry)
-{
- struct rb_node *node;
- int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
- u64 new_total, remaining;
-
- if (callchain_param.mode == CHAIN_GRAPH_REL)
- new_total = chain_node->children_hit;
- else
- new_total = total;
-
- remaining = new_total;
- node = rb_first(&chain_node->rb_root);
- while (node) {
- struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
- struct rb_node *next = rb_next(node);
- u64 cumul = callchain_cumul_hits(child);
- struct callchain_list *chain;
- char folded_sign = ' ';
- int first = true;
- int extra_offset = 0;
-
- remaining -= cumul;
-
- list_for_each_entry(chain, &child->val, list) {
- char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
- const char *str;
- int color;
- bool was_first = first;
-
- if (first)
- first = false;
- else
- extra_offset = LEVEL_OFFSET_STEP;
-
- folded_sign = callchain_list__folded(chain);
- if (*row_offset != 0) {
- --*row_offset;
- goto do_next;
- }
-
- alloc_str = NULL;
- str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
- if (was_first) {
- double percent = cumul * 100.0 / new_total;
-
- if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
- str = "Not enough memory!";
- else
- str = alloc_str;
- }
-
- color = HE_COLORSET_NORMAL;
- width = self->b.width - (offset + extra_offset + 2);
- if (ui_browser__is_current_entry(&self->b, row)) {
- self->selection = &chain->ms;
- color = HE_COLORSET_SELECTED;
- *is_current_entry = true;
- }
-
- ui_browser__set_color(&self->b, color);
- ui_browser__gotorc(&self->b, row, 0);
- slsmg_write_nstring(" ", offset + extra_offset);
- slsmg_printf("%c ", folded_sign);
- slsmg_write_nstring(str, width);
- free(alloc_str);
-
- if (++row == self->b.height)
- goto out;
-do_next:
- if (folded_sign == '+')
- break;
- }
-
- if (folded_sign == '-') {
- const int new_level = level + (extra_offset ? 2 : 1);
- row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
- new_level, row, row_offset,
- is_current_entry);
- }
- if (row == self->b.height)
- goto out;
- node = next;
- }
-out:
- return row - first_row;
-}
-
-static int hist_browser__show_callchain_node(struct hist_browser *self,
- struct callchain_node *node,
- int level, unsigned short row,
- off_t *row_offset,
- bool *is_current_entry)
-{
- struct callchain_list *chain;
- int first_row = row,
- offset = level * LEVEL_OFFSET_STEP,
- width = self->b.width - offset;
- char folded_sign = ' ';
-
- list_for_each_entry(chain, &node->val, list) {
- char ipstr[BITS_PER_LONG / 4 + 1], *s;
- int color;
-
- folded_sign = callchain_list__folded(chain);
-
- if (*row_offset != 0) {
- --*row_offset;
- continue;
- }
-
- color = HE_COLORSET_NORMAL;
- if (ui_browser__is_current_entry(&self->b, row)) {
- self->selection = &chain->ms;
- color = HE_COLORSET_SELECTED;
- *is_current_entry = true;
- }
-
- s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
- ui_browser__gotorc(&self->b, row, 0);
- ui_browser__set_color(&self->b, color);
- slsmg_write_nstring(" ", offset);
- slsmg_printf("%c ", folded_sign);
- slsmg_write_nstring(s, width - 2);
-
- if (++row == self->b.height)
- goto out;
- }
-
- if (folded_sign == '-')
- row += hist_browser__show_callchain_node_rb_tree(self, node,
- self->hists->stats.total_period,
- level + 1, row,
- row_offset,
- is_current_entry);
-out:
- return row - first_row;
-}
-
-static int hist_browser__show_callchain(struct hist_browser *self,
- struct rb_root *chain,
- int level, unsigned short row,
- off_t *row_offset,
- bool *is_current_entry)
-{
- struct rb_node *nd;
- int first_row = row;
-
- for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
- struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
-
- row += hist_browser__show_callchain_node(self, node, level,
- row, row_offset,
- is_current_entry);
- if (row == self->b.height)
- break;
- }
-
- return row - first_row;
-}
-
-static int hist_browser__show_entry(struct hist_browser *self,
- struct hist_entry *entry,
- unsigned short row)
-{
- char s[256];
- double percent;
- int printed = 0;
- int width = self->b.width - 6; /* The percentage */
- char folded_sign = ' ';
- bool current_entry = ui_browser__is_current_entry(&self->b, row);
- off_t row_offset = entry->row_offset;
-
- if (current_entry) {
- self->he_selection = entry;
- self->selection = &entry->ms;
- }
-
- if (symbol_conf.use_callchain) {
- hist_entry__init_have_children(entry);
- folded_sign = hist_entry__folded(entry);
- }
-
- if (row_offset == 0) {
- hist_entry__snprintf(entry, s, sizeof(s), self->hists);
- percent = (entry->period * 100.0) / self->hists->stats.total_period;
-
- ui_browser__set_percent_color(&self->b, percent, current_entry);
- ui_browser__gotorc(&self->b, row, 0);
- if (symbol_conf.use_callchain) {
- slsmg_printf("%c ", folded_sign);
- width -= 2;
- }
-
- slsmg_printf(" %5.2f%%", percent);
-
- /* The scroll bar isn't being used */
- if (!self->b.navkeypressed)
- width += 1;
-
- if (!current_entry || !self->b.navkeypressed)
- ui_browser__set_color(&self->b, HE_COLORSET_NORMAL);
-
- if (symbol_conf.show_nr_samples) {
- slsmg_printf(" %11u", entry->nr_events);
- width -= 12;
- }
-
- if (symbol_conf.show_total_period) {
- slsmg_printf(" %12" PRIu64, entry->period);
- width -= 13;
- }
-
- slsmg_write_nstring(s, width);
- ++row;
- ++printed;
- } else
- --row_offset;
-
- if (folded_sign == '-' && row != self->b.height) {
- printed += hist_browser__show_callchain(self, &entry->sorted_chain,
- 1, row, &row_offset,
- ¤t_entry);
- if (current_entry)
- self->he_selection = entry;
- }
-
- return printed;
-}
-
-static void ui_browser__hists_init_top(struct ui_browser *browser)
-{
- if (browser->top == NULL) {
- struct hist_browser *hb;
-
- hb = container_of(browser, struct hist_browser, b);
- browser->top = rb_first(&hb->hists->entries);
- }
-}
-
-static unsigned int hist_browser__refresh(struct ui_browser *self)
-{
- unsigned row = 0;
- struct rb_node *nd;
- struct hist_browser *hb = container_of(self, struct hist_browser, b);
-
- ui_browser__hists_init_top(self);
-
- for (nd = self->top; nd; nd = rb_next(nd)) {
- struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-
- if (h->filtered)
- continue;
-
- row += hist_browser__show_entry(hb, h, row);
- if (row == self->height)
- break;
- }
-
- return row;
-}
-
-static struct rb_node *hists__filter_entries(struct rb_node *nd)
-{
- while (nd != NULL) {
- struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
- if (!h->filtered)
- return nd;
-
- nd = rb_next(nd);
- }
-
- return NULL;
-}
-
-static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
-{
- while (nd != NULL) {
- struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
- if (!h->filtered)
- return nd;
-
- nd = rb_prev(nd);
- }
-
- return NULL;
-}
-
-static void ui_browser__hists_seek(struct ui_browser *self,
- off_t offset, int whence)
-{
- struct hist_entry *h;
- struct rb_node *nd;
- bool first = true;
-
- if (self->nr_entries == 0)
- return;
-
- ui_browser__hists_init_top(self);
-
- switch (whence) {
- case SEEK_SET:
- nd = hists__filter_entries(rb_first(self->entries));
- break;
- case SEEK_CUR:
- nd = self->top;
- goto do_offset;
- case SEEK_END:
- nd = hists__filter_prev_entries(rb_last(self->entries));
- first = false;
- break;
- default:
- return;
- }
-
- /*
- * Moves not relative to the first visible entry invalidates its
- * row_offset:
- */
- h = rb_entry(self->top, struct hist_entry, rb_node);
- h->row_offset = 0;
-
- /*
- * Here we have to check if nd is expanded (+), if it is we can't go
- * the next top level hist_entry, instead we must compute an offset of
- * what _not_ to show and not change the first visible entry.
- *
- * This offset increments when we are going from top to bottom and
- * decreases when we're going from bottom to top.
- *
- * As we don't have backpointers to the top level in the callchains
- * structure, we need to always print the whole hist_entry callchain,
- * skipping the first ones that are before the first visible entry
- * and stop when we printed enough lines to fill the screen.
- */
-do_offset:
- if (offset > 0) {
- do {
- h = rb_entry(nd, struct hist_entry, rb_node);
- if (h->ms.unfolded) {
- u16 remaining = h->nr_rows - h->row_offset;
- if (offset > remaining) {
- offset -= remaining;
- h->row_offset = 0;
- } else {
- h->row_offset += offset;
- offset = 0;
- self->top = nd;
- break;
- }
- }
- nd = hists__filter_entries(rb_next(nd));
- if (nd == NULL)
- break;
- --offset;
- self->top = nd;
- } while (offset != 0);
- } else if (offset < 0) {
- while (1) {
- h = rb_entry(nd, struct hist_entry, rb_node);
- if (h->ms.unfolded) {
- if (first) {
- if (-offset > h->row_offset) {
- offset += h->row_offset;
- h->row_offset = 0;
- } else {
- h->row_offset += offset;
- offset = 0;
- self->top = nd;
- break;
- }
- } else {
- if (-offset > h->nr_rows) {
- offset += h->nr_rows;
- h->row_offset = 0;
- } else {
- h->row_offset = h->nr_rows + offset;
- offset = 0;
- self->top = nd;
- break;
- }
- }
- }
-
- nd = hists__filter_prev_entries(rb_prev(nd));
- if (nd == NULL)
- break;
- ++offset;
- 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;
- }
- 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;
- self->b.use_navkeypressed = true;
- if (sort__branch_mode == 1)
- self->has_symbols = sort_sym_from.list.next != NULL;
- else
- self->has_symbols = sort_sym.list.next != NULL;
- }
-
- return self;
-}
-
-static void hist_browser__delete(struct hist_browser *self)
-{
- 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 hists__browser_title(struct hists *self, char *bf, size_t size,
- const char *ev_name)
-{
- char unit;
- int printed;
- const struct dso *dso = self->dso_filter;
- const struct thread *thread = self->thread_filter;
- unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE];
- u64 nr_events = self->stats.total_period;
-
- nr_samples = convert_unit(nr_samples, &unit);
- printed = scnprintf(bf, size,
- "Samples: %lu%c of event '%s', Event count (approx.): %lu",
- nr_samples, unit, ev_name, nr_events);
-
-
- if (self->uid_filter_str)
- printed += snprintf(bf + printed, size - printed,
- ", UID: %s", self->uid_filter_str);
- if (thread)
- printed += scnprintf(bf + printed, size - printed,
- ", Thread: %s(%d)",
- (thread->comm_set ? thread->comm : ""),
- thread->pid);
- if (dso)
- printed += scnprintf(bf + printed, size - printed,
- ", DSO: %s", dso->short_name);
- return printed;
-}
-
-static inline void free_popup_options(char **options, int n)
-{
- int i;
-
- for (i = 0; i < n; ++i) {
- free(options[i]);
- options[i] = NULL;
- }
-}
-
-static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
- const char *helpline, const char *ev_name,
- bool left_exits,
- void(*timer)(void *arg), void *arg,
- int delay_secs)
-{
- struct hists *self = &evsel->hists;
- struct hist_browser *browser = hist_browser__new(self);
- struct branch_info *bi;
- struct pstack *fstack;
- char *options[16];
- int nr_options = 0;
- int key = -1;
- char buf[64];
-
- if (browser == NULL)
- return -1;
-
- fstack = pstack__new(2);
- if (fstack == NULL)
- goto out;
-
- ui_helpline__push(helpline);
-
- memset(options, 0, sizeof(options));
-
- while (1) {
- const struct thread *thread = NULL;
- const struct dso *dso = NULL;
- int choice = 0,
- annotate = -2, zoom_dso = -2, zoom_thread = -2,
- annotate_f = -2, annotate_t = -2, browse_map = -2;
-
- nr_options = 0;
-
- key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
-
- if (browser->he_selection != NULL) {
- thread = hist_browser__selected_thread(browser);
- dso = browser->selection->map ? browser->selection->map->dso : NULL;
- }
- switch (key) {
- case K_TAB:
- case K_UNTAB:
- if (nr_events == 1)
- continue;
- /*
- * Exit the browser, let hists__browser_tree
- * go to the next or previous
- */
- goto out_free_stack;
- case 'a':
- if (!browser->has_symbols) {
- ui_browser__warning(&browser->b, delay_secs * 2,
- "Annotation is only available for symbolic views, "
- "include \"sym*\" in --sort to use it.");
- continue;
- }
-
- if (browser->selection == NULL ||
- browser->selection->sym == NULL ||
- browser->selection->map->dso->annotate_warned)
- continue;
- goto do_annotate;
- case 'd':
- goto zoom_dso;
- case 't':
- goto zoom_thread;
- case 's':
- if (ui_browser__input_window("Symbol to show",
- "Please enter the name of symbol you want to see",
- buf, "ENTER: OK, ESC: Cancel",
- delay_secs * 2) == K_ENTER) {
- self->symbol_filter_str = *buf ? buf : NULL;
- hists__filter_by_symbol(self);
- hist_browser__reset(browser);
- }
- continue;
- case K_F1:
- case 'h':
- case '?':
- ui_browser__help_window(&browser->b,
- "h/?/F1 Show this window\n"
- "UP/DOWN/PGUP\n"
- "PGDN/SPACE Navigate\n"
- "q/ESC/CTRL+C Exit browser\n\n"
- "For multiple event sessions:\n\n"
- "TAB/UNTAB Switch events\n\n"
- "For symbolic views (--sort has sym):\n\n"
- "-> Zoom into DSO/Threads & Annotate current symbol\n"
- "<- Zoom out\n"
- "a Annotate current symbol\n"
- "C Collapse all callchains\n"
- "E Expand all callchains\n"
- "d Zoom into current DSO\n"
- "t Zoom into current Thread\n"
- "s Filter symbol by name");
- continue;
- case K_ENTER:
- case K_RIGHT:
- /* menu */
- break;
- case K_LEFT: {
- const void *top;
-
- if (pstack__empty(fstack)) {
- /*
- * Go back to the perf_evsel_menu__run or other user
- */
- if (left_exits)
- goto out_free_stack;
- continue;
- }
- top = pstack__pop(fstack);
- if (top == &browser->hists->dso_filter)
- goto zoom_out_dso;
- if (top == &browser->hists->thread_filter)
- goto zoom_out_thread;
- continue;
- }
- case K_ESC:
- if (!left_exits &&
- !ui_browser__dialog_yesno(&browser->b,
- "Do you really want to exit?"))
- continue;
- /* Fall thru */
- case 'q':
- case CTRL('c'):
- goto out_free_stack;
- default:
- continue;
- }
-
- if (!browser->has_symbols)
- goto add_exit_option;
-
- if (sort__branch_mode == 1) {
- bi = browser->he_selection->branch_info;
- if (browser->selection != NULL &&
- bi &&
- bi->from.sym != NULL &&
- !bi->from.map->dso->annotate_warned &&
- asprintf(&options[nr_options], "Annotate %s",
- bi->from.sym->name) > 0)
- annotate_f = nr_options++;
-
- if (browser->selection != NULL &&
- bi &&
- bi->to.sym != NULL &&
- !bi->to.map->dso->annotate_warned &&
- (bi->to.sym != bi->from.sym ||
- bi->to.map->dso != bi->from.map->dso) &&
- asprintf(&options[nr_options], "Annotate %s",
- bi->to.sym->name) > 0)
- annotate_t = nr_options++;
- } else {
-
- if (browser->selection != NULL &&
- 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",
- (browser->hists->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",
- (browser->hists->dso_filter ? "out of" : "into"),
- (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
- zoom_dso = nr_options++;
-
- if (browser->selection != NULL &&
- browser->selection->map != NULL &&
- asprintf(&options[nr_options], "Browse map details") > 0)
- browse_map = nr_options++;
-add_exit_option:
- options[nr_options++] = (char *)"Exit";
-retry_popup_menu:
- choice = ui__popup_menu(nr_options, options);
-
- if (choice == nr_options - 1)
- break;
-
- if (choice == -1) {
- free_popup_options(options, nr_options - 1);
- continue;
- }
-
- if (choice == annotate || choice == annotate_t || choice == annotate_f) {
- struct hist_entry *he;
- int err;
-do_annotate:
- he = hist_browser__selected_entry(browser);
- if (he == NULL)
- continue;
-
- /*
- * we stash the branch_info symbol + map into the
- * the ms so we don't have to rewrite all the annotation
- * code to use branch_info.
- * in branch mode, the ms struct is not used
- */
- if (choice == annotate_f) {
- he->ms.sym = he->branch_info->from.sym;
- he->ms.map = he->branch_info->from.map;
- } else if (choice == annotate_t) {
- he->ms.sym = he->branch_info->to.sym;
- he->ms.map = he->branch_info->to.map;
- }
-
- /*
- * Don't let this be freed, say, by hists__decay_entry.
- */
- he->used = true;
- err = hist_entry__tui_annotate(he, evsel->idx,
- timer, arg, delay_secs);
- he->used = false;
- /*
- * offer option to annotate the other branch source or target
- * (if they exists) when returning from annotate
- */
- if ((err == 'q' || err == CTRL('c'))
- && annotate_t != -2 && annotate_f != -2)
- goto retry_popup_menu;
-
- ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
- if (err)
- ui_browser__handle_resize(&browser->b);
-
- } else if (choice == browse_map)
- map__browse(browser->selection->map);
- else if (choice == zoom_dso) {
-zoom_dso:
- if (browser->hists->dso_filter) {
- pstack__remove(fstack, &browser->hists->dso_filter);
-zoom_out_dso:
- ui_helpline__pop();
- browser->hists->dso_filter = NULL;
- sort_dso.elide = false;
- } 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);
- browser->hists->dso_filter = dso;
- sort_dso.elide = true;
- pstack__push(fstack, &browser->hists->dso_filter);
- }
- hists__filter_by_dso(self);
- hist_browser__reset(browser);
- } else if (choice == zoom_thread) {
-zoom_thread:
- if (browser->hists->thread_filter) {
- pstack__remove(fstack, &browser->hists->thread_filter);
-zoom_out_thread:
- ui_helpline__pop();
- browser->hists->thread_filter = NULL;
- sort_thread.elide = false;
- } else {
- ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
- thread->comm_set ? thread->comm : "",
- thread->pid);
- browser->hists->thread_filter = thread;
- sort_thread.elide = true;
- pstack__push(fstack, &browser->hists->thread_filter);
- }
- hists__filter_by_thread(self);
- hist_browser__reset(browser);
- }
- }
-out_free_stack:
- pstack__delete(fstack);
-out:
- hist_browser__delete(browser);
- free_popup_options(options, nr_options - 1);
- return key;
-}
-
-struct perf_evsel_menu {
- struct ui_browser b;
- struct perf_evsel *selection;
- bool lost_events, lost_events_warned;
-};
-
-static void perf_evsel_menu__write(struct ui_browser *browser,
- void *entry, int row)
-{
- struct perf_evsel_menu *menu = container_of(browser,
- struct perf_evsel_menu, b);
- struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
- bool current_entry = ui_browser__is_current_entry(browser, row);
- unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
- const char *ev_name = event_name(evsel);
- char bf[256], unit;
- const char *warn = " ";
- size_t printed;
-
- ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
- HE_COLORSET_NORMAL);
-
- nr_events = convert_unit(nr_events, &unit);
- printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
- unit, unit == ' ' ? "" : " ", ev_name);
- slsmg_printf("%s", bf);
-
- nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
- if (nr_events != 0) {
- menu->lost_events = true;
- if (!current_entry)
- ui_browser__set_color(browser, HE_COLORSET_TOP);
- nr_events = convert_unit(nr_events, &unit);
- printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
- nr_events, unit, unit == ' ' ? "" : " ");
- warn = bf;
- }
-
- slsmg_write_nstring(warn, browser->width - printed);
-
- if (current_entry)
- menu->selection = evsel;
-}
-
-static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
- int nr_events, const char *help,
- void(*timer)(void *arg), void *arg, int delay_secs)
-{
- struct perf_evlist *evlist = menu->b.priv;
- struct perf_evsel *pos;
- const char *ev_name, *title = "Available samples";
- int key;
-
- if (ui_browser__show(&menu->b, title,
- "ESC: exit, ENTER|->: Browse histograms") < 0)
- return -1;
-
- while (1) {
- key = ui_browser__run(&menu->b, delay_secs);
-
- switch (key) {
- case K_TIMER:
- timer(arg);
-
- if (!menu->lost_events_warned && menu->lost_events) {
- ui_browser__warn_lost_events(&menu->b);
- menu->lost_events_warned = true;
- }
- continue;
- case K_RIGHT:
- case K_ENTER:
- if (!menu->selection)
- continue;
- pos = menu->selection;
-browse_hists:
- perf_evlist__set_selected(evlist, pos);
- /*
- * Give the calling tool a chance to populate the non
- * default evsel resorted hists tree.
- */
- if (timer)
- timer(arg);
- ev_name = event_name(pos);
- key = perf_evsel__hists_browse(pos, nr_events, help,
- ev_name, true, timer,
- arg, delay_secs);
- ui_browser__show_title(&menu->b, title);
- switch (key) {
- case K_TAB:
- if (pos->node.next == &evlist->entries)
- pos = list_entry(evlist->entries.next, struct perf_evsel, node);
- else
- pos = list_entry(pos->node.next, struct perf_evsel, node);
- goto browse_hists;
- case K_UNTAB:
- if (pos->node.prev == &evlist->entries)
- pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
- else
- pos = list_entry(pos->node.prev, struct perf_evsel, node);
- goto browse_hists;
- case K_ESC:
- if (!ui_browser__dialog_yesno(&menu->b,
- "Do you really want to exit?"))
- continue;
- /* Fall thru */
- case 'q':
- case CTRL('c'):
- goto out;
- default:
- continue;
- }
- case K_LEFT:
- continue;
- case K_ESC:
- if (!ui_browser__dialog_yesno(&menu->b,
- "Do you really want to exit?"))
- continue;
- /* Fall thru */
- case 'q':
- case CTRL('c'):
- goto out;
- default:
- continue;
- }
- }
-
-out:
- ui_browser__hide(&menu->b);
- return key;
-}
-
-static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
- const char *help,
- void(*timer)(void *arg), void *arg,
- int delay_secs)
-{
- struct perf_evsel *pos;
- struct perf_evsel_menu menu = {
- .b = {
- .entries = &evlist->entries,
- .refresh = ui_browser__list_head_refresh,
- .seek = ui_browser__list_head_seek,
- .write = perf_evsel_menu__write,
- .nr_entries = evlist->nr_entries,
- .priv = evlist,
- },
- };
-
- ui_helpline__push("Press ESC to exit");
-
- list_for_each_entry(pos, &evlist->entries, node) {
- const char *ev_name = event_name(pos);
- size_t line_len = strlen(ev_name) + 7;
-
- if (menu.b.width < line_len)
- menu.b.width = line_len;
- /*
- * Cache the evsel name, tracepoints have a _high_ cost per
- * event_name() call.
- */
- if (pos->name == NULL)
- pos->name = strdup(ev_name);
- }
-
- return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
- arg, delay_secs);
-}
-
-int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
- void(*timer)(void *arg), void *arg,
- int delay_secs)
-{
-
- if (evlist->nr_entries == 1) {
- struct perf_evsel *first = list_entry(evlist->entries.next,
- struct perf_evsel, node);
- const char *ev_name = event_name(first);
- return perf_evsel__hists_browse(first, evlist->nr_entries, help,
- ev_name, false, timer, arg,
- delay_secs);
- }
-
- return __perf_evlist__tui_browse_hists(evlist, help,
- timer, arg, delay_secs);
-}
+++ /dev/null
-#include "../libslang.h"
-#include <elf.h>
-#include <newt.h>
-#include <inttypes.h>
-#include <sys/ttydefaults.h>
-#include <string.h>
-#include <linux/bitops.h>
-#include "../../util.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 err;
-}
-
-struct map_browser {
- struct ui_browser b;
- struct map *map;
- 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 width;
-
- ui_browser__set_percent_color(self, 0, current_entry);
- slsmg_printf("%*" PRIx64 " %*" PRIx64 " %c ",
- mb->addrlen, sym->start, mb->addrlen, sym->end,
- sym->binding == STB_GLOBAL ? 'g' :
- sym->binding == STB_LOCAL ? 'l' : 'w');
- width = self->width - ((mb->addrlen * 2) + 4);
- if (width > 0)
- slsmg_write_nstring(sym->name, width);
-}
-
-/* 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)
-{
- int key;
-
- 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;
-
- while (1) {
- key = ui_browser__run(&self->b, 0);
-
- if (verbose && key == '/')
- map_browser__search(self);
- else
- break;
- }
-
- ui_browser__hide(&self->b);
- return key;
-}
-
-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 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 (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), "%" PRIx64, maxaddr);
- return map_browser__run(&mb);
-}
+++ /dev/null
-#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_ */
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "../debug.h"
-#include "helpline.h"
-#include "ui.h"
-#include "libslang.h"
-
-void ui_helpline__pop(void)
-{
-}
-
-char ui_helpline__current[512];
-
-void ui_helpline__push(const char *msg)
-{
- const size_t sz = sizeof(ui_helpline__current);
-
- SLsmg_gotorc(SLtt_Screen_Rows - 1, 0);
- SLsmg_set_color(0);
- SLsmg_write_nstring((char *)msg, SLtt_Screen_Cols);
- SLsmg_refresh();
- strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0';
-}
-
-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;
-
- pthread_mutex_lock(&ui__lock);
- ret = vscnprintf(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);
- SLsmg_refresh();
- backlog = 0;
- }
- pthread_mutex_unlock(&ui__lock);
-
- return ret;
-}
+++ /dev/null
-#ifndef _PERF_UI_HELPLINE_H_
-#define _PERF_UI_HELPLINE_H_ 1
-
-#include <stdio.h>
-#include <stdarg.h>
-
-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);
-
-extern char ui_helpline__current[];
-
-#endif /* _PERF_UI_HELPLINE_H_ */
+++ /dev/null
-#ifndef _PERF_KEYSYMS_H_
-#define _PERF_KEYSYMS_H_ 1
-
-#include "libslang.h"
-
-#define K_DOWN SL_KEY_DOWN
-#define K_END SL_KEY_END
-#define K_ENTER '\r'
-#define K_ESC 033
-#define K_F1 SL_KEY_F(1)
-#define K_HOME SL_KEY_HOME
-#define K_LEFT SL_KEY_LEFT
-#define K_PGDN SL_KEY_NPAGE
-#define K_PGUP SL_KEY_PPAGE
-#define K_RIGHT SL_KEY_RIGHT
-#define K_TAB '\t'
-#define K_UNTAB SL_KEY_UNTAB
-#define K_UP SL_KEY_UP
-#define K_BKSPC 0x7f
-#define K_DEL SL_KEY_DELETE
-
-/* Not really keys */
-#define K_TIMER -1
-#define K_ERROR -2
-#define K_RESIZE -3
-
-#endif /* _PERF_KEYSYMS_H_ */
+++ /dev/null
-#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
-
-#define SL_KEY_UNTAB 0x1000
-
-#endif /* _PERF_UI_SLANG_H_ */
+++ /dev/null
-#include "../cache.h"
-#include "progress.h"
-#include "libslang.h"
-#include "ui.h"
-#include "browser.h"
-
-void ui_progress__update(u64 curr, u64 total, const char *title)
-{
- int bar, y;
- /*
- * 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;
-
- if (total == 0)
- return;
-
- ui__refresh_dimensions(true);
- pthread_mutex_lock(&ui__lock);
- y = SLtt_Screen_Rows / 2 - 2;
- SLsmg_set_color(0);
- SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols);
- SLsmg_gotorc(y++, 1);
- SLsmg_write_string((char *)title);
- SLsmg_set_color(HE_COLORSET_SELECTED);
- bar = ((SLtt_Screen_Cols - 2) * curr) / total;
- SLsmg_fill_region(y, 1, 1, bar, ' ');
- SLsmg_refresh();
- pthread_mutex_unlock(&ui__lock);
-}
+++ /dev/null
-#ifndef _PERF_UI_PROGRESS_H_
-#define _PERF_UI_PROGRESS_H_ 1
-
-#include <../types.h>
-
-void ui_progress__update(u64 curr, u64 total, const char *title);
-
-#endif
+++ /dev/null
-#include <newt.h>
-#include <signal.h>
-#include <stdbool.h>
-
-#include "../cache.h"
-#include "../debug.h"
-#include "browser.h"
-#include "helpline.h"
-#include "ui.h"
-#include "util.h"
-#include "libslang.h"
-#include "keysyms.h"
-
-pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
-
-static volatile int ui__need_resize;
-
-void ui__refresh_dimensions(bool force)
-{
- if (force || ui__need_resize) {
- ui__need_resize = 0;
- pthread_mutex_lock(&ui__lock);
- SLtt_get_screen_size();
- SLsmg_reinit_smg();
- pthread_mutex_unlock(&ui__lock);
- }
-}
-
-static void ui__sigwinch(int sig __used)
-{
- ui__need_resize = 1;
-}
-
-static void ui__setup_sigwinch(void)
-{
- static bool done;
-
- if (done)
- return;
-
- done = true;
- pthread__unblock_sigwinch();
- signal(SIGWINCH, ui__sigwinch);
-}
-
-int ui__getch(int delay_secs)
-{
- struct timeval timeout, *ptimeout = delay_secs ? &timeout : NULL;
- fd_set read_set;
- int err, key;
-
- ui__setup_sigwinch();
-
- FD_ZERO(&read_set);
- FD_SET(0, &read_set);
-
- if (delay_secs) {
- timeout.tv_sec = delay_secs;
- timeout.tv_usec = 0;
- }
-
- err = select(1, &read_set, NULL, NULL, ptimeout);
-
- if (err == 0)
- return K_TIMER;
-
- if (err == -1) {
- if (errno == EINTR)
- return K_RESIZE;
- return K_ERROR;
- }
-
- key = SLang_getkey();
- if (key != K_ESC)
- return key;
-
- FD_ZERO(&read_set);
- FD_SET(0, &read_set);
- timeout.tv_sec = 0;
- timeout.tv_usec = 20;
- err = select(1, &read_set, NULL, NULL, &timeout);
- if (err == 0)
- return K_ESC;
-
- SLang_ungetkey(key);
- return SLkp_getkey();
-}
-
-static void newt_suspend(void *d __used)
-{
- newtSuspend();
- raise(SIGTSTP);
- newtResume();
-}
-
-static int ui__init(void)
-{
- int err = SLkp_init();
-
- if (err < 0)
- goto out;
-
- SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB);
-out:
- return err;
-}
-
-static void ui__exit(void)
-{
- SLtt_set_cursor_visibility(1);
- SLsmg_refresh();
- SLsmg_reset_smg();
- SLang_reset_tty();
-}
-
-static void ui__signal(int sig)
-{
- ui__exit();
- psignal(sig, "perf");
- exit(0);
-}
-
-void setup_browser(bool fallback_to_pager)
-{
- if (!isatty(1) || !use_browser || dump_trace) {
- use_browser = 0;
- if (fallback_to_pager)
- setup_pager();
- return;
- }
-
- use_browser = 1;
- newtInit();
- ui__init();
- newtSetSuspendCallback(newt_suspend, NULL);
- ui_helpline__init();
- ui_browser__init();
-
- signal(SIGSEGV, ui__signal);
- signal(SIGFPE, ui__signal);
- signal(SIGINT, ui__signal);
- signal(SIGQUIT, ui__signal);
- signal(SIGTERM, ui__signal);
-}
-
-void exit_browser(bool wait_for_ok)
-{
- if (use_browser > 0) {
- if (wait_for_ok)
- ui__question_window("Fatal Error",
- ui_helpline__last_msg,
- "Press any key...", 0);
- ui__exit();
- }
-}
+++ /dev/null
-#ifndef _PERF_UI_H_
-#define _PERF_UI_H_ 1
-
-#include <pthread.h>
-#include <stdbool.h>
-
-extern pthread_mutex_t ui__lock;
-
-void ui__refresh_dimensions(bool force);
-
-#endif /* _PERF_UI_H_ */
+++ /dev/null
-#include "../util.h"
-#include <signal.h>
-#include <stdbool.h>
-#include <string.h>
-#include <sys/ttydefaults.h>
-
-#include "../cache.h"
-#include "../debug.h"
-#include "browser.h"
-#include "keysyms.h"
-#include "helpline.h"
-#include "ui.h"
-#include "util.h"
-#include "libslang.h"
-
-static void ui_browser__argv_write(struct ui_browser *browser,
- void *entry, int row)
-{
- char **arg = entry;
- bool current_entry = ui_browser__is_current_entry(browser, row);
-
- ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
- HE_COLORSET_NORMAL);
- slsmg_write_nstring(*arg, browser->width);
-}
-
-static int popup_menu__run(struct ui_browser *menu)
-{
- int key;
-
- if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0)
- return -1;
-
- while (1) {
- key = ui_browser__run(menu, 0);
-
- switch (key) {
- case K_RIGHT:
- case K_ENTER:
- key = menu->index;
- break;
- case K_LEFT:
- case K_ESC:
- case 'q':
- case CTRL('c'):
- key = -1;
- break;
- default:
- continue;
- }
-
- break;
- }
-
- ui_browser__hide(menu);
- return key;
-}
-
-int ui__popup_menu(int argc, char * const argv[])
-{
- struct ui_browser menu = {
- .entries = (void *)argv,
- .refresh = ui_browser__argv_refresh,
- .seek = ui_browser__argv_seek,
- .write = ui_browser__argv_write,
- .nr_entries = argc,
- };
-
- return popup_menu__run(&menu);
-}
-
-int ui_browser__input_window(const char *title, const char *text, char *input,
- const char *exit_msg, int delay_secs)
-{
- int x, y, len, key;
- int max_len = 60, nr_lines = 0;
- static char buf[50];
- const char *t;
-
- t = text;
- while (1) {
- const char *sep = strchr(t, '\n');
-
- 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;
- }
-
- max_len += 2;
- nr_lines += 8;
- y = SLtt_Screen_Rows / 2 - nr_lines / 2;
- x = SLtt_Screen_Cols / 2 - max_len / 2;
-
- SLsmg_set_color(0);
- SLsmg_draw_box(y, x++, nr_lines, max_len);
- if (title) {
- SLsmg_gotorc(y, x + 1);
- SLsmg_write_string((char *)title);
- }
- SLsmg_gotorc(++y, x);
- nr_lines -= 7;
- max_len -= 2;
- SLsmg_write_wrapped_string((unsigned char *)text, y, x,
- nr_lines, max_len, 1);
- y += nr_lines;
- len = 5;
- while (len--) {
- SLsmg_gotorc(y + len - 1, x);
- SLsmg_write_nstring((char *)" ", max_len);
- }
- SLsmg_draw_box(y++, x + 1, 3, max_len - 2);
-
- SLsmg_gotorc(y + 3, x);
- SLsmg_write_nstring((char *)exit_msg, max_len);
- SLsmg_refresh();
-
- x += 2;
- len = 0;
- key = ui__getch(delay_secs);
- while (key != K_TIMER && key != K_ENTER && key != K_ESC) {
- if (key == K_BKSPC) {
- if (len == 0)
- goto next_key;
- SLsmg_gotorc(y, x + --len);
- SLsmg_write_char(' ');
- } else {
- buf[len] = key;
- SLsmg_gotorc(y, x + len++);
- SLsmg_write_char(key);
- }
- SLsmg_refresh();
-
- /* XXX more graceful overflow handling needed */
- if (len == sizeof(buf) - 1) {
- ui_helpline__push("maximum size of symbol name reached!");
- key = K_ENTER;
- break;
- }
-next_key:
- key = ui__getch(delay_secs);
- }
-
- buf[len] = '\0';
- strncpy(input, buf, len+1);
- return key;
-}
-
-int ui__question_window(const char *title, const char *text,
- const char *exit_msg, int delay_secs)
-{
- int x, y;
- int max_len = 0, nr_lines = 0;
- const char *t;
-
- 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;
- }
-
- max_len += 2;
- nr_lines += 4;
- y = SLtt_Screen_Rows / 2 - nr_lines / 2,
- x = SLtt_Screen_Cols / 2 - max_len / 2;
-
- SLsmg_set_color(0);
- SLsmg_draw_box(y, x++, nr_lines, max_len);
- if (title) {
- SLsmg_gotorc(y, x + 1);
- SLsmg_write_string((char *)title);
- }
- SLsmg_gotorc(++y, x);
- nr_lines -= 2;
- max_len -= 2;
- SLsmg_write_wrapped_string((unsigned char *)text, y, x,
- nr_lines, max_len, 1);
- SLsmg_gotorc(y + nr_lines - 2, x);
- SLsmg_write_nstring((char *)" ", max_len);
- SLsmg_gotorc(y + nr_lines - 1, x);
- SLsmg_write_nstring((char *)exit_msg, max_len);
- SLsmg_refresh();
- return ui__getch(delay_secs);
-}
-
-int ui__help_window(const char *text)
-{
- return ui__question_window("Help", text, "Press any key...", 0);
-}
-
-int ui__dialog_yesno(const char *msg)
-{
- return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0);
-}
-
-int __ui__warning(const char *title, const char *format, va_list args)
-{
- char *s;
-
- if (use_browser > 0 && vasprintf(&s, format, args) > 0) {
- int key;
-
- pthread_mutex_lock(&ui__lock);
- key = ui__question_window(title, s, "Press any key...", 0);
- pthread_mutex_unlock(&ui__lock);
- free(s);
- return key;
- }
-
- fprintf(stderr, "%s:\n", title);
- vfprintf(stderr, format, args);
- return K_ESC;
-}
-
-int ui__warning(const char *format, ...)
-{
- int key;
- va_list args;
-
- va_start(args, format);
- key = __ui__warning("Warning", format, args);
- va_end(args);
- return key;
-}
-
-int ui__error(const char *format, ...)
-{
- int key;
- va_list args;
-
- va_start(args, format);
- key = __ui__warning("Error", format, args);
- va_end(args);
- return key;
-}
+++ /dev/null
-#ifndef _PERF_UI_UTIL_H_
-#define _PERF_UI_UTIL_H_ 1
-
-#include <stdarg.h>
-
-int ui__getch(int delay_secs);
-int ui__popup_menu(int argc, char * const argv[]);
-int ui__help_window(const char *text);
-int ui__dialog_yesno(const char *msg);
-int ui__question_window(const char *title, const char *text,
- const char *exit_msg, int delay_secs);
-int __ui__warning(const char *title, const char *format, va_list args);
-
-#endif /* _PERF_UI_UTIL_H_ */