summaryrefslogtreecommitdiffstats
path: root/tools/perf/util/newt.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/newt.c')
-rw-r--r--tools/perf/util/newt.c352
1 files changed, 306 insertions, 46 deletions
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c
index daa86efffce6..ba6acd04c082 100644
--- a/tools/perf/util/newt.c
+++ b/tools/perf/util/newt.c
@@ -2,6 +2,7 @@
#include <stdio.h>
#undef _GNU_SOURCE
+#include <slang.h>
#include <stdlib.h>
#include <newt.h>
#include <sys/ttydefaults.h>
@@ -171,6 +172,254 @@ static bool dialog_yesno(const char *msg)
return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
}
+#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
+
+static int ui_browser__percent_color(double percent, bool current)
+{
+ if (current)
+ return HE_COLORSET_SELECTED;
+ if (percent >= MIN_RED)
+ return HE_COLORSET_TOP;
+ if (percent >= MIN_GREEN)
+ return HE_COLORSET_MEDIUM;
+ return HE_COLORSET_NORMAL;
+}
+
+struct ui_browser {
+ newtComponent form, sb;
+ u64 index, first_visible_entry_idx;
+ void *first_visible_entry, *entries;
+ u16 top, left, width, height;
+ void *priv;
+ u32 nr_entries;
+};
+
+static void ui_browser__refresh_dimensions(struct ui_browser *self)
+{
+ int cols, rows;
+ newtGetScreenSize(&cols, &rows);
+
+ if (self->width > cols - 4)
+ self->width = cols - 4;
+ self->height = rows - 5;
+ if (self->height > self->nr_entries)
+ self->height = self->nr_entries;
+ self->top = (rows - self->height) / 2;
+ self->left = (cols - self->width) / 2;
+}
+
+static void ui_browser__reset_index(struct ui_browser *self)
+{
+ self->index = self->first_visible_entry_idx = 0;
+ self->first_visible_entry = NULL;
+}
+
+static int objdump_line__show(struct objdump_line *self, struct list_head *head,
+ int width, struct hist_entry *he, int len,
+ bool current_entry)
+{
+ if (self->offset != -1) {
+ struct symbol *sym = he->ms.sym;
+ unsigned int hits = 0;
+ double percent = 0.0;
+ int color;
+ struct sym_priv *priv = symbol__priv(sym);
+ struct sym_ext *sym_ext = priv->ext;
+ struct sym_hist *h = priv->hist;
+ s64 offset = self->offset;
+ struct objdump_line *next = objdump__get_next_ip_line(head, self);
+
+ while (offset < (s64)len &&
+ (next == NULL || offset < next->offset)) {
+ if (sym_ext) {
+ percent += sym_ext[offset].percent;
+ } else
+ hits += h->ip[offset];
+
+ ++offset;
+ }
+
+ if (sym_ext == NULL && h->sum)
+ percent = 100.0 * hits / h->sum;
+
+ color = ui_browser__percent_color(percent, current_entry);
+ SLsmg_set_color(color);
+ SLsmg_printf(" %7.2f ", percent);
+ if (!current_entry)
+ SLsmg_set_color(HE_COLORSET_CODE);
+ } else {
+ int color = ui_browser__percent_color(0, current_entry);
+ SLsmg_set_color(color);
+ SLsmg_write_nstring(" ", 9);
+ }
+
+ SLsmg_write_char(':');
+ SLsmg_write_nstring(" ", 8);
+ if (!*self->line)
+ SLsmg_write_nstring(" ", width - 18);
+ else
+ SLsmg_write_nstring(self->line, width - 18);
+
+ return 0;
+}
+
+static int ui_browser__refresh_entries(struct ui_browser *self)
+{
+ struct objdump_line *pos;
+ struct list_head *head = self->entries;
+ struct hist_entry *he = self->priv;
+ int row = 0;
+ int len = he->ms.sym->end - he->ms.sym->start;
+
+ if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
+ self->first_visible_entry = head->next;
+
+ pos = list_entry(self->first_visible_entry, struct objdump_line, node);
+
+ list_for_each_entry_from(pos, head, node) {
+ bool current_entry = (self->first_visible_entry_idx + row) == self->index;
+ SLsmg_gotorc(self->top + row, self->left);
+ objdump_line__show(pos, head, self->width,
+ he, len, current_entry);
+ if (++row == self->height)
+ break;
+ }
+
+ SLsmg_set_color(HE_COLORSET_NORMAL);
+ SLsmg_fill_region(self->top + row, self->left,
+ self->height - row, self->width, ' ');
+
+ return 0;
+}
+
+static int ui_browser__run(struct ui_browser *self, const char *title,
+ struct newtExitStruct *es)
+{
+ if (self->form) {
+ newtFormDestroy(self->form);
+ newtPopWindow();
+ }
+
+ ui_browser__refresh_dimensions(self);
+ newtCenteredWindow(self->width + 2, self->height, title);
+ self->form = newt_form__new();
+ if (self->form == NULL)
+ return -1;
+
+ self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height,
+ HE_COLORSET_NORMAL,
+ HE_COLORSET_SELECTED);
+ if (self->sb == NULL)
+ return -1;
+
+ newtFormAddHotKey(self->form, NEWT_KEY_UP);
+ newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
+ newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
+ newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
+ newtFormAddHotKey(self->form, NEWT_KEY_HOME);
+ newtFormAddHotKey(self->form, NEWT_KEY_END);
+
+ if (ui_browser__refresh_entries(self) < 0)
+ return -1;
+ newtFormAddComponent(self->form, self->sb);
+
+ while (1) {
+ unsigned int offset;
+
+ newtFormRun(self->form, es);
+
+ if (es->reason != NEWT_EXIT_HOTKEY)
+ break;
+ switch (es->u.key) {
+ case NEWT_KEY_DOWN:
+ if (self->index == self->nr_entries - 1)
+ break;
+ ++self->index;
+ if (self->index == self->first_visible_entry_idx + self->height) {
+ struct list_head *pos = self->first_visible_entry;
+ ++self->first_visible_entry_idx;
+ self->first_visible_entry = pos->next;
+ }
+ break;
+ case NEWT_KEY_UP:
+ if (self->index == 0)
+ break;
+ --self->index;
+ if (self->index < self->first_visible_entry_idx) {
+ struct list_head *pos = self->first_visible_entry;
+ --self->first_visible_entry_idx;
+ self->first_visible_entry = pos->prev;
+ }
+ break;
+ case NEWT_KEY_PGDN:
+ if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
+ break;
+
+ offset = self->height;
+ if (self->index + offset > self->nr_entries - 1)
+ offset = self->nr_entries - 1 - self->index;
+ self->index += offset;
+ self->first_visible_entry_idx += offset;
+
+ while (offset--) {
+ struct list_head *pos = self->first_visible_entry;
+ self->first_visible_entry = pos->next;
+ }
+
+ break;
+ case NEWT_KEY_PGUP:
+ if (self->first_visible_entry_idx == 0)
+ break;
+
+ if (self->first_visible_entry_idx < self->height)
+ offset = self->first_visible_entry_idx;
+ else
+ offset = self->height;
+
+ self->index -= offset;
+ self->first_visible_entry_idx -= offset;
+
+ while (offset--) {
+ struct list_head *pos = self->first_visible_entry;
+ self->first_visible_entry = pos->prev;
+ }
+ break;
+ case NEWT_KEY_HOME:
+ ui_browser__reset_index(self);
+ break;
+ case NEWT_KEY_END: {
+ struct list_head *head = self->entries;
+ offset = self->height - 1;
+
+ if (offset > self->nr_entries)
+ offset = self->nr_entries;
+
+ self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset;
+ self->first_visible_entry = head->prev;
+ while (offset-- != 0) {
+ struct list_head *pos = self->first_visible_entry;
+ self->first_visible_entry = pos->prev;
+ }
+ }
+ break;
+ case NEWT_KEY_ESCAPE:
+ case CTRL('c'):
+ case 'Q':
+ case 'q':
+ return 0;
+ default:
+ continue;
+ }
+ if (ui_browser__refresh_entries(self) < 0)
+ return -1;
+ }
+ return 0;
+}
+
/*
* When debugging newt problems it was useful to be able to "unroll"
* the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate
@@ -353,62 +602,40 @@ static size_t hist_entry__append_browser(struct hist_entry *self,
return ret;
}
-static void map_symbol__annotate_browser(const struct map_symbol *self,
- const char *input_name)
+static void hist_entry__annotate_browser(struct hist_entry *self)
{
- FILE *fp;
- int cols, rows;
- newtComponent form, tree;
+ struct ui_browser browser;
struct newtExitStruct es;
- char *str;
- size_t line_len, max_line_len = 0;
- size_t max_usable_width;
- char *line = NULL;
+ struct objdump_line *pos, *n;
+ LIST_HEAD(head);
- if (self->sym == NULL)
+ if (self->ms.sym == NULL)
return;
- if (asprintf(&str, "perf annotate -i \"%s\" -d \"%s\" %s 2>&1 | expand",
- input_name, self->map->dso->name, self->sym->name) < 0)
+ if (hist_entry__annotate(self, &head) < 0)
return;
- fp = popen(str, "r");
- if (fp == NULL)
- goto out_free_str;
-
ui_helpline__push("Press ESC to exit");
- newtGetScreenSize(&cols, &rows);
- tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL);
-
- while (!feof(fp)) {
- if (getline(&line, &line_len, fp) < 0 || !line_len)
- break;
- while (line_len != 0 && isspace(line[line_len - 1]))
- line[--line_len] = '\0';
- if (line_len > max_line_len)
- max_line_len = line_len;
- newtListboxAppendEntry(tree, line, NULL);
+ memset(&browser, 0, sizeof(browser));
+ browser.entries = &head;
+ browser.priv = self;
+ list_for_each_entry(pos, &head, node) {
+ size_t line_len = strlen(pos->line);
+ if (browser.width < line_len)
+ browser.width = line_len;
+ ++browser.nr_entries;
}
- fclose(fp);
- free(line);
-
- max_usable_width = cols - 22;
- if (max_line_len > max_usable_width)
- max_line_len = max_usable_width;
-
- newtListboxSetWidth(tree, max_line_len);
- newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name);
- form = newt_form__new();
- newtFormAddComponent(form, tree);
-
- newtFormRun(form, &es);
- newtFormDestroy(form);
+ browser.width += 18; /* Percentage */
+ ui_browser__run(&browser, self->ms.sym->name, &es);
+ newtFormDestroy(browser.form);
newtPopWindow();
+ list_for_each_entry_safe(pos, n, &head, node) {
+ list_del(&pos->node);
+ objdump_line__free(pos);
+ }
ui_helpline__pop();
-out_free_str:
- free(str);
}
static const void *newt__symbol_tree_get_current(newtComponent self)
@@ -527,7 +754,7 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists
return 0;
}
-static struct thread *hist_browser__selected_thread(struct hist_browser *self)
+static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
{
int *indexes;
@@ -543,7 +770,13 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self)
}
return NULL;
out:
- return *(struct thread **)(self->selection + 1);
+ return container_of(self->selection, struct hist_entry, ms);
+}
+
+static struct thread *hist_browser__selected_thread(struct hist_browser *self)
+{
+ struct hist_entry *he = hist_browser__selected_entry(self);
+ return he ? he->thread : NULL;
}
static int hist_browser__title(char *bf, size_t size, const char *input_name,
@@ -637,13 +870,20 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na
continue;
do_annotate:
if (choice == annotate) {
+ struct hist_entry *he;
+
if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
ui_helpline__puts("No vmlinux file found, can't "
"annotate with just a "
"kallsyms file");
continue;
}
- map_symbol__annotate_browser(browser->selection, input_name);
+
+ he = hist_browser__selected_entry(browser);
+ if (he == NULL)
+ continue;
+
+ hist_entry__annotate_browser(he);
} else if (choice == zoom_dso) {
if (dso_filter) {
ui_helpline__pop();
@@ -681,8 +921,23 @@ out:
return err;
}
+static struct newtPercentTreeColors {
+ const char *topColorFg, *topColorBg;
+ const char *mediumColorFg, *mediumColorBg;
+ const char *normalColorFg, *normalColorBg;
+ const char *selColorFg, *selColorBg;
+ const char *codeColorFg, *codeColorBg;
+} defaultPercentTreeColors = {
+ "red", "lightgray",
+ "green", "lightgray",
+ "black", "lightgray",
+ "lightgray", "magenta",
+ "blue", "lightgray",
+};
+
void setup_browser(void)
{
+ struct newtPercentTreeColors *c = &defaultPercentTreeColors;
if (!isatty(1))
return;
@@ -690,6 +945,11 @@ void setup_browser(void)
newtInit();
newtCls();
ui_helpline__puts(" ");
+ SLtt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
+ SLtt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
+ SLtt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
+ SLtt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
+ SLtt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
}
void exit_browser(bool wait_for_ok)