From a7c55cbee0c1bae9bf5a15a08300e91d88706e45 Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Wed, 4 Aug 2010 20:27:05 -0400 Subject: oprofile: add support for Intel processor model 30 Newer Intel processors identifying themselves as model 30 are not recognized by oprofile. model : 30 model name : Intel(R) Xeon(R) CPU X3470 @ 2.93GHz Running oprofile on these machines gives the following: + opcontrol --init + opcontrol --list-events oprofile: available events for CPU type "Intel Architectural Perfmon" See Intel 64 and IA-32 Architectures Software Developer's Manual Volume 3B (Document 253669) Chapter 18 for architectural perfmon events This is a limited set of fallback events because oprofile doesn't know your CPU CPU_CLK_UNHALTED: (counter: all) Clock cycles when not halted (min count: 6000) INST_RETIRED: (counter: all) number of instructions retired (min count: 6000) LLC_MISSES: (counter: all) Last level cache demand requests from this core that missed the LLC (min count: 6000) Unit masks (default 0x41) ---------- 0x41: No unit mask LLC_REFS: (counter: all) Last level cache demand requests from this core (min count: 6000) Unit masks (default 0x4f) ---------- 0x4f: No unit mask BR_MISS_PRED_RETIRED: (counter: all) number of mispredicted branches retired (precise) (min count: 500) + opcontrol --shutdown Tested using oprofile 0.9.6. Signed-off-by: Josh Hunt Reviewed-by: Andi Kleen Signed-off-by: Robert Richter diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c index 1ba67dc..f6b48f6 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c @@ -668,6 +668,7 @@ static int __init ppro_init(char **cpu_type) *cpu_type = "i386/core_2"; break; case 0x1a: + case 0x1e: case 0x2e: spec = &op_arch_perfmon_spec; *cpu_type = "i386/core_i7"; -- cgit v0.10.2 From c408fedfc4a1fa16e611ffd6f3280301b38614be Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 5 Aug 2010 12:59:47 -0300 Subject: perf symbols: Store the symbol binding So that tools that wan't to act only on a subset of (weak, global, local) symbols can do so, such as the upcoming uprobes support in 'perf probe'. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Srikar Dronamraju Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 6f0dd90..b6f5970 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -131,7 +131,8 @@ static void map_groups__fixup_end(struct map_groups *self) __map_groups__fixup_end(self, i); } -static struct symbol *symbol__new(u64 start, u64 len, const char *name) +static struct symbol *symbol__new(u64 start, u64 len, u8 binding, + const char *name) { size_t namelen = strlen(name) + 1; struct symbol *self = calloc(1, (symbol_conf.priv_size + @@ -144,6 +145,7 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name) self->start = start; self->end = len ? start + len - 1 : start; + self->binding = binding; self->namelen = namelen - 1; pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); @@ -160,8 +162,11 @@ void symbol__delete(struct symbol *self) static size_t symbol__fprintf(struct symbol *self, FILE *fp) { - return fprintf(fp, " %llx-%llx %s\n", - self->start, self->end, self->name); + return fprintf(fp, " %llx-%llx %c %s\n", + self->start, self->end, + self->binding == STB_GLOBAL ? 'g' : + self->binding == STB_LOCAL ? 'l' : 'w', + self->name); } void dso__set_long_name(struct dso *self, char *name) @@ -453,6 +458,14 @@ struct process_kallsyms_args { struct dso *dso; }; +static u8 kallsyms2elf_type(char type) +{ + if (type == 'W') + return STB_WEAK; + + return isupper(type) ? STB_GLOBAL : STB_LOCAL; +} + static int map__process_kallsym_symbol(void *arg, const char *name, char type, u64 start) { @@ -466,7 +479,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name, /* * Will fix up the end later, when we have all symbols sorted. */ - sym = symbol__new(start, 0, name); + sym = symbol__new(start, 0, kallsyms2elf_type(type), name); if (sym == NULL) return -ENOMEM; @@ -661,7 +674,7 @@ static int dso__load_perf_map(struct dso *self, struct map *map, if (len + 2 >= line_len) continue; - sym = symbol__new(start, size, line + len); + sym = symbol__new(start, size, STB_GLOBAL, line + len); if (sym == NULL) goto out_delete_line; @@ -873,7 +886,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map, "%s@plt", elf_sym__name(&sym, symstrs)); f = symbol__new(plt_offset, shdr_plt.sh_entsize, - sympltname); + STB_GLOBAL, sympltname); if (!f) goto out_elf_end; @@ -895,7 +908,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map, "%s@plt", elf_sym__name(&sym, symstrs)); f = symbol__new(plt_offset, shdr_plt.sh_entsize, - sympltname); + STB_GLOBAL, sympltname); if (!f) goto out_elf_end; @@ -1146,7 +1159,8 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, if (demangled != NULL) elf_name = demangled; new_symbol: - f = symbol__new(sym.st_value, sym.st_size, elf_name); + f = symbol__new(sym.st_value, sym.st_size, + GELF_ST_BIND(sym.st_info), elf_name); free(demangled); if (!f) goto out_elf_end; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 906be20..b7a8da4 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -53,6 +53,7 @@ struct symbol { u64 start; u64 end; u16 namelen; + u8 binding; char name[0]; }; -- cgit v0.10.2 From 9a725995e88fd3fd79daf99819c51d676ba37ad9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 5 Aug 2010 17:00:42 -0300 Subject: perf ui: Add a map browser Press -> and then "Browse map details" to see the DSO long name as the title and the list of symbols in the DSO used by the map where the current symbol is. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Srikar Dronamraju Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 91de99b..fc4a2b3 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -280,6 +281,7 @@ struct ui_browser { u16 top, left, width, height; void *priv; unsigned int (*refresh_entries)(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); u32 nr_entries; @@ -316,6 +318,58 @@ static void ui_browser__list_head_seek(struct ui_browser *self, self->first_visible_entry = pos; } +static 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->first_visible_entry; + 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->first_visible_entry = nd; +} + +static unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self) +{ + struct rb_node *nd; + int row = 0; + + if (self->first_visible_entry == NULL) + self->first_visible_entry = rb_first(self->entries); + + nd = self->first_visible_entry; + + while (nd != NULL) { + SLsmg_gotorc(self->top + row, self->left); + self->write(self, nd, row); + if (++row == self->height) + break; + nd = rb_next(nd); + } + + return row; +} + static bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) { return (self->first_visible_entry_idx + row) == self->index; @@ -592,6 +646,70 @@ int hist_entry__tui_annotate(struct hist_entry *self) return ret; } +/* -------------------------------------------------------------------- */ + +struct map_browser { + struct ui_browser b; + struct map *map; + u16 namelen; + u8 addrlen; +}; + +static void map_browser__write(struct ui_browser *self, void *nd, int row) +{ + struct symbol *sym = rb_entry(nd, struct symbol, rb_node); + struct map_browser *mb = container_of(self, struct map_browser, b); + bool current_entry = ui_browser__is_current_entry(self, row); + int color = ui_browser__percent_color(0, current_entry); + + SLsmg_set_color(color); + slsmg_printf("%*llx %*llx %c ", + mb->addrlen, sym->start, mb->addrlen, sym->end, + sym->binding == STB_GLOBAL ? 'g' : + sym->binding == STB_LOCAL ? 'l' : 'w'); + slsmg_write_nstring(sym->name, mb->namelen); +} + +static int map__browse(struct map *self) +{ + struct map_browser mb = { + .b = { + .entries = &self->dso->symbols[self->type], + .refresh_entries = ui_browser__rb_tree_refresh, + .seek = ui_browser__rb_tree_seek, + .write = map_browser__write, + }, + }; + struct newtExitStruct es; + struct rb_node *nd; + char tmp[BITS_PER_LONG / 4]; + u64 maxaddr = 0; + int ret; + + ui_helpline__push("Press <- or ESC to exit"); + + for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) { + struct symbol *pos = rb_entry(nd, struct symbol, rb_node); + + if (mb.namelen < pos->namelen) + mb.namelen = pos->namelen; + if (maxaddr < pos->end) + maxaddr = pos->end; + ++mb.b.nr_entries; + } + + mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr); + mb.b.width += mb.addrlen * 2 + 4 + mb.namelen; + ui_browser__show(&mb.b, self->dso->long_name); + ret = ui_browser__run(&mb.b, &es); + newtFormDestroy(mb.b.form); + newtPopWindow(); + ui_helpline__pop(); + return ret; +} + +/* -------------------------------------------------------------------- */ + struct hist_browser { struct ui_browser b; struct hists *hists; @@ -680,7 +798,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) const struct dso *dso; char *options[16]; int nr_options = 0, choice = 0, i, - annotate = -2, zoom_dso = -2, zoom_thread = -2; + annotate = -2, zoom_dso = -2, zoom_thread = -2, + browse_map = -2; if (hist_browser__run(browser, msg, &es)) break; @@ -771,6 +890,10 @@ do_help: (dso->kernel ? "the Kernel" : dso->short_name)) > 0) zoom_dso = nr_options++; + if (browser->selection->map != NULL && + asprintf(&options[nr_options], "Browse map details") > 0) + browse_map = nr_options++; + options[nr_options++] = (char *)"Exit"; choice = popup_menu(nr_options, options); @@ -800,7 +923,9 @@ do_annotate: continue; hist_entry__tui_annotate(he); - } else if (choice == zoom_dso) { + } else if (choice == browse_map) + map__browse(browser->selection->map); + else if (choice == zoom_dso) { zoom_dso: if (dso_filter) { pstack__remove(fstack, &dso_filter); -- cgit v0.10.2 From 76ce93d0b61fa8c61b9cd917d9f7190b40fb29b6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 5 Aug 2010 17:02:54 -0300 Subject: perf ui: Shorten ui_browser->refresh_entries to refresh LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index fc4a2b3..f98a240 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -280,7 +280,7 @@ struct ui_browser { void *first_visible_entry, *entries; u16 top, left, width, height; void *priv; - unsigned int (*refresh_entries)(struct ui_browser *self); + 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); @@ -472,12 +472,12 @@ static int objdump_line__show(struct objdump_line *self, struct list_head *head, return 0; } -static int ui_browser__refresh_entries(struct ui_browser *self) +static int ui_browser__refresh(struct ui_browser *self) { int row; newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); - row = self->refresh_entries(self); + row = self->refresh(self); SLsmg_set_color(HE_COLORSET_NORMAL); SLsmg_fill_region(self->top + row, self->left, self->height - row, self->width, ' '); @@ -487,7 +487,7 @@ static int ui_browser__refresh_entries(struct ui_browser *self) static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) { - if (ui_browser__refresh_entries(self) < 0) + if (ui_browser__refresh(self) < 0) return -1; while (1) { @@ -558,7 +558,7 @@ static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) default: return es->u.key; } - if (ui_browser__refresh_entries(self) < 0) + if (ui_browser__refresh(self) < 0) return -1; } return 0; @@ -621,9 +621,9 @@ int hist_entry__tui_annotate(struct hist_entry *self) ui_helpline__push("Press <- or ESC to exit"); memset(&browser, 0, sizeof(browser)); - browser.entries = &head; - browser.refresh_entries = hist_entry__annotate_browser_refresh; - browser.seek = ui_browser__list_head_seek; + browser.entries = &head; + browser.refresh = hist_entry__annotate_browser_refresh; + browser.seek = ui_browser__list_head_seek; browser.priv = self; list_for_each_entry(pos, &head, node) { size_t line_len = strlen(pos->line); @@ -675,7 +675,7 @@ static int map__browse(struct map *self) struct map_browser mb = { .b = { .entries = &self->dso->symbols[self->type], - .refresh_entries = ui_browser__rb_tree_refresh, + .refresh = ui_browser__rb_tree_refresh, .seek = ui_browser__rb_tree_seek, .write = map_browser__write, }, @@ -720,7 +720,7 @@ struct hist_browser { static void hist_browser__reset(struct hist_browser *self); static int hist_browser__run(struct hist_browser *self, const char *title, struct newtExitStruct *es); -static unsigned int hist_browser__refresh_entries(struct ui_browser *self); +static unsigned int hist_browser__refresh(struct ui_browser *self); static void ui_browser__hists_seek(struct ui_browser *self, off_t offset, int whence); @@ -730,7 +730,7 @@ static struct hist_browser *hist_browser__new(struct hists *hists) if (self) { self->hists = hists; - self->b.refresh_entries = hist_browser__refresh_entries; + self->b.refresh = hist_browser__refresh; self->b.seek = ui_browser__hists_seek; } @@ -1338,7 +1338,7 @@ static int hist_browser__show_entry(struct hist_browser *self, return printed; } -static unsigned int hist_browser__refresh_entries(struct ui_browser *self) +static unsigned int hist_browser__refresh(struct ui_browser *self) { unsigned row = 0; struct rb_node *nd; -- cgit v0.10.2 From 903cce6eb9117550755de9bf92f3b48367b7dfe0 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 5 Aug 2010 19:15:48 -0300 Subject: perf hists: Handle verbose in hists__sort_list_width Otherwise entries will get chopped up on the window. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index e7263d4..62ec9b0 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -876,6 +876,9 @@ unsigned int hists__sort_list_width(struct hists *self) if (!se->elide) ret += 2 + hists__col_len(self, se->se_width_idx); + if (verbose) /* Addr + origin */ + ret += 3 + BITS_PER_LONG / 4; + return ret; } -- cgit v0.10.2 From fb89941ea761f53201959cc217a2c73f6fe13855 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 5 Aug 2010 19:17:22 -0300 Subject: perf hists: Fixup addr snprintf width on 32 bit arches By using BITS_PER_LONG/4 as the width specifier. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 1c61a4f..b62a553 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -196,7 +196,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, if (verbose) { char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; - ret += repsep_snprintf(bf, size, "%#018llx %c ", self->ip, o); + ret += repsep_snprintf(bf, size, "%*Lx %c ", + BITS_PER_LONG / 4, self->ip, o); } ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); @@ -204,7 +205,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, ret += repsep_snprintf(bf + ret, size - ret, "%s", self->ms.sym->name); else - ret += repsep_snprintf(bf + ret, size - ret, "%#016llx", self->ip); + ret += repsep_snprintf(bf + ret, size - ret, "%*Lx", + BITS_PER_LONG / 4, self->ip); return ret; } -- cgit v0.10.2 From 80d50cae1b9cc958171c36fffc7357a5abad808c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 5 Aug 2010 19:28:27 -0300 Subject: perf ui: Add search by name/addr to the map__browser Only in verbose mode so as not to bloat struct symbol too much. The key used is '/', just like in vi, less, etc. More work is needed to allocate space on the symbol in a more clear way. This experiment shows how to do it for the hist_browser, in the main window. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Srikar Dronamraju Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 2f4b929..4a7a743 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -478,8 +478,24 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) * so don't allocate extra space that won't be used in the stdio * implementation. */ - if (use_browser > 0) + if (use_browser > 0) { symbol_conf.priv_size = sizeof(struct sym_priv); + /* + * For searching by name on the "Browse map details". + * providing it only in verbose mode not to bloat too + * much struct symbol. + */ + if (verbose) { + /* + * XXX: Need to provide a less kludgy way to ask for + * more space per symbol, the u32 is for the index on + * the ui browser. + * See symbol__browser_index. + */ + symbol_conf.priv_size += sizeof(u32); + symbol_conf.sort_by_name = true; + } + } if (symbol__init() < 0) return -1; diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index f98a240..e2deae0 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -128,6 +128,39 @@ static void ui_helpline__puts(const char *msg) ui_helpline__push(msg); } +static int ui_entry__read(const char *title, char *bf, size_t size, int width) +{ + struct newtExitStruct es; + newtComponent form, entry; + const char *result; + int err = -1; + + newtCenteredWindow(width, 1, title); + form = newtForm(NULL, NULL, 0); + if (form == NULL) + return -1; + + entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL); + if (entry == NULL) + goto out_free_form; + + newtFormAddComponent(form, entry); + newtFormAddHotKey(form, NEWT_KEY_ENTER); + newtFormAddHotKey(form, NEWT_KEY_ESCAPE); + newtFormAddHotKey(form, NEWT_KEY_LEFT); + newtFormAddHotKey(form, CTRL('c')); + newtFormRun(form, &es); + + if (result != NULL) { + strncpy(bf, result, size); + err = 0; + } +out_free_form: + newtPopWindow(); + newtFormDestroy(form); + return 0; +} + static char browser__last_msg[1024]; int browser__show_help(const char *format, va_list ap) @@ -670,6 +703,67 @@ static void map_browser__write(struct ui_browser *self, void *nd, int row) slsmg_write_nstring(sym->name, mb->namelen); } +/* FIXME uber-kludgy, see comment on cmd_report... */ +static u32 *symbol__browser_index(struct symbol *self) +{ + return ((void *)self) - sizeof(struct rb_node) - sizeof(u32); +} + +static int map_browser__search(struct map_browser *self) +{ + char target[512]; + struct symbol *sym; + int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40); + + if (err) + return err; + + if (target[0] == '0' && tolower(target[1]) == 'x') { + u64 addr = strtoull(target, NULL, 16); + sym = map__find_symbol(self->map, addr, NULL); + } else + sym = map__find_symbol_by_name(self->map, target, NULL); + + if (sym != NULL) { + u32 *idx = symbol__browser_index(sym); + + self->b.first_visible_entry = &sym->rb_node; + self->b.index = self->b.first_visible_entry_idx = *idx; + } else + ui_helpline__fpush("%s not found!", target); + + return 0; +} + +static int map_browser__run(struct map_browser *self, struct newtExitStruct *es) +{ + if (ui_browser__show(&self->b, self->map->dso->long_name) < 0) + return -1; + + ui_helpline__fpush("Press <- or ESC to exit, %s / to search", + verbose ? "" : "restart with -v to use"); + newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); + newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); + if (verbose) + newtFormAddHotKey(self->b.form, '/'); + + while (1) { + ui_browser__run(&self->b, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + if (verbose && es->u.key == '/') + map_browser__search(self); + else + break; + } + + newtFormDestroy(self->b.form); + newtPopWindow(); + ui_helpline__pop(); + return 0; +} + static int map__browse(struct map *self) { struct map_browser mb = { @@ -679,14 +773,12 @@ static int map__browse(struct map *self) .seek = ui_browser__rb_tree_seek, .write = map_browser__write, }, + .map = self, }; struct newtExitStruct es; struct rb_node *nd; char tmp[BITS_PER_LONG / 4]; u64 maxaddr = 0; - int ret; - - ui_helpline__push("Press <- or ESC to exit"); for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) { struct symbol *pos = rb_entry(nd, struct symbol, rb_node); @@ -695,17 +787,16 @@ static int map__browse(struct map *self) mb.namelen = pos->namelen; if (maxaddr < pos->end) maxaddr = pos->end; + if (verbose) { + u32 *idx = symbol__browser_index(pos); + *idx = mb.b.nr_entries; + } ++mb.b.nr_entries; } mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr); mb.b.width += mb.addrlen * 2 + 4 + mb.namelen; - ui_browser__show(&mb.b, self->dso->long_name); - ret = ui_browser__run(&mb.b, &es); - newtFormDestroy(mb.b.form); - newtPopWindow(); - ui_helpline__pop(); - return ret; + return map_browser__run(&mb, &es); } /* -------------------------------------------------------------------- */ -- cgit v0.10.2 From 7935f65f77e1cd19fe0a094fc1b34e258565751e Mon Sep 17 00:00:00 2001 From: Andrea Gelmini Date: Thu, 5 Aug 2010 15:51:39 +0200 Subject: perf probe: Remove duplicated #include Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Masami Hiramatsu Cc: Frederic Weisbecker LKML-Reference: <1281016299-23958-15-git-send-email-andrea.gelmini@gelma.net> Signed-off-by: Andrea Gelmini Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 840f1aa..6c7750d 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -33,7 +33,6 @@ #include #include -#include "string.h" #include "event.h" #include "debug.h" #include "util.h" -- cgit v0.10.2 From b7eead86d2951dab733d36554e8166a2a91d7363 Mon Sep 17 00:00:00 2001 From: Andrea Gelmini Date: Thu, 5 Aug 2010 15:51:38 +0200 Subject: perf trace: Clean up #includes Removed duplicated #includes util/trace-event.h and util/exec_cmd.h. Grouped and sorted all the #includes. Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Tom Zanussi Cc: Frederic Weisbecker Cc: Thomas Gleixner LKML-Reference: <1281016299-23958-14-git-send-email-andrea.gelmini@gelma.net> Signed-off-by: Andrea Gelmini Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 294da72..40a6a29 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1,13 +1,16 @@ #include "builtin.h" -#include "util/util.h" +#include "perf.h" #include "util/cache.h" +#include "util/debug.h" +#include "util/exec_cmd.h" +#include "util/header.h" +#include "util/parse-options.h" +#include "util/session.h" #include "util/symbol.h" #include "util/thread.h" -#include "util/header.h" -#include "util/exec_cmd.h" #include "util/trace-event.h" -#include "util/session.h" +#include "util/util.h" static char const *script_name; static char const *generate_script_lang; @@ -59,14 +62,6 @@ static int cleanup_scripting(void) return scripting_ops->stop_script(); } -#include "util/parse-options.h" - -#include "perf.h" -#include "util/debug.h" - -#include "util/trace-event.h" -#include "util/exec_cmd.h" - static char const *input_name = "perf.data"; static int process_sample_event(event_t *event, struct perf_session *session) -- cgit v0.10.2 From 33e26a1b492d21283d1372ddd547562d371dab98 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 5 Aug 2010 22:27:51 +0200 Subject: perf timechart: Adjust confusing if indentation Outdent the code following the if. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r disable braces4@ position p1,p2; statement S1,S2; @@ ( if (...) { ... } | if (...) S1@p1 S2@p2 ) @script:python@ p1 << r.p1; p2 << r.p2; @@ if (p1[0].column == p2[0].column): cocci.print_main("branch",p1) cocci.print_secs("after",p2) // Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar LKML-Reference: Signed-off-by: Julia Lawall Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 5161619..9bcc38f 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -455,8 +455,8 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) if (p->current->state != TYPE_NONE) pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp); - p->current->state_since = timestamp; - p->current->state = TYPE_RUNNING; + p->current->state_since = timestamp; + p->current->state = TYPE_RUNNING; } if (prev_p->current) { -- cgit v0.10.2 From 71e7cf3a37ba6189fa7215555e8e760b400fc23b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 5 Aug 2010 19:41:44 -0300 Subject: perf report: Speed up exit path When cmd_record exits the whole perf binary will exit right after, so no need to traverse lots of complex data structures freeing them. Sticked a comment for leak detectives and for a experiment with obstacks to be performed so that we can speed up freeing that memory. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Nick Piggin Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 4a7a743..55fc1f4 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -348,7 +348,18 @@ static int __cmd_report(void) hists__tty_browse_tree(&session->hists_tree, help); out_delete: - perf_session__delete(session); + /* + * Speed up the exit process, for large files this can + * take quite a while. + * + * XXX Enable this when using valgrind or if we ever + * librarize this command. + * + * Also experiment with obstacks to see how much speed + * up we'll get here. + * + * perf_session__delete(session); + */ return ret; } -- cgit v0.10.2 From 58432e1f3625ef22b347ec8f9487e1852aa9ad67 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 3 Aug 2010 11:11:36 +0900 Subject: perf probe: Fix to copy the type for raw parameters Copy type field if it is for raw parameters. Without this fix, perf probe drops the type if user passes it for raw parameters (e.g. %ax:u32 will be converted to %ax). Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Frederic Weisbecker Signed-off-by: Masami Hiramatsu LKML-Reference: <4C577AD8.50808@hitachi.com> Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 6c7750d..5251366 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -705,8 +705,12 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) pf->tvar->value = strdup(pf->pvar->var); if (pf->tvar->value == NULL) return -ENOMEM; - else - return 0; + if (pf->pvar->type) { + pf->tvar->type = strdup(pf->pvar->type); + if (pf->tvar->type == NULL) + return -ENOMEM; + } + return 0; } pr_debug("Searching '%s' variable in context.\n", -- cgit v0.10.2 From 449e5b247ca7c9dc9fc3391b7eebfefdeb2ce1fc Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 3 Aug 2010 11:11:40 +0900 Subject: perf probe: Fix memory leaks in add_perf_probe_events Fix several memory leaks of pkgs and tevs in add_perf_probe_events(). Reported-by: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: linux-kernel@vger.kernel.org LKML-Reference: <4C577ADC.1000309@hitachi.com> Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 2e665cb..e72f05c 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -1606,8 +1606,10 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, /* Init vmlinux path */ ret = init_vmlinux(); - if (ret < 0) + if (ret < 0) { + free(pkgs); return ret; + } /* Loop 1: convert all events */ for (i = 0; i < npevs; i++) { @@ -1625,10 +1627,13 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, pkgs[i].ntevs, force_add); end: - /* Loop 3: cleanup trace events */ - for (i = 0; i < npevs; i++) + /* Loop 3: cleanup and free trace events */ + for (i = 0; i < npevs; i++) { for (j = 0; j < pkgs[i].ntevs; j++) clear_probe_trace_event(&pkgs[i].tevs[j]); + free(pkgs[i].tevs); + } + free(pkgs); return ret; } -- cgit v0.10.2 From 43730982c3e9355dd8bd6b31f0a0a3508ad4209d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 6 Aug 2010 16:51:12 -0300 Subject: perf tui: Introduce list_head based generic ui_browser refresh routine So that building other browser based on structures linked via a linked list can be as easy as it is already for the ones linked via an rb_tree. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h index dbe4b81..f5ca26e 100644 --- a/tools/perf/util/include/linux/list.h +++ b/tools/perf/util/include/linux/list.h @@ -15,4 +15,12 @@ static inline void list_del_range(struct list_head *begin, begin->prev->next = end->next; end->next->prev = begin->prev; } + +/** + * list_for_each_from - iterate over a list from one of its nodes + * @pos: the &struct list_head to use as a loop cursor, from where to start + * @head: the head for your list. + */ +#define list_for_each_from(pos, head) \ + for (; prefetch(pos->next), pos != (head); pos = pos->next) #endif diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index e2deae0..37fe8eb 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -456,20 +456,24 @@ static int ui_browser__show(struct ui_browser *self, const char *title) return 0; } -static int objdump_line__show(struct objdump_line *self, struct list_head *head, - int width, struct hist_entry *he, int len, - bool current_entry) +static void annotate_browser__write(struct ui_browser *self, void *entry, int row) { - if (self->offset != -1) { + struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); + bool current_entry = ui_browser__is_current_entry(self, row); + int width = self->width; + + if (ol->offset != -1) { + struct hist_entry *he = self->priv; struct symbol *sym = he->ms.sym; + int len = he->ms.sym->end - he->ms.sym->start; unsigned int hits = 0; double percent = 0.0; int color; struct sym_priv *priv = symbol__priv(sym); struct sym_ext *sym_ext = priv->ext; struct sym_hist *h = priv->hist; - s64 offset = self->offset; - struct objdump_line *next = objdump__get_next_ip_line(head, self); + s64 offset = ol->offset; + struct objdump_line *next = objdump__get_next_ip_line(self->entries, ol); while (offset < (s64)len && (next == NULL || offset < next->offset)) { @@ -497,12 +501,10 @@ static int objdump_line__show(struct objdump_line *self, struct list_head *head, SLsmg_write_char(':'); slsmg_write_nstring(" ", 8); - if (!*self->line) + if (!*ol->line) slsmg_write_nstring(" ", width - 18); else - slsmg_write_nstring(self->line, width - 18); - - return 0; + slsmg_write_nstring(ol->line, width - 18); } static int ui_browser__refresh(struct ui_browser *self) @@ -607,24 +609,20 @@ static char *callchain_list__sym_name(struct callchain_list *self, return bf; } -static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self) +static unsigned int ui_browser__list_head_refresh(struct ui_browser *self) { - struct objdump_line *pos; + struct list_head *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); + pos = self->first_visible_entry; - list_for_each_entry_from(pos, head, node) { - bool current_entry = ui_browser__is_current_entry(self, row); + list_for_each_from(pos, head) { SLsmg_gotorc(self->top + row, self->left); - objdump_line__show(pos, head, self->width, - he, len, current_entry); + self->write(self, pos, row); if (++row == self->height) break; } @@ -634,10 +632,16 @@ static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self int hist_entry__tui_annotate(struct hist_entry *self) { - struct ui_browser browser; struct newtExitStruct es; struct objdump_line *pos, *n; LIST_HEAD(head); + struct ui_browser browser = { + .entries = &head, + .refresh = ui_browser__list_head_refresh, + .seek = ui_browser__list_head_seek, + .write = annotate_browser__write, + .priv = self, + }; int ret; if (self->ms.sym == NULL) @@ -653,11 +657,6 @@ int hist_entry__tui_annotate(struct hist_entry *self) ui_helpline__push("Press <- or ESC to exit"); - memset(&browser, 0, sizeof(browser)); - browser.entries = &head; - browser.refresh = hist_entry__annotate_browser_refresh; - browser.seek = ui_browser__list_head_seek; - browser.priv = self; list_for_each_entry(pos, &head, node) { size_t line_len = strlen(pos->line); if (browser.width < line_len) -- cgit v0.10.2 From ef8f34aabf2450a9fb36b2c87fe0ea0b86a38195 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 6 Aug 2010 17:35:02 -0300 Subject: perf ui: Start breaking down newt.c into multiple files As new TUI features get added the newt.c file is growing a lot and its name is growing misleading as an effort is being made to reduce the coupling with libnewt. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 26f626d..d5bce76 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -157,9 +157,8 @@ all:: # # Define NO_DWARF if you do not want debug-info analysis feature at all. -$(shell sh -c 'mkdir -p $(OUTPUT)scripts/python/Perf-Trace-Util/' 2> /dev/null) -$(shell sh -c 'mkdir -p $(OUTPUT)scripts/perl/Perf-Trace-Util/' 2> /dev/null) -$(shell sh -c 'mkdir -p $(OUTPUT)util/scripting-engines/' 2> /dev/null) +$(shell sh -c 'mkdir -p $(OUTPUT)scripts/{perl,python}/Perf-Trace-Util/' 2> /dev/null) +$(shell sh -c 'mkdir -p $(OUTPUT)util/{ui,scripting-engines}/' 2> /dev/null) $(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null) $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE @@ -569,6 +568,8 @@ else BASIC_CFLAGS += -I/usr/include/slang EXTLIBS += -lnewt -lslang LIB_OBJS += $(OUTPUT)util/newt.o + LIB_OBJS += $(OUTPUT)util/ui/browser.o + LIB_H += util/ui/browser.h endif endif @@ -969,6 +970,9 @@ $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< +$(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< + $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 37fe8eb..266a9e0 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -23,6 +23,7 @@ #include "session.h" #include "sort.h" #include "symbol.h" +#include "ui/browser.h" #if SLANG_VERSION < 20104 #define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args) @@ -35,6 +36,8 @@ #define sltt_set_color SLtt_set_color #endif +newtComponent newt_form__new(void); + struct ui_progress { newtComponent form, scale; }; @@ -190,7 +193,7 @@ static void newt_form__set_exit_keys(newtComponent self) newtFormAddHotKey(self, CTRL('c')); } -static newtComponent newt_form__new(void) +newtComponent newt_form__new(void) { newtComponent self = newtForm(NULL, NULL, 0); if (self) @@ -290,172 +293,6 @@ static void ui__error_window(const char *fmt, ...) va_end(ap); } -#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; - 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); - u32 nr_entries; -}; - -static void ui_browser__list_head_seek(struct ui_browser *self, - off_t offset, int whence) -{ - struct list_head *head = self->entries; - struct list_head *pos; - - switch (whence) { - case SEEK_SET: - pos = head->next; - break; - case SEEK_CUR: - pos = self->first_visible_entry; - break; - case SEEK_END: - pos = head->prev; - break; - default: - return; - } - - if (offset > 0) { - while (offset-- != 0) - pos = pos->next; - } else { - while (offset++ != 0) - pos = pos->prev; - } - - self->first_visible_entry = pos; -} - -static 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->first_visible_entry; - 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->first_visible_entry = nd; -} - -static unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self) -{ - struct rb_node *nd; - int row = 0; - - if (self->first_visible_entry == NULL) - self->first_visible_entry = rb_first(self->entries); - - nd = self->first_visible_entry; - - while (nd != NULL) { - SLsmg_gotorc(self->top + row, self->left); - self->write(self, nd, row); - if (++row == self->height) - break; - nd = rb_next(nd); - } - - return row; -} - -static bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) -{ - return (self->first_visible_entry_idx + row) == self->index; -} - -static void ui_browser__refresh_dimensions(struct ui_browser *self) -{ - int cols, rows; - 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->seek(self, 0, SEEK_SET); -} - -static int ui_browser__show(struct ui_browser *self, const char *title) -{ - if (self->form != NULL) { - newtFormDestroy(self->form); - newtPopWindow(); - } - ui_browser__refresh_dimensions(self); - newtCenteredWindow(self->width, self->height, title); - self->form = newt_form__new(); - if (self->form == NULL) - return -1; - - self->sb = newtVerticalScrollbar(self->width, 0, self->height, - HE_COLORSET_NORMAL, - HE_COLORSET_SELECTED); - if (self->sb == NULL) - return -1; - - newtFormAddHotKey(self->form, NEWT_KEY_UP); - newtFormAddHotKey(self->form, NEWT_KEY_DOWN); - newtFormAddHotKey(self->form, NEWT_KEY_PGUP); - newtFormAddHotKey(self->form, NEWT_KEY_PGDN); - newtFormAddHotKey(self->form, NEWT_KEY_HOME); - newtFormAddHotKey(self->form, NEWT_KEY_END); - newtFormAddComponent(self->form, self->sb); - return 0; -} - static void annotate_browser__write(struct ui_browser *self, void *entry, int row) { struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); @@ -507,98 +344,6 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro slsmg_write_nstring(ol->line, width - 18); } -static int ui_browser__refresh(struct ui_browser *self) -{ - int row; - - newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); - row = self->refresh(self); - SLsmg_set_color(HE_COLORSET_NORMAL); - SLsmg_fill_region(self->top + row, self->left, - self->height - row, self->width, ' '); - - return 0; -} - -static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) -{ - if (ui_browser__refresh(self) < 0) - return -1; - - while (1) { - off_t offset; - - newtFormRun(self->form, es); - - if (es->reason != NEWT_EXIT_HOTKEY) - break; - if (is_exit_key(es->u.key)) - return es->u.key; - 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) { - ++self->first_visible_entry_idx; - self->seek(self, +1, SEEK_CUR); - } - break; - case NEWT_KEY_UP: - if (self->index == 0) - break; - --self->index; - if (self->index < self->first_visible_entry_idx) { - --self->first_visible_entry_idx; - self->seek(self, -1, SEEK_CUR); - } - break; - case NEWT_KEY_PGDN: - case ' ': - if (self->first_visible_entry_idx + self->height > self->nr_entries - 1) - break; - - offset = self->height; - if (self->index + offset > self->nr_entries - 1) - offset = self->nr_entries - 1 - self->index; - self->index += offset; - self->first_visible_entry_idx += offset; - self->seek(self, +offset, SEEK_CUR); - 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; - self->seek(self, -offset, SEEK_CUR); - break; - case NEWT_KEY_HOME: - ui_browser__reset_index(self); - break; - case NEWT_KEY_END: - offset = self->height - 1; - if (offset >= self->nr_entries) - offset = self->nr_entries - 1; - - self->index = self->nr_entries - 1; - self->first_visible_entry_idx = self->index - offset; - self->seek(self, -offset, SEEK_END); - break; - default: - return es->u.key; - } - if (ui_browser__refresh(self) < 0) - return -1; - } - return 0; -} - static char *callchain_list__sym_name(struct callchain_list *self, char *bf, size_t bfsize) { @@ -609,27 +354,6 @@ static char *callchain_list__sym_name(struct callchain_list *self, return bf; } -static 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->first_visible_entry == NULL || self->first_visible_entry == self->entries) - self->first_visible_entry = head->next; - - pos = self->first_visible_entry; - - list_for_each_from(pos, head) { - SLsmg_gotorc(self->top + row, self->left); - self->write(self, pos, row); - if (++row == self->height) - break; - } - - return row; -} - int hist_entry__tui_annotate(struct hist_entry *self) { struct newtExitStruct es; @@ -1093,20 +817,6 @@ int hists__tui_browse_tree(struct rb_root *self, const char *help) return key; } -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", -}; - static void newt_suspend(void *d __used) { newtSuspend(); @@ -1116,8 +826,6 @@ static void newt_suspend(void *d __used) void setup_browser(void) { - struct newtPercentTreeColors *c = &defaultPercentTreeColors; - if (!isatty(1) || !use_browser || dump_trace) { use_browser = 0; setup_pager(); @@ -1129,11 +837,7 @@ void setup_browser(void) newtCls(); newtSetSuspendCallback(newt_suspend, NULL); ui_helpline__puts(" "); - sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); - sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); - 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); + ui_browser__init(); } void exit_browser(bool wait_for_ok) diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c new file mode 100644 index 0000000..0b2b930 --- /dev/null +++ b/tools/perf/util/ui/browser.c @@ -0,0 +1,313 @@ +#define _GNU_SOURCE +#include +#undef _GNU_SOURCE +/* + * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks + * the build if it isn't defined. Use the equivalent one that glibc + * has on features.h. + */ +#include +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG +#endif +#include +#include +#include +#include +#include +#include "browser.h" +#include "../color.h" +#include "../util.h" + +#if SLANG_VERSION < 20104 +#define sltt_set_color(obj, name, fg, bg) \ + SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg) +#else +#define sltt_set_color SLtt_set_color +#endif + +newtComponent newt_form__new(void); + +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; +} + +void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence) +{ + struct list_head *head = self->entries; + struct list_head *pos; + + switch (whence) { + case SEEK_SET: + pos = head->next; + break; + case SEEK_CUR: + pos = self->first_visible_entry; + break; + case SEEK_END: + pos = head->prev; + break; + default: + return; + } + + if (offset > 0) { + while (offset-- != 0) + pos = pos->next; + } else { + while (offset++ != 0) + pos = pos->prev; + } + + self->first_visible_entry = pos; +} + +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->first_visible_entry; + 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->first_visible_entry = nd; +} + +unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self) +{ + struct rb_node *nd; + int row = 0; + + if (self->first_visible_entry == NULL) + self->first_visible_entry = rb_first(self->entries); + + nd = self->first_visible_entry; + + while (nd != NULL) { + SLsmg_gotorc(self->top + row, self->left); + 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->first_visible_entry_idx + row) == self->index; +} + +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; +} + +void ui_browser__reset_index(struct ui_browser *self) +{ + self->index = self->first_visible_entry_idx = 0; + self->seek(self, 0, SEEK_SET); +} + +int ui_browser__show(struct ui_browser *self, const char *title) +{ + if (self->form != NULL) { + newtFormDestroy(self->form); + newtPopWindow(); + } + ui_browser__refresh_dimensions(self); + newtCenteredWindow(self->width, self->height, title); + self->form = newt_form__new(); + if (self->form == NULL) + return -1; + + self->sb = newtVerticalScrollbar(self->width, 0, self->height, + HE_COLORSET_NORMAL, + HE_COLORSET_SELECTED); + if (self->sb == NULL) + return -1; + + newtFormAddHotKey(self->form, NEWT_KEY_UP); + newtFormAddHotKey(self->form, NEWT_KEY_DOWN); + newtFormAddHotKey(self->form, NEWT_KEY_PGUP); + newtFormAddHotKey(self->form, NEWT_KEY_PGDN); + newtFormAddHotKey(self->form, NEWT_KEY_HOME); + newtFormAddHotKey(self->form, NEWT_KEY_END); + newtFormAddComponent(self->form, self->sb); + return 0; +} + +int ui_browser__refresh(struct ui_browser *self) +{ + int row; + + newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); + row = self->refresh(self); + SLsmg_set_color(HE_COLORSET_NORMAL); + SLsmg_fill_region(self->top + row, self->left, + self->height - row, self->width, ' '); + + return 0; +} + +int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) +{ + if (ui_browser__refresh(self) < 0) + return -1; + + while (1) { + off_t offset; + + newtFormRun(self->form, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + if (is_exit_key(es->u.key)) + return es->u.key; + 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) { + ++self->first_visible_entry_idx; + self->seek(self, +1, SEEK_CUR); + } + break; + case NEWT_KEY_UP: + if (self->index == 0) + break; + --self->index; + if (self->index < self->first_visible_entry_idx) { + --self->first_visible_entry_idx; + self->seek(self, -1, SEEK_CUR); + } + break; + case NEWT_KEY_PGDN: + case ' ': + if (self->first_visible_entry_idx + self->height > self->nr_entries - 1) + break; + + offset = self->height; + if (self->index + offset > self->nr_entries - 1) + offset = self->nr_entries - 1 - self->index; + self->index += offset; + self->first_visible_entry_idx += offset; + self->seek(self, +offset, SEEK_CUR); + 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; + self->seek(self, -offset, SEEK_CUR); + break; + case NEWT_KEY_HOME: + ui_browser__reset_index(self); + break; + case NEWT_KEY_END: + offset = self->height - 1; + if (offset >= self->nr_entries) + offset = self->nr_entries - 1; + + self->index = self->nr_entries - 1; + self->first_visible_entry_idx = self->index - offset; + self->seek(self, -offset, SEEK_END); + break; + default: + return es->u.key; + } + if (ui_browser__refresh(self) < 0) + return -1; + } + return 0; +} + +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->first_visible_entry == NULL || + self->first_visible_entry == self->entries) + self->first_visible_entry = head->next; + + pos = self->first_visible_entry; + + list_for_each_from(pos, head) { + SLsmg_gotorc(self->top + row, self->left); + self->write(self, pos, row); + if (++row == self->height) + break; + } + + return row; +} + +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 ui_browser__init(void) +{ + struct newtPercentTreeColors *c = &defaultPercentTreeColors; + + 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); +} diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h new file mode 100644 index 0000000..bcc4391 --- /dev/null +++ b/tools/perf/util/ui/browser.h @@ -0,0 +1,43 @@ +#ifndef _PERF_UI_BROWSER_H_ +#define _PERF_UI_BROWSER_H_ 1 + +#include +#include +#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 + +struct ui_browser { + newtComponent form, sb; + u64 index, first_visible_entry_idx; + void *first_visible_entry, *entries; + u16 top, left, width, height; + void *priv; + 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); + u32 nr_entries; +}; + + +int ui_browser__percent_color(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); + +int ui_browser__show(struct ui_browser *self, const char *title); +int ui_browser__refresh(struct ui_browser *self); +int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es); + +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_ */ -- cgit v0.10.2 From 1c250d709fdc8aa5bf42d90be99428a01a256a55 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Thu, 5 Aug 2010 19:09:17 +0400 Subject: perf, x86: P4 PMU -- update nmi irq statistics and unmask lvt entry properly In case if last active performance counter is not overflowed at moment of NMI being triggered by another counter, the irq statistics may miss an update stage. As a more serious consequence -- apic quirk may not be triggered so apic lvt entry stay masked. Tested-by: Lin Ming Signed-off-by: Cyrill Gorcunov Cc: Stephane Eranian Cc: Peter Zijlstra Cc: Frederic Weisbecker LKML-Reference: <20100805150917.GA6311@lenovo> Signed-off-by: Ingo Molnar diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index 107711b..febb12c 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -656,6 +656,7 @@ static int p4_pmu_handle_irq(struct pt_regs *regs) cpuc = &__get_cpu_var(cpu_hw_events); for (idx = 0; idx < x86_pmu.num_counters; idx++) { + int overflow; if (!test_bit(idx, cpuc->active_mask)) continue; @@ -666,12 +667,14 @@ static int p4_pmu_handle_irq(struct pt_regs *regs) WARN_ON_ONCE(hwc->idx != idx); /* it might be unflagged overflow */ - handled = p4_pmu_clear_cccr_ovf(hwc); + overflow = p4_pmu_clear_cccr_ovf(hwc); val = x86_perf_event_update(event); - if (!handled && (val & (1ULL << (x86_pmu.cntval_bits - 1)))) + if (!overflow && (val & (1ULL << (x86_pmu.cntval_bits - 1)))) continue; + handled += overflow; + /* event overflow for sure */ data.period = event->hw.last_period; @@ -687,7 +690,7 @@ static int p4_pmu_handle_irq(struct pt_regs *regs) inc_irq_stat(apic_perf_irqs); } - return handled; + return handled > 0; } /* -- cgit v0.10.2 From d247eb6b924bbc2f13748c89b6c72c7a3d46645c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 7 Aug 2010 13:56:04 -0300 Subject: perf ui: Shorten ui_browser member names LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 266a9e0..9768be3 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -450,8 +450,8 @@ static int map_browser__search(struct map_browser *self) if (sym != NULL) { u32 *idx = symbol__browser_index(sym); - self->b.first_visible_entry = &sym->rb_node; - self->b.index = self->b.first_visible_entry_idx = *idx; + self->b.top = &sym->rb_node; + self->b.index = self->b.top_idx = *idx; } else ui_helpline__fpush("%s not found!", target); @@ -967,7 +967,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, } SLsmg_set_color(color); - SLsmg_gotorc(self->b.top + row, self->b.left); + SLsmg_gotorc(self->b.y + row, self->b.x); slsmg_write_nstring(" ", offset + extra_offset); slsmg_printf("%c ", folded_sign); slsmg_write_nstring(str, width); @@ -1030,7 +1030,7 @@ static int hist_browser__show_callchain_node(struct hist_browser *self, } s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - SLsmg_gotorc(self->b.top + row, self->b.left); + SLsmg_gotorc(self->b.y + row, self->b.x); SLsmg_set_color(color); slsmg_write_nstring(" ", offset); slsmg_printf("%c ", folded_sign); @@ -1110,7 +1110,7 @@ static int hist_browser__show_entry(struct hist_browser *self, } SLsmg_set_color(color); - SLsmg_gotorc(self->b.top + row, self->b.left); + SLsmg_gotorc(self->b.y + row, self->b.x); if (symbol_conf.use_callchain) { slsmg_printf("%c ", folded_sign); width -= 2; @@ -1138,10 +1138,10 @@ static unsigned int hist_browser__refresh(struct ui_browser *self) struct rb_node *nd; struct hist_browser *hb = container_of(self, struct hist_browser, b); - if (self->first_visible_entry == NULL) - self->first_visible_entry = rb_first(&hb->hists->entries); + if (self->top == NULL) + self->top = rb_first(&hb->hists->entries); - for (nd = self->first_visible_entry; nd; nd = rb_next(nd)) { + for (nd = self->top; nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); if (h->filtered) @@ -1244,7 +1244,7 @@ static void ui_browser__hists_seek(struct ui_browser *self, nd = hists__filter_entries(rb_first(self->entries)); break; case SEEK_CUR: - nd = self->first_visible_entry; + nd = self->top; goto do_offset; case SEEK_END: nd = hists__filter_prev_entries(rb_last(self->entries)); @@ -1258,7 +1258,7 @@ static void ui_browser__hists_seek(struct ui_browser *self, * Moves not relative to the first visible entry invalidates its * row_offset: */ - h = rb_entry(self->first_visible_entry, struct hist_entry, rb_node); + h = rb_entry(self->top, struct hist_entry, rb_node); h->row_offset = 0; /* @@ -1286,7 +1286,7 @@ do_offset: } else { h->row_offset += offset; offset = 0; - self->first_visible_entry = nd; + self->top = nd; break; } } @@ -1294,7 +1294,7 @@ do_offset: if (nd == NULL) break; --offset; - self->first_visible_entry = nd; + self->top = nd; } while (offset != 0); } else if (offset < 0) { while (1) { @@ -1307,7 +1307,7 @@ do_offset: } else { h->row_offset += offset; offset = 0; - self->first_visible_entry = nd; + self->top = nd; break; } } else { @@ -1317,7 +1317,7 @@ do_offset: } else { h->row_offset = h->nr_rows + offset; offset = 0; - self->first_visible_entry = nd; + self->top = nd; break; } } @@ -1327,7 +1327,7 @@ do_offset: if (nd == NULL) break; ++offset; - self->first_visible_entry = nd; + self->top = nd; if (offset == 0) { /* * Last unfiltered hist_entry, check if it is @@ -1342,7 +1342,7 @@ do_offset: first = false; } } else { - self->first_visible_entry = nd; + self->top = nd; h = rb_entry(nd, struct hist_entry, rb_node); h->row_offset = 0; } @@ -1463,7 +1463,7 @@ static int hist_browser__run(struct hist_browser *self, const char *title, switch (es->u.key) { case 'd': { /* Debug */ static int seq; - struct hist_entry *h = rb_entry(self->b.first_visible_entry, + 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", @@ -1471,7 +1471,7 @@ static int hist_browser__run(struct hist_browser *self, const char *title, self->hists->nr_entries, self->b.height, self->b.index, - self->b.first_visible_entry_idx, + self->b.top_idx, h->row_offset, h->nr_rows); } continue; diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index 0b2b930..edbb7dd 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c @@ -49,7 +49,7 @@ void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whenc pos = head->next; break; case SEEK_CUR: - pos = self->first_visible_entry; + pos = self->top; break; case SEEK_END: pos = head->prev; @@ -66,7 +66,7 @@ void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whenc pos = pos->prev; } - self->first_visible_entry = pos; + self->top = pos; } void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence) @@ -79,7 +79,7 @@ void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence) nd = rb_first(root); break; case SEEK_CUR: - nd = self->first_visible_entry; + nd = self->top; break; case SEEK_END: nd = rb_last(root); @@ -96,7 +96,7 @@ void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence) nd = rb_prev(nd); } - self->first_visible_entry = nd; + self->top = nd; } unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self) @@ -104,13 +104,13 @@ unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self) struct rb_node *nd; int row = 0; - if (self->first_visible_entry == NULL) - self->first_visible_entry = rb_first(self->entries); + if (self->top == NULL) + self->top = rb_first(self->entries); - nd = self->first_visible_entry; + nd = self->top; while (nd != NULL) { - SLsmg_gotorc(self->top + row, self->left); + SLsmg_gotorc(self->y + row, self->x); self->write(self, nd, row); if (++row == self->height) break; @@ -122,7 +122,7 @@ unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self) bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) { - return (self->first_visible_entry_idx + row) == self->index; + return self->top_idx + row == self->index; } void ui_browser__refresh_dimensions(struct ui_browser *self) @@ -135,13 +135,13 @@ void ui_browser__refresh_dimensions(struct ui_browser *self) self->height = rows - 5; if (self->height > self->nr_entries) self->height = self->nr_entries; - self->top = (rows - self->height) / 2; - self->left = (cols - self->width) / 2; + self->y = (rows - self->height) / 2; + self->x = (cols - self->width) / 2; } void ui_browser__reset_index(struct ui_browser *self) { - self->index = self->first_visible_entry_idx = 0; + self->index = self->top_idx = 0; self->seek(self, 0, SEEK_SET); } @@ -180,7 +180,7 @@ int ui_browser__refresh(struct ui_browser *self) newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); row = self->refresh(self); SLsmg_set_color(HE_COLORSET_NORMAL); - SLsmg_fill_region(self->top + row, self->left, + SLsmg_fill_region(self->y + row, self->x, self->height - row, self->width, ' '); return 0; @@ -205,8 +205,8 @@ int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) if (self->index == self->nr_entries - 1) break; ++self->index; - if (self->index == self->first_visible_entry_idx + self->height) { - ++self->first_visible_entry_idx; + if (self->index == self->top_idx + self->height) { + ++self->top_idx; self->seek(self, +1, SEEK_CUR); } break; @@ -214,34 +214,34 @@ int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) if (self->index == 0) break; --self->index; - if (self->index < self->first_visible_entry_idx) { - --self->first_visible_entry_idx; + if (self->index < self->top_idx) { + --self->top_idx; self->seek(self, -1, SEEK_CUR); } break; case NEWT_KEY_PGDN: case ' ': - if (self->first_visible_entry_idx + self->height > self->nr_entries - 1) + if (self->top_idx + self->height > self->nr_entries - 1) break; offset = self->height; if (self->index + offset > self->nr_entries - 1) offset = self->nr_entries - 1 - self->index; self->index += offset; - self->first_visible_entry_idx += offset; + self->top_idx += offset; self->seek(self, +offset, SEEK_CUR); break; case NEWT_KEY_PGUP: - if (self->first_visible_entry_idx == 0) + if (self->top_idx == 0) break; - if (self->first_visible_entry_idx < self->height) - offset = self->first_visible_entry_idx; + if (self->top_idx < self->height) + offset = self->top_idx; else offset = self->height; self->index -= offset; - self->first_visible_entry_idx -= offset; + self->top_idx -= offset; self->seek(self, -offset, SEEK_CUR); break; case NEWT_KEY_HOME: @@ -253,7 +253,7 @@ int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) offset = self->nr_entries - 1; self->index = self->nr_entries - 1; - self->first_visible_entry_idx = self->index - offset; + self->top_idx = self->index - offset; self->seek(self, -offset, SEEK_END); break; default: @@ -271,14 +271,13 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *self) struct list_head *head = self->entries; int row = 0; - if (self->first_visible_entry == NULL || - self->first_visible_entry == self->entries) - self->first_visible_entry = head->next; + if (self->top == NULL || self->top == self->entries) + self->top = head->next; - pos = self->first_visible_entry; + pos = self->top; list_for_each_from(pos, head) { - SLsmg_gotorc(self->top + row, self->left); + SLsmg_gotorc(self->y + row, self->x); self->write(self, pos, row); if (++row == self->height) break; diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index bcc4391..8eed24c 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h @@ -13,9 +13,9 @@ struct ui_browser { newtComponent form, sb; - u64 index, first_visible_entry_idx; - void *first_visible_entry, *entries; - u16 top, left, width, height; + u64 index, top_idx; + void *top, *entries; + u16 y, x, width, height; void *priv; unsigned int (*refresh)(struct ui_browser *self); void (*write)(struct ui_browser *self, void *entry, int row); -- cgit v0.10.2 From 5575536fc7ad7577a4e687a13e2f49acebc519f3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 8 Aug 2010 19:48:31 -0300 Subject: perf ui: Move ui_helpline routines to separate file in util/ui/ LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index d5bce76..d77a101 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -569,7 +569,9 @@ else EXTLIBS += -lnewt -lslang LIB_OBJS += $(OUTPUT)util/newt.o LIB_OBJS += $(OUTPUT)util/ui/browser.o + LIB_OBJS += $(OUTPUT)util/ui/helpline.o LIB_H += util/ui/browser.h + LIB_H += util/ui/helpline.h endif endif diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 9768be3..23f3b7d 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -24,6 +24,7 @@ #include "sort.h" #include "symbol.h" #include "ui/browser.h" +#include "ui/helpline.h" #if SLANG_VERSION < 20104 #define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args) @@ -94,43 +95,6 @@ void ui_progress__delete(struct ui_progress *self) free(self); } -static void ui_helpline__pop(void) -{ - newtPopHelpLine(); -} - -static void ui_helpline__push(const char *msg) -{ - newtPushHelpLine(msg); -} - -static 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); - } -} - -static void ui_helpline__fpush(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - ui_helpline__vpush(fmt, ap); - va_end(ap); -} - -static void ui_helpline__puts(const char *msg) -{ - ui_helpline__pop(); - ui_helpline__push(msg); -} - static int ui_entry__read(const char *title, char *bf, size_t size, int width) { struct newtExitStruct es; diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c new file mode 100644 index 0000000..6a11e130 --- /dev/null +++ b/tools/perf/util/ui/helpline.c @@ -0,0 +1,43 @@ +#define _GNU_SOURCE +#include +#include +#include + +#include "helpline.h" + +void ui_helpline__pop(void) +{ + newtPopHelpLine(); +} + +void ui_helpline__push(const char *msg) +{ + newtPushHelpLine(msg); +} + +static 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); +} diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h new file mode 100644 index 0000000..56d8c1d --- /dev/null +++ b/tools/perf/util/ui/helpline.h @@ -0,0 +1,9 @@ +#ifndef _PERF_UI_HELPLINE_H_ +#define _PERF_UI_HELPLINE_H_ 1 + +void ui_helpline__pop(void); +void ui_helpline__push(const char *msg); +void ui_helpline__fpush(const char *fmt, ...); +void ui_helpline__puts(const char *msg); + +#endif /* _PERF_UI_HELPLINE_H_ */ -- cgit v0.10.2 From 34cea7f7c0620c964676eece258ef431a6608bce Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 8 Aug 2010 19:56:47 -0300 Subject: perf ui: Move ui_progress routines to separate file in util/ui/ LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index d77a101..528c914 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -570,8 +570,10 @@ else LIB_OBJS += $(OUTPUT)util/newt.o LIB_OBJS += $(OUTPUT)util/ui/browser.o LIB_OBJS += $(OUTPUT)util/ui/helpline.o + LIB_OBJS += $(OUTPUT)util/ui/progress.o LIB_H += util/ui/browser.h LIB_H += util/ui/helpline.h + LIB_H += util/ui/progress.h endif endif diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 047ac33..a929b06 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -31,9 +31,7 @@ static inline void ui_progress__update(struct ui_progress *self __used, static inline void ui_progress__delete(struct ui_progress *self __used) {} #else int browser__show_help(const char *format, va_list ap); -struct ui_progress *ui_progress__new(const char *title, u64 total); -void ui_progress__update(struct ui_progress *self, u64 curr); -void ui_progress__delete(struct ui_progress *self); +#include "ui/progress.h" #endif #endif /* __PERF_DEBUG_H */ diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 23f3b7d..c0986d3 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -39,62 +39,6 @@ newtComponent newt_form__new(void); -struct ui_progress { - newtComponent form, scale; -}; - -struct ui_progress *ui_progress__new(const char *title, u64 total) -{ - struct ui_progress *self = malloc(sizeof(*self)); - - if (self != NULL) { - int cols; - - if (use_browser <= 0) - return self; - newtGetScreenSize(&cols, NULL); - cols -= 4; - newtCenteredWindow(cols, 1, title); - self->form = newtForm(NULL, NULL, 0); - if (self->form == NULL) - goto out_free_self; - self->scale = newtScale(0, 0, cols, total); - if (self->scale == NULL) - goto out_free_form; - newtFormAddComponent(self->form, self->scale); - newtRefresh(); - } - - return self; - -out_free_form: - newtFormDestroy(self->form); -out_free_self: - free(self); - return NULL; -} - -void ui_progress__update(struct ui_progress *self, u64 curr) -{ - /* - * FIXME: We should have a per UI backend way of showing progress, - * stdio will just show a percentage as NN%, etc. - */ - if (use_browser <= 0) - return; - newtScaleSet(self->scale, curr); - newtRefresh(); -} - -void ui_progress__delete(struct ui_progress *self) -{ - if (use_browser > 0) { - newtFormDestroy(self->form); - newtPopWindow(); - } - free(self); -} - static int ui_entry__read(const char *title, char *bf, size_t size, int width) { struct newtExitStruct es; diff --git a/tools/perf/util/ui/progress.c b/tools/perf/util/ui/progress.c new file mode 100644 index 0000000..d7fc399 --- /dev/null +++ b/tools/perf/util/ui/progress.c @@ -0,0 +1,60 @@ +#include +#include +#include "../cache.h" +#include "progress.h" + +struct ui_progress { + newtComponent form, scale; +}; + +struct ui_progress *ui_progress__new(const char *title, u64 total) +{ + struct ui_progress *self = malloc(sizeof(*self)); + + if (self != NULL) { + int cols; + + if (use_browser <= 0) + return self; + newtGetScreenSize(&cols, NULL); + cols -= 4; + newtCenteredWindow(cols, 1, title); + self->form = newtForm(NULL, NULL, 0); + if (self->form == NULL) + goto out_free_self; + self->scale = newtScale(0, 0, cols, total); + if (self->scale == NULL) + goto out_free_form; + newtFormAddComponent(self->form, self->scale); + newtRefresh(); + } + + return self; + +out_free_form: + newtFormDestroy(self->form); +out_free_self: + free(self); + return NULL; +} + +void ui_progress__update(struct ui_progress *self, u64 curr) +{ + /* + * FIXME: We should have a per UI backend way of showing progress, + * stdio will just show a percentage as NN%, etc. + */ + if (use_browser <= 0) + return; + newtScaleSet(self->scale, curr); + newtRefresh(); +} + +void ui_progress__delete(struct ui_progress *self) +{ + if (use_browser > 0) { + newtFormDestroy(self->form); + newtPopWindow(); + } + free(self); +} diff --git a/tools/perf/util/ui/progress.h b/tools/perf/util/ui/progress.h new file mode 100644 index 0000000..a3820a0 --- /dev/null +++ b/tools/perf/util/ui/progress.h @@ -0,0 +1,11 @@ +#ifndef _PERF_UI_PROGRESS_H_ +#define _PERF_UI_PROGRESS_H_ 1 + +struct ui_progress; + +struct ui_progress *ui_progress__new(const char *title, u64 total); +void ui_progress__delete(struct ui_progress *self); + +void ui_progress__update(struct ui_progress *self, u64 curr); + +#endif -- cgit v0.10.2 From 211ef12771e759a08e10c3c606e6a8b1663519e7 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Aug 2010 14:54:09 -0300 Subject: perf ui: Move annotate browser to util/ui/browsers/ LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 528c914..ae443b6 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -158,7 +158,7 @@ all:: # Define NO_DWARF if you do not want debug-info analysis feature at all. $(shell sh -c 'mkdir -p $(OUTPUT)scripts/{perl,python}/Perf-Trace-Util/' 2> /dev/null) -$(shell sh -c 'mkdir -p $(OUTPUT)util/{ui,scripting-engines}/' 2> /dev/null) +$(shell sh -c 'mkdir -p $(OUTPUT)util/{ui/browsers,scripting-engines}/' 2> /dev/null) $(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null) $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE @@ -569,10 +569,12 @@ else EXTLIBS += -lnewt -lslang LIB_OBJS += $(OUTPUT)util/newt.o LIB_OBJS += $(OUTPUT)util/ui/browser.o + LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o LIB_OBJS += $(OUTPUT)util/ui/helpline.o LIB_OBJS += $(OUTPUT)util/ui/progress.o LIB_H += util/ui/browser.h LIB_H += util/ui/helpline.h + LIB_H += util/ui/libslang.h LIB_H += util/ui/progress.h endif endif @@ -977,6 +979,9 @@ $(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< +$(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< + $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index a929b06..ba4892e 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -30,6 +30,7 @@ static inline void ui_progress__update(struct ui_progress *self __used, static inline void ui_progress__delete(struct ui_progress *self __used) {} #else +extern char browser__last_msg[]; int browser__show_help(const char *format, va_list ap); #include "ui/progress.h" #endif diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index c0986d3..f188258 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -1,16 +1,7 @@ #define _GNU_SOURCE #include #undef _GNU_SOURCE -/* - * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks - * the build if it isn't defined. Use the equivalent one that glibc - * has on features.h. - */ -#include -#ifndef HAVE_LONG_LONG -#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG -#endif -#include +#include "ui/libslang.h" #include #include #include @@ -26,17 +17,6 @@ #include "ui/browser.h" #include "ui/helpline.h" -#if SLANG_VERSION < 20104 -#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args) -#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len) -#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\ - (char *)fg, (char *)bg) -#else -#define slsmg_printf SLsmg_printf -#define slsmg_write_nstring SLsmg_write_nstring -#define sltt_set_color SLtt_set_color -#endif - newtComponent newt_form__new(void); static int ui_entry__read(const char *title, char *bf, size_t size, int width) @@ -72,7 +52,7 @@ out_free_form: return 0; } -static char browser__last_msg[1024]; +char browser__last_msg[1024]; int browser__show_help(const char *format, va_list ap) { @@ -192,66 +172,6 @@ static bool dialog_yesno(const char *msg) return newtWinChoice(NULL, yes, no, (char *)msg) == 1; } -static void ui__error_window(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap); - va_end(ap); -} - -static void annotate_browser__write(struct ui_browser *self, void *entry, int row) -{ - struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); - bool current_entry = ui_browser__is_current_entry(self, row); - int width = self->width; - - if (ol->offset != -1) { - struct hist_entry *he = self->priv; - struct symbol *sym = he->ms.sym; - int len = he->ms.sym->end - he->ms.sym->start; - unsigned int hits = 0; - double percent = 0.0; - int color; - struct sym_priv *priv = symbol__priv(sym); - struct sym_ext *sym_ext = priv->ext; - struct sym_hist *h = priv->hist; - s64 offset = ol->offset; - struct objdump_line *next = objdump__get_next_ip_line(self->entries, ol); - - while (offset < (s64)len && - (next == NULL || offset < next->offset)) { - if (sym_ext) { - percent += sym_ext[offset].percent; - } 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 (!*ol->line) - slsmg_write_nstring(" ", width - 18); - else - slsmg_write_nstring(ol->line, width - 18); -} - static char *callchain_list__sym_name(struct callchain_list *self, char *bf, size_t bfsize) { @@ -262,54 +182,6 @@ static char *callchain_list__sym_name(struct callchain_list *self, return bf; } -int hist_entry__tui_annotate(struct hist_entry *self) -{ - struct newtExitStruct es; - struct objdump_line *pos, *n; - LIST_HEAD(head); - struct ui_browser browser = { - .entries = &head, - .refresh = ui_browser__list_head_refresh, - .seek = ui_browser__list_head_seek, - .write = annotate_browser__write, - .priv = self, - }; - int ret; - - if (self->ms.sym == NULL) - return -1; - - if (self->ms.map->dso->annotate_warned) - return -1; - - if (hist_entry__annotate(self, &head) < 0) { - ui__error_window(browser__last_msg); - return -1; - } - - ui_helpline__push("Press <- or ESC to exit"); - - 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; - } - - browser.width += 18; /* Percentage */ - ui_browser__show(&browser, self->ms.sym->name); - newtFormAddHotKey(browser.form, ' '); - ret = ui_browser__run(&browser, &es); - newtFormDestroy(browser.form); - newtPopWindow(); - list_for_each_entry_safe(pos, n, &head, node) { - list_del(&pos->node); - objdump_line__free(pos); - } - ui_helpline__pop(); - return ret; -} - /* -------------------------------------------------------------------- */ struct map_browser { diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index 8eed24c..856e343 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h @@ -3,6 +3,7 @@ #include #include +#include #include "../types.h" #define HE_COLORSET_TOP 50 diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c new file mode 100644 index 0000000..783d277 --- /dev/null +++ b/tools/perf/util/ui/browsers/annotate.c @@ -0,0 +1,114 @@ +#include "../browser.h" +#include "../helpline.h" +#include "../libslang.h" +#include "../../hist.h" +#include "../../sort.h" +#include "../../symbol.h" + +static void ui__error_window(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap); + va_end(ap); +} + +static void annotate_browser__write(struct ui_browser *self, void *entry, int row) +{ + struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); + bool current_entry = ui_browser__is_current_entry(self, row); + int width = self->width; + + if (ol->offset != -1) { + struct hist_entry *he = self->priv; + struct symbol *sym = he->ms.sym; + int len = he->ms.sym->end - he->ms.sym->start; + unsigned int hits = 0; + double percent = 0.0; + int color; + struct sym_priv *priv = symbol__priv(sym); + struct sym_ext *sym_ext = priv->ext; + struct sym_hist *h = priv->hist; + s64 offset = ol->offset; + struct objdump_line *next = objdump__get_next_ip_line(self->entries, ol); + + while (offset < (s64)len && + (next == NULL || offset < next->offset)) { + if (sym_ext) { + percent += sym_ext[offset].percent; + } 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 (!*ol->line) + slsmg_write_nstring(" ", width - 18); + else + slsmg_write_nstring(ol->line, width - 18); +} + +int hist_entry__tui_annotate(struct hist_entry *self) +{ + struct newtExitStruct es; + struct objdump_line *pos, *n; + LIST_HEAD(head); + struct ui_browser browser = { + .entries = &head, + .refresh = ui_browser__list_head_refresh, + .seek = ui_browser__list_head_seek, + .write = annotate_browser__write, + .priv = self, + }; + int ret; + + if (self->ms.sym == NULL) + return -1; + + if (self->ms.map->dso->annotate_warned) + return -1; + + if (hist_entry__annotate(self, &head) < 0) { + ui__error_window(browser__last_msg); + return -1; + } + + ui_helpline__push("Press <- or ESC to exit"); + + 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; + } + + browser.width += 18; /* Percentage */ + ui_browser__show(&browser, self->ms.sym->name); + newtFormAddHotKey(browser.form, ' '); + ret = ui_browser__run(&browser, &es); + newtFormDestroy(browser.form); + newtPopWindow(); + list_for_each_entry_safe(pos, n, &head, node) { + list_del(&pos->node); + objdump_line__free(pos); + } + ui_helpline__pop(); + return ret; +} diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h new file mode 100644 index 0000000..5623da8 --- /dev/null +++ b/tools/perf/util/ui/libslang.h @@ -0,0 +1,27 @@ +#ifndef _PERF_UI_SLANG_H_ +#define _PERF_UI_SLANG_H_ 1 +/* + * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks + * the build if it isn't defined. Use the equivalent one that glibc + * has on features.h. + */ +#include +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG +#endif +#include + +#if SLANG_VERSION < 20104 +#define slsmg_printf(msg, args...) \ + SLsmg_printf((char *)msg, ##args) +#define slsmg_write_nstring(msg, len) \ + SLsmg_write_nstring((char *)msg, len) +#define sltt_set_color(obj, name, fg, bg) \ + SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg) +#else +#define slsmg_printf SLsmg_printf +#define slsmg_write_nstring SLsmg_write_nstring +#define sltt_set_color SLtt_set_color +#endif + +#endif /* _PERF_UI_SLANG_H_ */ -- cgit v0.10.2 From b1b0267336b1b74eeb8884bac4be96296b719e67 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Aug 2010 15:37:34 -0300 Subject: perf ui: Move map browser to util/ui/browsers/ LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index ae443b6..d118020 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -570,9 +570,11 @@ else LIB_OBJS += $(OUTPUT)util/newt.o LIB_OBJS += $(OUTPUT)util/ui/browser.o LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o + LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o LIB_OBJS += $(OUTPUT)util/ui/helpline.o LIB_OBJS += $(OUTPUT)util/ui/progress.o LIB_H += util/ui/browser.h + LIB_H += util/ui/browsers/map.h LIB_H += util/ui/helpline.h LIB_H += util/ui/libslang.h LIB_H += util/ui/progress.h @@ -982,6 +984,9 @@ $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< +$(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< + $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index f188258..b596926 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -16,42 +16,10 @@ #include "symbol.h" #include "ui/browser.h" #include "ui/helpline.h" +#include "ui/browsers/map.h" newtComponent newt_form__new(void); -static int ui_entry__read(const char *title, char *bf, size_t size, int width) -{ - struct newtExitStruct es; - newtComponent form, entry; - const char *result; - int err = -1; - - newtCenteredWindow(width, 1, title); - form = newtForm(NULL, NULL, 0); - if (form == NULL) - return -1; - - entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL); - if (entry == NULL) - goto out_free_form; - - newtFormAddComponent(form, entry); - newtFormAddHotKey(form, NEWT_KEY_ENTER); - newtFormAddHotKey(form, NEWT_KEY_ESCAPE); - newtFormAddHotKey(form, NEWT_KEY_LEFT); - newtFormAddHotKey(form, CTRL('c')); - newtFormRun(form, &es); - - if (result != NULL) { - strncpy(bf, result, size); - err = 0; - } -out_free_form: - newtPopWindow(); - newtFormDestroy(form); - return 0; -} - char browser__last_msg[1024]; int browser__show_help(const char *format, va_list ap) @@ -182,128 +150,6 @@ static char *callchain_list__sym_name(struct callchain_list *self, return bf; } -/* -------------------------------------------------------------------- */ - -struct map_browser { - struct ui_browser b; - struct map *map; - u16 namelen; - u8 addrlen; -}; - -static void map_browser__write(struct ui_browser *self, void *nd, int row) -{ - struct symbol *sym = rb_entry(nd, struct symbol, rb_node); - struct map_browser *mb = container_of(self, struct map_browser, b); - bool current_entry = ui_browser__is_current_entry(self, row); - int color = ui_browser__percent_color(0, current_entry); - - SLsmg_set_color(color); - slsmg_printf("%*llx %*llx %c ", - mb->addrlen, sym->start, mb->addrlen, sym->end, - sym->binding == STB_GLOBAL ? 'g' : - sym->binding == STB_LOCAL ? 'l' : 'w'); - slsmg_write_nstring(sym->name, mb->namelen); -} - -/* FIXME uber-kludgy, see comment on cmd_report... */ -static u32 *symbol__browser_index(struct symbol *self) -{ - return ((void *)self) - sizeof(struct rb_node) - sizeof(u32); -} - -static int map_browser__search(struct map_browser *self) -{ - char target[512]; - struct symbol *sym; - int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40); - - if (err) - return err; - - if (target[0] == '0' && tolower(target[1]) == 'x') { - u64 addr = strtoull(target, NULL, 16); - sym = map__find_symbol(self->map, addr, NULL); - } else - sym = map__find_symbol_by_name(self->map, target, NULL); - - if (sym != NULL) { - u32 *idx = symbol__browser_index(sym); - - self->b.top = &sym->rb_node; - self->b.index = self->b.top_idx = *idx; - } else - ui_helpline__fpush("%s not found!", target); - - return 0; -} - -static int map_browser__run(struct map_browser *self, struct newtExitStruct *es) -{ - if (ui_browser__show(&self->b, self->map->dso->long_name) < 0) - return -1; - - ui_helpline__fpush("Press <- or ESC to exit, %s / to search", - verbose ? "" : "restart with -v to use"); - newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); - newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); - if (verbose) - newtFormAddHotKey(self->b.form, '/'); - - while (1) { - ui_browser__run(&self->b, es); - - if (es->reason != NEWT_EXIT_HOTKEY) - break; - if (verbose && es->u.key == '/') - map_browser__search(self); - else - break; - } - - newtFormDestroy(self->b.form); - newtPopWindow(); - ui_helpline__pop(); - return 0; -} - -static int map__browse(struct map *self) -{ - struct map_browser mb = { - .b = { - .entries = &self->dso->symbols[self->type], - .refresh = ui_browser__rb_tree_refresh, - .seek = ui_browser__rb_tree_seek, - .write = map_browser__write, - }, - .map = self, - }; - struct newtExitStruct es; - struct rb_node *nd; - char tmp[BITS_PER_LONG / 4]; - u64 maxaddr = 0; - - for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) { - struct symbol *pos = rb_entry(nd, struct symbol, rb_node); - - if (mb.namelen < pos->namelen) - mb.namelen = pos->namelen; - if (maxaddr < pos->end) - maxaddr = pos->end; - if (verbose) { - u32 *idx = symbol__browser_index(pos); - *idx = mb.b.nr_entries; - } - ++mb.b.nr_entries; - } - - mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr); - mb.b.width += mb.addrlen * 2 + 4 + mb.namelen; - return map_browser__run(&mb, &es); -} - -/* -------------------------------------------------------------------- */ - struct hist_browser { struct ui_browser b; struct hists *hists; diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c new file mode 100644 index 0000000..b79f0c9 --- /dev/null +++ b/tools/perf/util/ui/browsers/map.c @@ -0,0 +1,163 @@ +#include "../libslang.h" +#include +#include +#include +#include +#include +#include +#include "../../debug.h" +#include "../../symbol.h" +#include "../browser.h" +#include "../helpline.h" +#include "map.h" + +static int ui_entry__read(const char *title, char *bf, size_t size, int width) +{ + struct newtExitStruct es; + newtComponent form, entry; + const char *result; + int err = -1; + + newtCenteredWindow(width, 1, title); + form = newtForm(NULL, NULL, 0); + if (form == NULL) + return -1; + + entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL); + if (entry == NULL) + goto out_free_form; + + newtFormAddComponent(form, entry); + newtFormAddHotKey(form, NEWT_KEY_ENTER); + newtFormAddHotKey(form, NEWT_KEY_ESCAPE); + newtFormAddHotKey(form, NEWT_KEY_LEFT); + newtFormAddHotKey(form, CTRL('c')); + newtFormRun(form, &es); + + if (result != NULL) { + strncpy(bf, result, size); + err = 0; + } +out_free_form: + newtPopWindow(); + newtFormDestroy(form); + return 0; +} + +struct map_browser { + struct ui_browser b; + struct map *map; + u16 namelen; + u8 addrlen; +}; + +static void map_browser__write(struct ui_browser *self, void *nd, int row) +{ + struct symbol *sym = rb_entry(nd, struct symbol, rb_node); + struct map_browser *mb = container_of(self, struct map_browser, b); + bool current_entry = ui_browser__is_current_entry(self, row); + int color = ui_browser__percent_color(0, current_entry); + + SLsmg_set_color(color); + slsmg_printf("%*llx %*llx %c ", + mb->addrlen, sym->start, mb->addrlen, sym->end, + sym->binding == STB_GLOBAL ? 'g' : + sym->binding == STB_LOCAL ? 'l' : 'w'); + slsmg_write_nstring(sym->name, mb->namelen); +} + +/* FIXME uber-kludgy, see comment on cmd_report... */ +static u32 *symbol__browser_index(struct symbol *self) +{ + return ((void *)self) - sizeof(struct rb_node) - sizeof(u32); +} + +static int map_browser__search(struct map_browser *self) +{ + char target[512]; + struct symbol *sym; + int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40); + + if (err) + return err; + + if (target[0] == '0' && tolower(target[1]) == 'x') { + u64 addr = strtoull(target, NULL, 16); + sym = map__find_symbol(self->map, addr, NULL); + } else + sym = map__find_symbol_by_name(self->map, target, NULL); + + if (sym != NULL) { + u32 *idx = symbol__browser_index(sym); + + self->b.top = &sym->rb_node; + self->b.index = self->b.top_idx = *idx; + } else + ui_helpline__fpush("%s not found!", target); + + return 0; +} + +static int map_browser__run(struct map_browser *self, struct newtExitStruct *es) +{ + if (ui_browser__show(&self->b, self->map->dso->long_name) < 0) + return -1; + + ui_helpline__fpush("Press <- or ESC to exit, %s / to search", + verbose ? "" : "restart with -v to use"); + newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); + newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); + if (verbose) + newtFormAddHotKey(self->b.form, '/'); + + while (1) { + ui_browser__run(&self->b, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + if (verbose && es->u.key == '/') + map_browser__search(self); + else + break; + } + + newtFormDestroy(self->b.form); + newtPopWindow(); + ui_helpline__pop(); + return 0; +} + +int map__browse(struct map *self) +{ + struct map_browser mb = { + .b = { + .entries = &self->dso->symbols[self->type], + .refresh = ui_browser__rb_tree_refresh, + .seek = ui_browser__rb_tree_seek, + .write = map_browser__write, + }, + .map = self, + }; + struct newtExitStruct es; + struct rb_node *nd; + char tmp[BITS_PER_LONG / 4]; + u64 maxaddr = 0; + + for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) { + struct symbol *pos = rb_entry(nd, struct symbol, rb_node); + + if (mb.namelen < pos->namelen) + mb.namelen = pos->namelen; + if (maxaddr < pos->end) + maxaddr = pos->end; + if (verbose) { + u32 *idx = symbol__browser_index(pos); + *idx = mb.b.nr_entries; + } + ++mb.b.nr_entries; + } + + mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr); + mb.b.width += mb.addrlen * 2 + 4 + mb.namelen; + return map_browser__run(&mb, &es); +} diff --git a/tools/perf/util/ui/browsers/map.h b/tools/perf/util/ui/browsers/map.h new file mode 100644 index 0000000..df8581a --- /dev/null +++ b/tools/perf/util/ui/browsers/map.h @@ -0,0 +1,6 @@ +#ifndef _PERF_UI_MAP_BROWSER_H_ +#define _PERF_UI_MAP_BROWSER_H_ 1 +struct map; + +int map__browse(struct map *self); +#endif /* _PERF_UI_MAP_BROWSER_H_ */ -- cgit v0.10.2 From 696b97a5d2de9e2b22699300835e675dfffe8592 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Mon, 9 Aug 2010 12:21:18 +0100 Subject: perf symbols: Ignore mapping symbols on ARM ARM ELF files use symbols with special names $a, $t, $d to identify regions of ARM code, Thumb code and data within code sections. This can cause confusing output from the perf tools, especially for partially stripped binaries, or binaries containing user-added zero-sized symbols (which may occur in hand-written assembler which hasn't been fully annotated with .size directives). This patch filters out these symbols at load time. LKML-Reference: <1281352878-8735-2-git-send-email-dave.martin@linaro.org> Signed-off-by: Dave Martin Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index b6f5970..1a36773 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1079,6 +1079,16 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, if (!is_label && !elf_sym__is_a(&sym, map->type)) continue; + /* Reject ARM ELF "mapping symbols": these aren't unique and + * don't identify functions, so will confuse the profile + * output: */ + if (ehdr.e_machine == EM_ARM) { + if (!strcmp(elf_name, "$a") || + !strcmp(elf_name, "$d") || + !strcmp(elf_name, "$t")) + continue; + } + if (opdsec && sym.st_shndx == opdidx) { u32 offset = sym.st_value - opdshdr.sh_addr; u64 *opd = opddata->d_buf + offset; -- cgit v0.10.2 From d1b4f2491c3341c61c752049f73ba12553f978d8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Aug 2010 15:49:07 -0300 Subject: perf ui: Move hists browser to util/ui/browsers/ LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index d118020..62e4d6f 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -570,6 +570,7 @@ else LIB_OBJS += $(OUTPUT)util/newt.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 @@ -984,6 +985,9 @@ $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< +$(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< + $(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index b596926..6bccdaa 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -1,24 +1,19 @@ -#define _GNU_SOURCE -#include -#undef _GNU_SOURCE -#include "ui/libslang.h" -#include -#include -#include #include +#include +#include +#include +#include #include #include "cache.h" -#include "hist.h" -#include "pstack.h" -#include "session.h" -#include "sort.h" -#include "symbol.h" +#include "debug.h" #include "ui/browser.h" #include "ui/helpline.h" -#include "ui/browsers/map.h" newtComponent newt_form__new(void); +int popup_menu(int argc, char * const argv[]); +int ui__help_window(const char *text); +bool dialog_yesno(const char *msg); char browser__last_msg[1024]; @@ -57,7 +52,7 @@ newtComponent newt_form__new(void) return self; } -static int popup_menu(int argc, char * const argv[]) +int popup_menu(int argc, char * const argv[]) { struct newtExitStruct es; int i, rc = -1, max_len = 5; @@ -91,7 +86,7 @@ out_destroy_form: return rc; } -static int ui__help_window(const char *text) +int ui__help_window(const char *text) { struct newtExitStruct es; newtComponent tb, form = newt_form__new(); @@ -133,316 +128,13 @@ out_destroy_form: return rc; } -static bool dialog_yesno(const char *msg) +bool dialog_yesno(const char *msg) { /* newtWinChoice should really be accepting const char pointers... */ char yes[] = "Yes", no[] = "No"; return newtWinChoice(NULL, yes, no, (char *)msg) == 1; } -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, "%#Lx", self->ip); - return bf; -} - -struct hist_browser { - struct ui_browser b; - struct hists *hists; - struct hist_entry *he_selection; - struct map_symbol *selection; -}; - -static void hist_browser__reset(struct hist_browser *self); -static int hist_browser__run(struct hist_browser *self, const char *title, - struct newtExitStruct *es); -static unsigned int hist_browser__refresh(struct ui_browser *self); -static void ui_browser__hists_seek(struct ui_browser *self, - off_t offset, int whence); - -static struct hist_browser *hist_browser__new(struct hists *hists) -{ - struct hist_browser *self = zalloc(sizeof(*self)); - - if (self) { - self->hists = hists; - self->b.refresh = hist_browser__refresh; - self->b.seek = ui_browser__hists_seek; - } - - return self; -} - -static void hist_browser__delete(struct hist_browser *self) -{ - newtFormDestroy(self->b.form); - newtPopWindow(); - free(self); -} - -static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) -{ - return self->he_selection; -} - -static struct thread *hist_browser__selected_thread(struct hist_browser *self) -{ - return self->he_selection->thread; -} - -static int hist_browser__title(char *bf, size_t size, const char *ev_name, - const struct dso *dso, const struct thread *thread) -{ - int printed = 0; - - if (thread) - printed += snprintf(bf + printed, size - printed, - "Thread: %s(%d)", - (thread->comm_set ? thread->comm : ""), - thread->pid); - if (dso) - printed += snprintf(bf + printed, size - printed, - "%sDSO: %s", thread ? " " : "", - dso->short_name); - return printed ?: snprintf(bf, size, "Event: %s", ev_name); -} - -int hists__browse(struct hists *self, const char *helpline, const char *ev_name) -{ - struct hist_browser *browser = hist_browser__new(self); - struct pstack *fstack; - const struct thread *thread_filter = NULL; - const struct dso *dso_filter = NULL; - struct newtExitStruct es; - char msg[160]; - int key = -1; - - if (browser == NULL) - return -1; - - fstack = pstack__new(2); - if (fstack == NULL) - goto out; - - ui_helpline__push(helpline); - - hist_browser__title(msg, sizeof(msg), ev_name, - dso_filter, thread_filter); - - while (1) { - const struct thread *thread; - const struct dso *dso; - char *options[16]; - int nr_options = 0, choice = 0, i, - annotate = -2, zoom_dso = -2, zoom_thread = -2, - browse_map = -2; - - if (hist_browser__run(browser, msg, &es)) - break; - - thread = hist_browser__selected_thread(browser); - dso = browser->selection->map ? browser->selection->map->dso : NULL; - - if (es.reason == NEWT_EXIT_HOTKEY) { - key = es.u.key; - - switch (key) { - case NEWT_KEY_F1: - goto do_help; - case NEWT_KEY_TAB: - case NEWT_KEY_UNTAB: - /* - * Exit the browser, let hists__browser_tree - * go to the next or previous - */ - goto out_free_stack; - default:; - } - - key = toupper(key); - switch (key) { - case 'A': - if (browser->selection->map == NULL && - browser->selection->map->dso->annotate_warned) - continue; - goto do_annotate; - case 'D': - goto zoom_dso; - case 'T': - goto zoom_thread; - case 'H': - case '?': -do_help: - ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" - "<- Zoom out\n" - "a Annotate current symbol\n" - "h/?/F1 Show this window\n" - "d Zoom into current DSO\n" - "t Zoom into current Thread\n" - "q/CTRL+C Exit browser"); - continue; - default:; - } - if (is_exit_key(key)) { - if (key == NEWT_KEY_ESCAPE) { - if (dialog_yesno("Do you really want to exit?")) - break; - else - continue; - } else - break; - } - - if (es.u.key == NEWT_KEY_LEFT) { - const void *top; - - if (pstack__empty(fstack)) - continue; - top = pstack__pop(fstack); - if (top == &dso_filter) - goto zoom_out_dso; - if (top == &thread_filter) - goto zoom_out_thread; - continue; - } - } - - if (browser->selection->sym != NULL && - !browser->selection->map->dso->annotate_warned && - asprintf(&options[nr_options], "Annotate %s", - browser->selection->sym->name) > 0) - annotate = nr_options++; - - if (thread != NULL && - asprintf(&options[nr_options], "Zoom %s %s(%d) thread", - (thread_filter ? "out of" : "into"), - (thread->comm_set ? thread->comm : ""), - thread->pid) > 0) - zoom_thread = nr_options++; - - if (dso != NULL && - asprintf(&options[nr_options], "Zoom %s %s DSO", - (dso_filter ? "out of" : "into"), - (dso->kernel ? "the Kernel" : dso->short_name)) > 0) - zoom_dso = nr_options++; - - if (browser->selection->map != NULL && - asprintf(&options[nr_options], "Browse map details") > 0) - browse_map = nr_options++; - - options[nr_options++] = (char *)"Exit"; - - choice = popup_menu(nr_options, options); - - for (i = 0; i < nr_options - 1; ++i) - free(options[i]); - - if (choice == nr_options - 1) - break; - - if (choice == -1) - continue; - - if (choice == annotate) { - struct hist_entry *he; -do_annotate: - if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { - browser->selection->map->dso->annotate_warned = 1; - ui_helpline__puts("No vmlinux file found, can't " - "annotate with just a " - "kallsyms file"); - continue; - } - - he = hist_browser__selected_entry(browser); - if (he == NULL) - continue; - - hist_entry__tui_annotate(he); - } else if (choice == browse_map) - map__browse(browser->selection->map); - else if (choice == zoom_dso) { -zoom_dso: - if (dso_filter) { - pstack__remove(fstack, &dso_filter); -zoom_out_dso: - ui_helpline__pop(); - dso_filter = NULL; - } else { - if (dso == NULL) - continue; - ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", - dso->kernel ? "the Kernel" : dso->short_name); - dso_filter = dso; - pstack__push(fstack, &dso_filter); - } - hists__filter_by_dso(self, dso_filter); - hist_browser__title(msg, sizeof(msg), ev_name, - dso_filter, thread_filter); - hist_browser__reset(browser); - } else if (choice == zoom_thread) { -zoom_thread: - if (thread_filter) { - pstack__remove(fstack, &thread_filter); -zoom_out_thread: - ui_helpline__pop(); - thread_filter = NULL; - } else { - ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", - thread->comm_set ? thread->comm : "", - thread->pid); - thread_filter = thread; - pstack__push(fstack, &thread_filter); - } - hists__filter_by_thread(self, thread_filter); - hist_browser__title(msg, sizeof(msg), ev_name, - dso_filter, thread_filter); - hist_browser__reset(browser); - } - } -out_free_stack: - pstack__delete(fstack); -out: - hist_browser__delete(browser); - return key; -} - -int hists__tui_browse_tree(struct rb_root *self, const char *help) -{ - struct rb_node *first = rb_first(self), *nd = first, *next; - int key = 0; - - while (nd) { - struct hists *hists = rb_entry(nd, struct hists, rb_node); - const char *ev_name = __event_name(hists->type, hists->config); - - key = hists__browse(hists, help, ev_name); - - if (is_exit_key(key)) - break; - - switch (key) { - case NEWT_KEY_TAB: - next = rb_next(nd); - if (next) - nd = next; - break; - case NEWT_KEY_UNTAB: - if (nd == first) - continue; - nd = rb_prev(nd); - default: - break; - } - } - - return key; -} - static void newt_suspend(void *d __used) { newtSuspend(); @@ -476,638 +168,3 @@ void exit_browser(bool wait_for_ok) newtFinished(); } } - -static void hist_browser__refresh_dimensions(struct hist_browser *self) -{ - /* 3 == +/- toggle symbol before actual hist_entry rendering */ - self->b.width = 3 + (hists__sort_list_width(self->hists) + - sizeof("[k]")); -} - -static void hist_browser__reset(struct hist_browser *self) -{ - self->b.nr_entries = self->hists->nr_entries; - hist_browser__refresh_dimensions(self); - ui_browser__reset_index(&self->b); -} - -static char tree__folded_sign(bool unfolded) -{ - return unfolded ? '-' : '+'; -} - -static char map_symbol__folded(const struct map_symbol *self) -{ - return self->has_children ? tree__folded_sign(self->unfolded) : ' '; -} - -static char hist_entry__folded(const struct hist_entry *self) -{ - return map_symbol__folded(&self->ms); -} - -static char callchain_list__folded(const struct callchain_list *self) -{ - return map_symbol__folded(&self->ms); -} - -static bool map_symbol__toggle_fold(struct map_symbol *self) -{ - if (!self->has_children) - return false; - - self->unfolded = !self->unfolded; - return true; -} - -#define LEVEL_OFFSET_STEP 3 - -static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, - struct callchain_node *chain_node, - u64 total, int level, - unsigned short row, - off_t *row_offset, - bool *is_current_entry) -{ - struct rb_node *node; - int first_row = row, width, offset = level * LEVEL_OFFSET_STEP; - u64 new_total, remaining; - - if (callchain_param.mode == CHAIN_GRAPH_REL) - new_total = chain_node->children_hit; - else - new_total = total; - - remaining = new_total; - node = rb_first(&chain_node->rb_root); - while (node) { - struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); - struct rb_node *next = rb_next(node); - u64 cumul = cumul_hits(child); - struct callchain_list *chain; - char folded_sign = ' '; - int first = true; - int extra_offset = 0; - - remaining -= cumul; - - list_for_each_entry(chain, &child->val, list) { - char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str; - const char *str; - int color; - bool was_first = first; - - if (first) { - first = false; - chain->ms.has_children = chain->list.next != &child->val || - rb_first(&child->rb_root) != NULL; - } else { - extra_offset = LEVEL_OFFSET_STEP; - chain->ms.has_children = chain->list.next == &child->val && - rb_first(&child->rb_root) != NULL; - } - - folded_sign = callchain_list__folded(chain); - if (*row_offset != 0) { - --*row_offset; - goto do_next; - } - - alloc_str = NULL; - str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - if (was_first) { - double percent = cumul * 100.0 / new_total; - - if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) - str = "Not enough memory!"; - else - str = alloc_str; - } - - color = HE_COLORSET_NORMAL; - width = self->b.width - (offset + extra_offset + 2); - if (ui_browser__is_current_entry(&self->b, row)) { - self->selection = &chain->ms; - color = HE_COLORSET_SELECTED; - *is_current_entry = true; - } - - SLsmg_set_color(color); - SLsmg_gotorc(self->b.y + row, self->b.x); - slsmg_write_nstring(" ", offset + extra_offset); - slsmg_printf("%c ", folded_sign); - slsmg_write_nstring(str, width); - free(alloc_str); - - if (++row == self->b.height) - goto out; -do_next: - if (folded_sign == '+') - break; - } - - if (folded_sign == '-') { - const int new_level = level + (extra_offset ? 2 : 1); - row += hist_browser__show_callchain_node_rb_tree(self, child, new_total, - new_level, row, row_offset, - is_current_entry); - } - if (row == self->b.height) - goto out; - node = next; - } -out: - return row - first_row; -} - -static int hist_browser__show_callchain_node(struct hist_browser *self, - struct callchain_node *node, - int level, unsigned short row, - off_t *row_offset, - bool *is_current_entry) -{ - struct callchain_list *chain; - int first_row = row, - offset = level * LEVEL_OFFSET_STEP, - width = self->b.width - offset; - char folded_sign = ' '; - - list_for_each_entry(chain, &node->val, list) { - char ipstr[BITS_PER_LONG / 4 + 1], *s; - int color; - /* - * FIXME: This should be moved to somewhere else, - * probably when the callchain is created, so as not to - * traverse it all over again - */ - chain->ms.has_children = rb_first(&node->rb_root) != NULL; - folded_sign = callchain_list__folded(chain); - - if (*row_offset != 0) { - --*row_offset; - continue; - } - - color = HE_COLORSET_NORMAL; - if (ui_browser__is_current_entry(&self->b, row)) { - self->selection = &chain->ms; - color = HE_COLORSET_SELECTED; - *is_current_entry = true; - } - - s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); - SLsmg_gotorc(self->b.y + row, self->b.x); - SLsmg_set_color(color); - slsmg_write_nstring(" ", offset); - slsmg_printf("%c ", folded_sign); - slsmg_write_nstring(s, width - 2); - - if (++row == self->b.height) - goto out; - } - - if (folded_sign == '-') - row += hist_browser__show_callchain_node_rb_tree(self, node, - self->hists->stats.total_period, - level + 1, row, - row_offset, - is_current_entry); -out: - return row - first_row; -} - -static int hist_browser__show_callchain(struct hist_browser *self, - struct rb_root *chain, - int level, unsigned short row, - off_t *row_offset, - bool *is_current_entry) -{ - struct rb_node *nd; - int first_row = row; - - for (nd = rb_first(chain); nd; nd = rb_next(nd)) { - struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); - - row += hist_browser__show_callchain_node(self, node, level, - row, row_offset, - is_current_entry); - if (row == self->b.height) - break; - } - - return row - first_row; -} - -static int hist_browser__show_entry(struct hist_browser *self, - struct hist_entry *entry, - unsigned short row) -{ - char s[256]; - double percent; - int printed = 0; - int color, width = self->b.width; - char folded_sign = ' '; - bool current_entry = ui_browser__is_current_entry(&self->b, row); - off_t row_offset = entry->row_offset; - - if (current_entry) { - self->he_selection = entry; - self->selection = &entry->ms; - } - - if (symbol_conf.use_callchain) { - entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain); - folded_sign = hist_entry__folded(entry); - } - - if (row_offset == 0) { - hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false, - 0, false, self->hists->stats.total_period); - percent = (entry->period * 100.0) / self->hists->stats.total_period; - - color = HE_COLORSET_SELECTED; - if (!current_entry) { - if (percent >= MIN_RED) - color = HE_COLORSET_TOP; - else if (percent >= MIN_GREEN) - color = HE_COLORSET_MEDIUM; - else - color = HE_COLORSET_NORMAL; - } - - SLsmg_set_color(color); - SLsmg_gotorc(self->b.y + row, self->b.x); - if (symbol_conf.use_callchain) { - slsmg_printf("%c ", folded_sign); - width -= 2; - } - slsmg_write_nstring(s, width); - ++row; - ++printed; - } else - --row_offset; - - if (folded_sign == '-' && row != self->b.height) { - printed += hist_browser__show_callchain(self, &entry->sorted_chain, - 1, row, &row_offset, - ¤t_entry); - if (current_entry) - self->he_selection = entry; - } - - return printed; -} - -static unsigned int hist_browser__refresh(struct ui_browser *self) -{ - unsigned row = 0; - struct rb_node *nd; - struct hist_browser *hb = container_of(self, struct hist_browser, b); - - if (self->top == NULL) - self->top = rb_first(&hb->hists->entries); - - 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 void callchain_node__init_have_children_rb_tree(struct callchain_node *self) -{ - struct rb_node *nd = rb_first(&self->rb_root); - - for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { - struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); - struct callchain_list *chain; - int first = true; - - list_for_each_entry(chain, &child->val, list) { - if (first) { - first = false; - chain->ms.has_children = chain->list.next != &child->val || - rb_first(&child->rb_root) != NULL; - } else - chain->ms.has_children = chain->list.next == &child->val && - rb_first(&child->rb_root) != NULL; - } - - callchain_node__init_have_children_rb_tree(child); - } -} - -static void callchain_node__init_have_children(struct callchain_node *self) -{ - struct callchain_list *chain; - - list_for_each_entry(chain, &self->val, list) - chain->ms.has_children = rb_first(&self->rb_root) != NULL; - - callchain_node__init_have_children_rb_tree(self); -} - -static void callchain__init_have_children(struct rb_root *self) -{ - struct rb_node *nd; - - for (nd = rb_first(self); nd; nd = rb_next(nd)) { - struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); - callchain_node__init_have_children(node); - } -} - -static void hist_entry__init_have_children(struct hist_entry *self) -{ - if (!self->init_have_children) { - callchain__init_have_children(&self->sorted_chain); - self->init_have_children = true; - } -} - -static struct rb_node *hists__filter_entries(struct rb_node *nd) -{ - while (nd != NULL) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - if (!h->filtered) - return nd; - - nd = rb_next(nd); - } - - return NULL; -} - -static struct rb_node *hists__filter_prev_entries(struct rb_node *nd) -{ - while (nd != NULL) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - if (!h->filtered) - return nd; - - nd = rb_prev(nd); - } - - return NULL; -} - -static void ui_browser__hists_seek(struct ui_browser *self, - off_t offset, int whence) -{ - struct hist_entry *h; - struct rb_node *nd; - bool first = true; - - switch (whence) { - case SEEK_SET: - nd = hists__filter_entries(rb_first(self->entries)); - break; - case SEEK_CUR: - nd = self->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 int callchain_node__count_rows_rb_tree(struct callchain_node *self) -{ - int n = 0; - struct rb_node *nd; - - for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { - struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); - struct callchain_list *chain; - char folded_sign = ' '; /* No children */ - - list_for_each_entry(chain, &child->val, list) { - ++n; - /* We need this because we may not have children */ - folded_sign = callchain_list__folded(chain); - if (folded_sign == '+') - break; - } - - if (folded_sign == '-') /* Have children and they're unfolded */ - n += callchain_node__count_rows_rb_tree(child); - } - - return n; -} - -static int callchain_node__count_rows(struct callchain_node *node) -{ - struct callchain_list *chain; - bool unfolded = false; - int n = 0; - - list_for_each_entry(chain, &node->val, list) { - ++n; - unfolded = chain->ms.unfolded; - } - - if (unfolded) - n += callchain_node__count_rows_rb_tree(node); - - return n; -} - -static int callchain__count_rows(struct rb_root *chain) -{ - struct rb_node *nd; - int n = 0; - - for (nd = rb_first(chain); nd; nd = rb_next(nd)) { - struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); - n += callchain_node__count_rows(node); - } - - return n; -} - -static bool hist_browser__toggle_fold(struct hist_browser *self) -{ - if (map_symbol__toggle_fold(self->selection)) { - struct hist_entry *he = self->he_selection; - - hist_entry__init_have_children(he); - self->hists->nr_entries -= he->nr_rows; - - if (he->ms.unfolded) - he->nr_rows = callchain__count_rows(&he->sorted_chain); - else - he->nr_rows = 0; - self->hists->nr_entries += he->nr_rows; - self->b.nr_entries = self->hists->nr_entries; - - return true; - } - - /* If it doesn't have children, no toggling performed */ - return false; -} - -static int hist_browser__run(struct hist_browser *self, const char *title, - struct newtExitStruct *es) -{ - char str[256], unit; - unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE]; - - self->b.entries = &self->hists->entries; - self->b.nr_entries = self->hists->nr_entries; - - hist_browser__refresh_dimensions(self); - - nr_events = convert_unit(nr_events, &unit); - snprintf(str, sizeof(str), "Events: %lu%c ", - nr_events, unit); - newtDrawRootText(0, 0, str); - - if (ui_browser__show(&self->b, title) < 0) - return -1; - - newtFormAddHotKey(self->b.form, 'A'); - newtFormAddHotKey(self->b.form, 'a'); - newtFormAddHotKey(self->b.form, '?'); - newtFormAddHotKey(self->b.form, 'h'); - newtFormAddHotKey(self->b.form, 'H'); - newtFormAddHotKey(self->b.form, 'd'); - - newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); - newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); - newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); - - while (1) { - ui_browser__run(&self->b, es); - - if (es->reason != NEWT_EXIT_HOTKEY) - break; - switch (es->u.key) { - case 'd': { /* Debug */ - static int seq; - struct hist_entry *h = rb_entry(self->b.top, - struct hist_entry, rb_node); - ui_helpline__pop(); - ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", - seq++, self->b.nr_entries, - self->hists->nr_entries, - self->b.height, - self->b.index, - self->b.top_idx, - h->row_offset, h->nr_rows); - } - continue; - case NEWT_KEY_ENTER: - if (hist_browser__toggle_fold(self)) - break; - /* fall thru */ - default: - return 0; - } - } - return 0; -} diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h index 5ad0702..4cedea5 100644 --- a/tools/perf/util/pstack.h +++ b/tools/perf/util/pstack.h @@ -1,6 +1,8 @@ #ifndef _PERF_PSTACK_ #define _PERF_PSTACK_ +#include + struct pstack; struct pstack *pstack__new(unsigned short max_nr_entries); void pstack__delete(struct pstack *self); diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c new file mode 100644 index 0000000..9d32a41 --- /dev/null +++ b/tools/perf/util/ui/browsers/hists.c @@ -0,0 +1,950 @@ +#define _GNU_SOURCE +#include +#undef _GNU_SOURCE +#include "../libslang.h" +#include +#include +#include +#include + +#include "../../hist.h" +#include "../../pstack.h" +#include "../../sort.h" +#include "../../util.h" + +#include "../browser.h" +#include "../helpline.h" +#include "../util.h" +#include "map.h" + +int ui__help_window(const char *text); +bool dialog_yesno(const char *msg); +int popup_menu(int argc, char * const argv[]); + +struct hist_browser { + struct ui_browser b; + struct hists *hists; + struct hist_entry *he_selection; + struct map_symbol *selection; +}; + +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 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->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; + int first = true; + + list_for_each_entry(chain, &child->val, list) { + if (first) { + first = false; + chain->ms.has_children = chain->list.next != &child->val || + rb_first(&child->rb_root) != NULL; + } else + chain->ms.has_children = chain->list.next == &child->val && + rb_first(&child->rb_root) != NULL; + } + + callchain_node__init_have_children_rb_tree(child); + } +} + +static void callchain_node__init_have_children(struct callchain_node *self) +{ + struct callchain_list *chain; + + list_for_each_entry(chain, &self->val, list) + chain->ms.has_children = rb_first(&self->rb_root) != NULL; + + callchain_node__init_have_children_rb_tree(self); +} + +static void callchain__init_have_children(struct rb_root *self) +{ + struct rb_node *nd; + + for (nd = rb_first(self); nd; nd = rb_next(nd)) { + struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); + callchain_node__init_have_children(node); + } +} + +static void hist_entry__init_have_children(struct hist_entry *self) +{ + if (!self->init_have_children) { + callchain__init_have_children(&self->sorted_chain); + self->init_have_children = true; + } +} + +static bool hist_browser__toggle_fold(struct hist_browser *self) +{ + if (map_symbol__toggle_fold(self->selection)) { + struct hist_entry *he = self->he_selection; + + hist_entry__init_have_children(he); + self->hists->nr_entries -= he->nr_rows; + + if (he->ms.unfolded) + he->nr_rows = callchain__count_rows(&he->sorted_chain); + else + he->nr_rows = 0; + self->hists->nr_entries += he->nr_rows; + self->b.nr_entries = self->hists->nr_entries; + + return true; + } + + /* If it doesn't have children, no toggling performed */ + return false; +} + +static int hist_browser__run(struct hist_browser *self, const char *title, + struct newtExitStruct *es) +{ + char str[256], unit; + unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE]; + + self->b.entries = &self->hists->entries; + self->b.nr_entries = self->hists->nr_entries; + + hist_browser__refresh_dimensions(self); + + nr_events = convert_unit(nr_events, &unit); + snprintf(str, sizeof(str), "Events: %lu%c ", + nr_events, unit); + newtDrawRootText(0, 0, str); + + if (ui_browser__show(&self->b, title) < 0) + return -1; + + newtFormAddHotKey(self->b.form, 'A'); + newtFormAddHotKey(self->b.form, 'a'); + newtFormAddHotKey(self->b.form, '?'); + newtFormAddHotKey(self->b.form, 'h'); + newtFormAddHotKey(self->b.form, 'H'); + newtFormAddHotKey(self->b.form, 'd'); + + newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); + newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); + newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); + + while (1) { + ui_browser__run(&self->b, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + switch (es->u.key) { + case 'd': { /* Debug */ + static int seq; + struct hist_entry *h = rb_entry(self->b.top, + struct hist_entry, rb_node); + ui_helpline__pop(); + ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", + seq++, self->b.nr_entries, + self->hists->nr_entries, + self->b.height, + self->b.index, + self->b.top_idx, + h->row_offset, h->nr_rows); + } + continue; + case NEWT_KEY_ENTER: + if (hist_browser__toggle_fold(self)) + break; + /* fall thru */ + default: + return 0; + } + } + return 0; +} + +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, "%#Lx", 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 = cumul_hits(child); + struct callchain_list *chain; + char folded_sign = ' '; + int first = true; + int extra_offset = 0; + + remaining -= cumul; + + list_for_each_entry(chain, &child->val, list) { + char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str; + const char *str; + int color; + bool was_first = first; + + if (first) { + first = false; + chain->ms.has_children = chain->list.next != &child->val || + rb_first(&child->rb_root) != NULL; + } else { + extra_offset = LEVEL_OFFSET_STEP; + chain->ms.has_children = chain->list.next == &child->val && + rb_first(&child->rb_root) != NULL; + } + + folded_sign = callchain_list__folded(chain); + if (*row_offset != 0) { + --*row_offset; + goto do_next; + } + + alloc_str = NULL; + str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); + if (was_first) { + double percent = cumul * 100.0 / new_total; + + if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) + str = "Not enough memory!"; + else + str = alloc_str; + } + + color = HE_COLORSET_NORMAL; + width = self->b.width - (offset + extra_offset + 2); + if (ui_browser__is_current_entry(&self->b, row)) { + self->selection = &chain->ms; + color = HE_COLORSET_SELECTED; + *is_current_entry = true; + } + + SLsmg_set_color(color); + SLsmg_gotorc(self->b.y + row, self->b.x); + slsmg_write_nstring(" ", offset + extra_offset); + slsmg_printf("%c ", folded_sign); + slsmg_write_nstring(str, width); + free(alloc_str); + + if (++row == self->b.height) + goto out; +do_next: + if (folded_sign == '+') + break; + } + + if (folded_sign == '-') { + const int new_level = level + (extra_offset ? 2 : 1); + row += hist_browser__show_callchain_node_rb_tree(self, child, new_total, + new_level, row, row_offset, + is_current_entry); + } + if (row == self->b.height) + goto out; + node = next; + } +out: + return row - first_row; +} + +static int hist_browser__show_callchain_node(struct hist_browser *self, + struct callchain_node *node, + int level, unsigned short row, + off_t *row_offset, + bool *is_current_entry) +{ + struct callchain_list *chain; + int first_row = row, + offset = level * LEVEL_OFFSET_STEP, + width = self->b.width - offset; + char folded_sign = ' '; + + list_for_each_entry(chain, &node->val, list) { + char ipstr[BITS_PER_LONG / 4 + 1], *s; + int color; + /* + * FIXME: This should be moved to somewhere else, + * probably when the callchain is created, so as not to + * traverse it all over again + */ + chain->ms.has_children = rb_first(&node->rb_root) != NULL; + folded_sign = callchain_list__folded(chain); + + if (*row_offset != 0) { + --*row_offset; + continue; + } + + color = HE_COLORSET_NORMAL; + if (ui_browser__is_current_entry(&self->b, row)) { + self->selection = &chain->ms; + color = HE_COLORSET_SELECTED; + *is_current_entry = true; + } + + s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); + SLsmg_gotorc(self->b.y + row, self->b.x); + SLsmg_set_color(color); + slsmg_write_nstring(" ", offset); + slsmg_printf("%c ", folded_sign); + slsmg_write_nstring(s, width - 2); + + if (++row == self->b.height) + goto out; + } + + if (folded_sign == '-') + row += hist_browser__show_callchain_node_rb_tree(self, node, + self->hists->stats.total_period, + level + 1, row, + row_offset, + is_current_entry); +out: + return row - first_row; +} + +static int hist_browser__show_callchain(struct hist_browser *self, + struct rb_root *chain, + int level, unsigned short row, + off_t *row_offset, + bool *is_current_entry) +{ + struct rb_node *nd; + int first_row = row; + + for (nd = rb_first(chain); nd; nd = rb_next(nd)) { + struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); + + row += hist_browser__show_callchain_node(self, node, level, + row, row_offset, + is_current_entry); + if (row == self->b.height) + break; + } + + return row - first_row; +} + +static int hist_browser__show_entry(struct hist_browser *self, + struct hist_entry *entry, + unsigned short row) +{ + char s[256]; + double percent; + int printed = 0; + int color, width = self->b.width; + char folded_sign = ' '; + bool current_entry = ui_browser__is_current_entry(&self->b, row); + off_t row_offset = entry->row_offset; + + if (current_entry) { + self->he_selection = entry; + self->selection = &entry->ms; + } + + if (symbol_conf.use_callchain) { + entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain); + folded_sign = hist_entry__folded(entry); + } + + if (row_offset == 0) { + hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false, + 0, false, self->hists->stats.total_period); + percent = (entry->period * 100.0) / self->hists->stats.total_period; + + color = HE_COLORSET_SELECTED; + if (!current_entry) { + if (percent >= MIN_RED) + color = HE_COLORSET_TOP; + else if (percent >= MIN_GREEN) + color = HE_COLORSET_MEDIUM; + else + color = HE_COLORSET_NORMAL; + } + + SLsmg_set_color(color); + SLsmg_gotorc(self->b.y + row, self->b.x); + if (symbol_conf.use_callchain) { + slsmg_printf("%c ", folded_sign); + width -= 2; + } + slsmg_write_nstring(s, width); + ++row; + ++printed; + } else + --row_offset; + + if (folded_sign == '-' && row != self->b.height) { + printed += hist_browser__show_callchain(self, &entry->sorted_chain, + 1, row, &row_offset, + ¤t_entry); + if (current_entry) + self->he_selection = entry; + } + + return printed; +} + +static unsigned int hist_browser__refresh(struct ui_browser *self) +{ + unsigned row = 0; + struct rb_node *nd; + struct hist_browser *hb = container_of(self, struct hist_browser, b); + + if (self->top == NULL) + self->top = rb_first(&hb->hists->entries); + + 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; + + 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; + } + + return self; +} + +static void hist_browser__delete(struct hist_browser *self) +{ + newtFormDestroy(self->b.form); + newtPopWindow(); + free(self); +} + +static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) +{ + return self->he_selection; +} + +static struct thread *hist_browser__selected_thread(struct hist_browser *self) +{ + return self->he_selection->thread; +} + +static int hist_browser__title(char *bf, size_t size, const char *ev_name, + const struct dso *dso, const struct thread *thread) +{ + int printed = 0; + + if (thread) + printed += snprintf(bf + printed, size - printed, + "Thread: %s(%d)", + (thread->comm_set ? thread->comm : ""), + thread->pid); + if (dso) + printed += snprintf(bf + printed, size - printed, + "%sDSO: %s", thread ? " " : "", + dso->short_name); + return printed ?: snprintf(bf, size, "Event: %s", ev_name); +} + +int hists__browse(struct hists *self, const char *helpline, const char *ev_name) +{ + struct hist_browser *browser = hist_browser__new(self); + struct pstack *fstack; + const struct thread *thread_filter = NULL; + const struct dso *dso_filter = NULL; + struct newtExitStruct es; + char msg[160]; + int key = -1; + + if (browser == NULL) + return -1; + + fstack = pstack__new(2); + if (fstack == NULL) + goto out; + + ui_helpline__push(helpline); + + hist_browser__title(msg, sizeof(msg), ev_name, + dso_filter, thread_filter); + + while (1) { + const struct thread *thread; + const struct dso *dso; + char *options[16]; + int nr_options = 0, choice = 0, i, + annotate = -2, zoom_dso = -2, zoom_thread = -2, + browse_map = -2; + + if (hist_browser__run(browser, msg, &es)) + break; + + thread = hist_browser__selected_thread(browser); + dso = browser->selection->map ? browser->selection->map->dso : NULL; + + if (es.reason == NEWT_EXIT_HOTKEY) { + key = es.u.key; + + switch (key) { + case NEWT_KEY_F1: + goto do_help; + case NEWT_KEY_TAB: + case NEWT_KEY_UNTAB: + /* + * Exit the browser, let hists__browser_tree + * go to the next or previous + */ + goto out_free_stack; + default:; + } + + key = toupper(key); + switch (key) { + case 'A': + if (browser->selection->map == NULL && + browser->selection->map->dso->annotate_warned) + continue; + goto do_annotate; + case 'D': + goto zoom_dso; + case 'T': + goto zoom_thread; + case 'H': + case '?': +do_help: + ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" + "<- Zoom out\n" + "a Annotate current symbol\n" + "h/?/F1 Show this window\n" + "d Zoom into current DSO\n" + "t Zoom into current Thread\n" + "q/CTRL+C Exit browser"); + continue; + default:; + } + if (is_exit_key(key)) { + if (key == NEWT_KEY_ESCAPE && + !dialog_yesno("Do you really want to exit?")) + continue; + break; + } + + if (es.u.key == NEWT_KEY_LEFT) { + const void *top; + + if (pstack__empty(fstack)) + continue; + top = pstack__pop(fstack); + if (top == &dso_filter) + goto zoom_out_dso; + if (top == &thread_filter) + goto zoom_out_thread; + continue; + } + } + + if (browser->selection->sym != NULL && + !browser->selection->map->dso->annotate_warned && + asprintf(&options[nr_options], "Annotate %s", + browser->selection->sym->name) > 0) + annotate = nr_options++; + + if (thread != NULL && + asprintf(&options[nr_options], "Zoom %s %s(%d) thread", + (thread_filter ? "out of" : "into"), + (thread->comm_set ? thread->comm : ""), + thread->pid) > 0) + zoom_thread = nr_options++; + + if (dso != NULL && + asprintf(&options[nr_options], "Zoom %s %s DSO", + (dso_filter ? "out of" : "into"), + (dso->kernel ? "the Kernel" : dso->short_name)) > 0) + zoom_dso = nr_options++; + + if (browser->selection->map != NULL && + asprintf(&options[nr_options], "Browse map details") > 0) + browse_map = nr_options++; + + options[nr_options++] = (char *)"Exit"; + + choice = popup_menu(nr_options, options); + + for (i = 0; i < nr_options - 1; ++i) + free(options[i]); + + if (choice == nr_options - 1) + break; + + if (choice == -1) + continue; + + if (choice == annotate) { + struct hist_entry *he; +do_annotate: + if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { + browser->selection->map->dso->annotate_warned = 1; + ui_helpline__puts("No vmlinux file found, can't " + "annotate with just a " + "kallsyms file"); + continue; + } + + he = hist_browser__selected_entry(browser); + if (he == NULL) + continue; + + hist_entry__tui_annotate(he); + } else if (choice == browse_map) + map__browse(browser->selection->map); + else if (choice == zoom_dso) { +zoom_dso: + if (dso_filter) { + pstack__remove(fstack, &dso_filter); +zoom_out_dso: + ui_helpline__pop(); + dso_filter = NULL; + } else { + if (dso == NULL) + continue; + ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", + dso->kernel ? "the Kernel" : dso->short_name); + dso_filter = dso; + pstack__push(fstack, &dso_filter); + } + hists__filter_by_dso(self, dso_filter); + hist_browser__title(msg, sizeof(msg), ev_name, + dso_filter, thread_filter); + hist_browser__reset(browser); + } else if (choice == zoom_thread) { +zoom_thread: + if (thread_filter) { + pstack__remove(fstack, &thread_filter); +zoom_out_thread: + ui_helpline__pop(); + thread_filter = NULL; + } else { + ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", + thread->comm_set ? thread->comm : "", + thread->pid); + thread_filter = thread; + pstack__push(fstack, &thread_filter); + } + hists__filter_by_thread(self, thread_filter); + hist_browser__title(msg, sizeof(msg), ev_name, + dso_filter, thread_filter); + hist_browser__reset(browser); + } + } +out_free_stack: + pstack__delete(fstack); +out: + hist_browser__delete(browser); + return key; +} + +int hists__tui_browse_tree(struct rb_root *self, const char *help) +{ + struct rb_node *first = rb_first(self), *nd = first, *next; + int key = 0; + + while (nd) { + struct hists *hists = rb_entry(nd, struct hists, rb_node); + const char *ev_name = __event_name(hists->type, hists->config); + + key = hists__browse(hists, help, ev_name); + + if (is_exit_key(key)) + break; + + switch (key) { + case NEWT_KEY_TAB: + next = rb_next(nd); + if (next) + nd = next; + break; + case NEWT_KEY_UNTAB: + if (nd == first) + continue; + nd = rb_prev(nd); + default: + break; + } + } + + return key; +} -- cgit v0.10.2 From 1e6dd077a880ba5570beb690523b7a78a91a7615 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Aug 2010 15:58:50 -0300 Subject: perf ui: Complete the breakdown of util/newt.c LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 62e4d6f..41abb90 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -567,18 +567,20 @@ else # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h BASIC_CFLAGS += -I/usr/include/slang EXTLIBS += -lnewt -lslang - LIB_OBJS += $(OUTPUT)util/newt.o + LIB_OBJS += $(OUTPUT)util/ui/setup.o LIB_OBJS += $(OUTPUT)util/ui/browser.o LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o LIB_OBJS += $(OUTPUT)util/ui/helpline.o LIB_OBJS += $(OUTPUT)util/ui/progress.o + LIB_OBJS += $(OUTPUT)util/ui/util.o LIB_H += util/ui/browser.h LIB_H += util/ui/browsers/map.h LIB_H += util/ui/helpline.h LIB_H += util/ui/libslang.h LIB_H += util/ui/progress.h + LIB_H += util/ui/util.h endif endif @@ -976,9 +978,6 @@ $(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< -$(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< - $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 318dab1..f9c7e3a 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -23,7 +23,7 @@ int eprintf(int level, const char *fmt, ...) if (verbose >= level) { va_start(args, fmt); if (use_browser > 0) - ret = browser__show_help(fmt, args); + ret = ui_helpline__show_help(fmt, args); else ret = vfprintf(stderr, fmt, args); va_end(args); diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index ba4892e..7a17ee0 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -14,7 +14,7 @@ void trace_event(event_t *event); struct ui_progress; #ifdef NO_NEWT_SUPPORT -static inline int browser__show_help(const char *format __used, va_list ap __used) +static inline int ui_helpline__show_help(const char *format __used, va_list ap __used) { return 0; } @@ -30,8 +30,8 @@ static inline void ui_progress__update(struct ui_progress *self __used, static inline void ui_progress__delete(struct ui_progress *self __used) {} #else -extern char browser__last_msg[]; -int browser__show_help(const char *format, va_list ap); +extern char ui_helpline__last_msg[]; +int ui_helpline__show_help(const char *format, va_list ap); #include "ui/progress.h" #endif diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c deleted file mode 100644 index 6bccdaa..0000000 --- a/tools/perf/util/newt.c +++ /dev/null @@ -1,170 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "cache.h" -#include "debug.h" -#include "ui/browser.h" -#include "ui/helpline.h" - -newtComponent newt_form__new(void); -int popup_menu(int argc, char * const argv[]); -int ui__help_window(const char *text); -bool dialog_yesno(const char *msg); - -char browser__last_msg[1024]; - -int browser__show_help(const char *format, va_list ap) -{ - int ret; - static int backlog; - - ret = vsnprintf(browser__last_msg + backlog, - sizeof(browser__last_msg) - backlog, format, ap); - backlog += ret; - - if (browser__last_msg[backlog - 1] == '\n') { - ui_helpline__puts(browser__last_msg); - newtRefresh(); - backlog = 0; - } - - return ret; -} - -static void newt_form__set_exit_keys(newtComponent self) -{ - newtFormAddHotKey(self, NEWT_KEY_LEFT); - newtFormAddHotKey(self, NEWT_KEY_ESCAPE); - newtFormAddHotKey(self, 'Q'); - newtFormAddHotKey(self, 'q'); - newtFormAddHotKey(self, CTRL('c')); -} - -newtComponent newt_form__new(void) -{ - newtComponent self = newtForm(NULL, NULL, 0); - if (self) - newt_form__set_exit_keys(self); - return self; -} - -int popup_menu(int argc, char * const argv[]) -{ - struct newtExitStruct es; - int i, rc = -1, max_len = 5; - newtComponent listbox, form = newt_form__new(); - - if (form == NULL) - return -1; - - listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT); - if (listbox == NULL) - goto out_destroy_form; - - newtFormAddComponent(form, listbox); - - for (i = 0; i < argc; ++i) { - int len = strlen(argv[i]); - if (len > max_len) - max_len = len; - if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i)) - goto out_destroy_form; - } - - newtCenteredWindow(max_len, argc, NULL); - newtFormRun(form, &es); - rc = newtListboxGetCurrent(listbox) - NULL; - if (es.reason == NEWT_EXIT_HOTKEY) - rc = -1; - newtPopWindow(); -out_destroy_form: - newtFormDestroy(form); - return rc; -} - -int ui__help_window(const char *text) -{ - struct newtExitStruct es; - newtComponent tb, form = newt_form__new(); - int rc = -1; - int max_len = 0, nr_lines = 0; - const char *t; - - if (form == NULL) - return -1; - - t = text; - while (1) { - const char *sep = strchr(t, '\n'); - int len; - - if (sep == NULL) - sep = strchr(t, '\0'); - len = sep - t; - if (max_len < len) - max_len = len; - ++nr_lines; - if (*sep == '\0') - break; - t = sep + 1; - } - - tb = newtTextbox(0, 0, max_len, nr_lines, 0); - if (tb == NULL) - goto out_destroy_form; - - newtTextboxSetText(tb, text); - newtFormAddComponent(form, tb); - newtCenteredWindow(max_len, nr_lines, NULL); - newtFormRun(form, &es); - newtPopWindow(); - rc = 0; -out_destroy_form: - newtFormDestroy(form); - return rc; -} - -bool dialog_yesno(const char *msg) -{ - /* newtWinChoice should really be accepting const char pointers... */ - char yes[] = "Yes", no[] = "No"; - return newtWinChoice(NULL, yes, no, (char *)msg) == 1; -} - -static void newt_suspend(void *d __used) -{ - newtSuspend(); - raise(SIGTSTP); - newtResume(); -} - -void setup_browser(void) -{ - if (!isatty(1) || !use_browser || dump_trace) { - use_browser = 0; - setup_pager(); - return; - } - - use_browser = 1; - newtInit(); - newtCls(); - newtSetSuspendCallback(newt_suspend, NULL); - ui_helpline__puts(" "); - ui_browser__init(); -} - -void exit_browser(bool wait_for_ok) -{ - if (use_browser > 0) { - if (wait_for_ok) { - char title[] = "Fatal Error", ok[] = "Ok"; - newtWinMessage(title, ok, browser__last_msg); - } - newtFinished(); - } -} diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 783d277..5b01df6 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c @@ -86,7 +86,7 @@ int hist_entry__tui_annotate(struct hist_entry *self) return -1; if (hist_entry__annotate(self, &head) < 0) { - ui__error_window(browser__last_msg); + ui__error_window(ui_helpline__last_msg); return -1; } diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 9d32a41..cee7998 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c @@ -17,10 +17,6 @@ #include "../util.h" #include "map.h" -int ui__help_window(const char *text); -bool dialog_yesno(const char *msg); -int popup_menu(int argc, char * const argv[]); - struct hist_browser { struct ui_browser b; struct hists *hists; @@ -798,7 +794,7 @@ do_help: } if (is_exit_key(key)) { if (key == NEWT_KEY_ESCAPE && - !dialog_yesno("Do you really want to exit?")) + !ui__dialog_yesno("Do you really want to exit?")) continue; break; } @@ -842,7 +838,7 @@ do_help: options[nr_options++] = (char *)"Exit"; - choice = popup_menu(nr_options, options); + choice = ui__popup_menu(nr_options, options); for (i = 0; i < nr_options - 1; ++i) free(options[i]); diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c index 6a11e130..ff58460 100644 --- a/tools/perf/util/ui/helpline.c +++ b/tools/perf/util/ui/helpline.c @@ -3,6 +3,7 @@ #include #include +#include "../debug.h" #include "helpline.h" void ui_helpline__pop(void) @@ -41,3 +42,28 @@ void ui_helpline__puts(const char *msg) ui_helpline__pop(); ui_helpline__push(msg); } + +void ui_helpline__init(void) +{ + ui_helpline__puts(" "); +} + +char ui_helpline__last_msg[1024]; + +int ui_helpline__show_help(const char *format, va_list ap) +{ + int ret; + static int backlog; + + ret = vsnprintf(ui_helpline__last_msg + backlog, + sizeof(ui_helpline__last_msg) - backlog, format, ap); + backlog += ret; + + if (ui_helpline__last_msg[backlog - 1] == '\n') { + ui_helpline__puts(ui_helpline__last_msg); + newtRefresh(); + backlog = 0; + } + + return ret; +} diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h index 56d8c1d..5d1e5e7 100644 --- a/tools/perf/util/ui/helpline.h +++ b/tools/perf/util/ui/helpline.h @@ -1,6 +1,7 @@ #ifndef _PERF_UI_HELPLINE_H_ #define _PERF_UI_HELPLINE_H_ 1 +void ui_helpline__init(void); void ui_helpline__pop(void); void ui_helpline__push(const char *msg); void ui_helpline__fpush(const char *fmt, ...); diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c new file mode 100644 index 0000000..6620850 --- /dev/null +++ b/tools/perf/util/ui/setup.c @@ -0,0 +1,42 @@ +#include +#include +#include + +#include "../cache.h" +#include "../debug.h" +#include "browser.h" +#include "helpline.h" + +static void newt_suspend(void *d __used) +{ + newtSuspend(); + raise(SIGTSTP); + newtResume(); +} + +void setup_browser(void) +{ + if (!isatty(1) || !use_browser || dump_trace) { + use_browser = 0; + setup_pager(); + return; + } + + use_browser = 1; + newtInit(); + newtCls(); + newtSetSuspendCallback(newt_suspend, NULL); + ui_helpline__init(); + ui_browser__init(); +} + +void exit_browser(bool wait_for_ok) +{ + if (use_browser > 0) { + if (wait_for_ok) { + char title[] = "Fatal Error", ok[] = "Ok"; + newtWinMessage(title, ok, ui_helpline__last_msg); + } + newtFinished(); + } +} diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c new file mode 100644 index 0000000..04600e2 --- /dev/null +++ b/tools/perf/util/ui/util.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include +#include + +#include "../cache.h" +#include "../debug.h" +#include "browser.h" +#include "helpline.h" +#include "util.h" + +newtComponent newt_form__new(void); + +static void newt_form__set_exit_keys(newtComponent self) +{ + newtFormAddHotKey(self, NEWT_KEY_LEFT); + newtFormAddHotKey(self, NEWT_KEY_ESCAPE); + newtFormAddHotKey(self, 'Q'); + newtFormAddHotKey(self, 'q'); + newtFormAddHotKey(self, CTRL('c')); +} + +newtComponent newt_form__new(void) +{ + newtComponent self = newtForm(NULL, NULL, 0); + if (self) + newt_form__set_exit_keys(self); + return self; +} + +int ui__popup_menu(int argc, char * const argv[]) +{ + struct newtExitStruct es; + int i, rc = -1, max_len = 5; + newtComponent listbox, form = newt_form__new(); + + if (form == NULL) + return -1; + + listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT); + if (listbox == NULL) + goto out_destroy_form; + + newtFormAddComponent(form, listbox); + + for (i = 0; i < argc; ++i) { + int len = strlen(argv[i]); + if (len > max_len) + max_len = len; + if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i)) + goto out_destroy_form; + } + + newtCenteredWindow(max_len, argc, NULL); + newtFormRun(form, &es); + rc = newtListboxGetCurrent(listbox) - NULL; + if (es.reason == NEWT_EXIT_HOTKEY) + rc = -1; + newtPopWindow(); +out_destroy_form: + newtFormDestroy(form); + return rc; +} + +int ui__help_window(const char *text) +{ + struct newtExitStruct es; + newtComponent tb, form = newt_form__new(); + int rc = -1; + int max_len = 0, nr_lines = 0; + const char *t; + + if (form == NULL) + return -1; + + t = text; + while (1) { + const char *sep = strchr(t, '\n'); + int len; + + if (sep == NULL) + sep = strchr(t, '\0'); + len = sep - t; + if (max_len < len) + max_len = len; + ++nr_lines; + if (*sep == '\0') + break; + t = sep + 1; + } + + tb = newtTextbox(0, 0, max_len, nr_lines, 0); + if (tb == NULL) + goto out_destroy_form; + + newtTextboxSetText(tb, text); + newtFormAddComponent(form, tb); + newtCenteredWindow(max_len, nr_lines, NULL); + newtFormRun(form, &es); + newtPopWindow(); + rc = 0; +out_destroy_form: + newtFormDestroy(form); + return rc; +} + +bool ui__dialog_yesno(const char *msg) +{ + /* newtWinChoice should really be accepting const char pointers... */ + char yes[] = "Yes", no[] = "No"; + return newtWinChoice(NULL, yes, no, (char *)msg) == 1; +} diff --git a/tools/perf/util/ui/util.h b/tools/perf/util/ui/util.h new file mode 100644 index 0000000..afcbc1d --- /dev/null +++ b/tools/perf/util/ui/util.h @@ -0,0 +1,10 @@ +#ifndef _PERF_UI_UTIL_H_ +#define _PERF_UI_UTIL_H_ 1 + +#include + +int ui__popup_menu(int argc, char * const argv[]); +int ui__help_window(const char *text); +bool ui__dialog_yesno(const char *msg); + +#endif /* _PERF_UI_UTIL_H_ */ -- cgit v0.10.2 From 92221162875ec48913d3f9710046e48d599c9cf2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 9 Aug 2010 15:30:40 -0300 Subject: perf annotate: Sort by hottest lines in the TUI Right now it will just sort and position at the hottest line, i.e. the one where more samples were taken. It will be at the center of the screen and later TAB/shift-TAB will cycle thru the hottest lines. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index fd20670..1478dc6 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -285,7 +285,7 @@ static int hist_entry__tty_annotate(struct hist_entry *he) LIST_HEAD(head); struct objdump_line *pos, *n; - if (hist_entry__annotate(he, &head) < 0) + if (hist_entry__annotate(he, &head, 0) < 0) return -1; if (full_paths) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 62ec9b0..be22ae6 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -983,9 +983,9 @@ int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) return 0; } -static struct objdump_line *objdump_line__new(s64 offset, char *line) +static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) { - struct objdump_line *self = malloc(sizeof(*self)); + struct objdump_line *self = malloc(sizeof(*self) + privsize); if (self != NULL) { self->offset = offset; @@ -1017,7 +1017,7 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head, } static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, - struct list_head *head) + struct list_head *head, size_t privsize) { struct symbol *sym = self->ms.sym; struct objdump_line *objdump_line; @@ -1068,7 +1068,7 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, offset = -1; } - objdump_line = objdump_line__new(offset, line); + objdump_line = objdump_line__new(offset, line, privsize); if (objdump_line == NULL) { free(line); return -1; @@ -1078,7 +1078,8 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, return 0; } -int hist_entry__annotate(struct hist_entry *self, struct list_head *head) +int hist_entry__annotate(struct hist_entry *self, struct list_head *head, + size_t privsize) { struct symbol *sym = self->ms.sym; struct map *map = self->ms.map; @@ -1143,7 +1144,7 @@ fallback: goto out_free_filename; while (!feof(file)) - if (hist_entry__parse_objdump_line(self, file, head) < 0) + if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0) break; pclose(file); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 65a48db..587d375 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -101,7 +101,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, bool show_displacement, FILE *fp); int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); -int hist_entry__annotate(struct hist_entry *self, struct list_head *head); +int hist_entry__annotate(struct hist_entry *self, struct list_head *head, + size_t privsize); void hists__filter_by_dso(struct hists *self, const struct dso *dso); void hists__filter_by_thread(struct hists *self, const struct thread *thread); diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 5b01df6..763592b 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c @@ -14,6 +14,23 @@ static void ui__error_window(const char *fmt, ...) va_end(ap); } +struct annotate_browser { + struct ui_browser b; + struct rb_root entries; +}; + +struct objdump_line_rb_node { + struct rb_node rb_node; + double percent; + u32 idx; +}; + +static inline +struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self) +{ + return (struct objdump_line_rb_node *)(self + 1); +} + static void annotate_browser__write(struct ui_browser *self, void *entry, int row) { struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); @@ -21,17 +38,41 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro int width = self->width; if (ol->offset != -1) { - struct hist_entry *he = self->priv; - struct symbol *sym = he->ms.sym; - int len = he->ms.sym->end - he->ms.sym->start; + struct objdump_line_rb_node *olrb = objdump_line__rb(ol); + int color = ui_browser__percent_color(olrb->percent, current_entry); + SLsmg_set_color(color); + slsmg_printf(" %7.2f ", olrb->percent); + if (!current_entry) + SLsmg_set_color(HE_COLORSET_CODE); + } else { + int color = ui_browser__percent_color(0, current_entry); + SLsmg_set_color(color); + slsmg_write_nstring(" ", 9); + } + + SLsmg_write_char(':'); + slsmg_write_nstring(" ", 8); + if (!*ol->line) + slsmg_write_nstring(" ", width - 18); + else + slsmg_write_nstring(ol->line, width - 18); +} + +static double objdump_line__calc_percent(struct objdump_line *self, + struct list_head *head, + struct symbol *sym) +{ + double percent = 0.0; + + if (self->offset != -1) { + int len = sym->end - sym->start; unsigned int hits = 0; - double percent = 0.0; - int color; struct sym_priv *priv = symbol__priv(sym); struct sym_ext *sym_ext = priv->ext; struct sym_hist *h = priv->hist; - s64 offset = ol->offset; - struct objdump_line *next = objdump__get_next_ip_line(self->entries, ol); + s64 offset = self->offset; + struct objdump_line *next = objdump__get_next_ip_line(head, self); + while (offset < (s64)len && (next == NULL || offset < next->offset)) { @@ -45,37 +86,45 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro 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 (!*ol->line) - slsmg_write_nstring(" ", width - 18); - else - slsmg_write_nstring(ol->line, width - 18); + 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); } int hist_entry__tui_annotate(struct hist_entry *self) { struct newtExitStruct es; struct objdump_line *pos, *n; + struct objdump_line_rb_node *rbpos; + struct rb_node *nd; LIST_HEAD(head); - struct ui_browser browser = { - .entries = &head, - .refresh = ui_browser__list_head_refresh, - .seek = ui_browser__list_head_seek, - .write = annotate_browser__write, - .priv = self, + struct annotate_browser browser = { + .b = { + .entries = &head, + .refresh = ui_browser__list_head_refresh, + .seek = ui_browser__list_head_seek, + .write = annotate_browser__write, + .priv = self, + }, }; int ret; @@ -85,7 +134,7 @@ int hist_entry__tui_annotate(struct hist_entry *self) if (self->ms.map->dso->annotate_warned) return -1; - if (hist_entry__annotate(self, &head) < 0) { + if (hist_entry__annotate(self, &head, sizeof(*rbpos)) < 0) { ui__error_window(ui_helpline__last_msg); return -1; } @@ -94,16 +143,44 @@ int hist_entry__tui_annotate(struct hist_entry *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; + if (browser.b.width < line_len) + browser.b.width = line_len; + rbpos = objdump_line__rb(pos); + rbpos->idx = browser.b.nr_entries++; + rbpos->percent = objdump_line__calc_percent(pos, &head, self->ms.sym); + if (rbpos->percent < 0.01) + continue; + objdump__insert_line(&browser.entries, rbpos); + } + + /* + * Position the browser at the hottest line. + */ + nd = rb_last(&browser.entries); + if (nd != NULL) { + unsigned back; + + ui_browser__refresh_dimensions(&browser.b); + back = browser.b.height / 2; + rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node); + pos = ((struct objdump_line *)rbpos) - 1; + browser.b.top_idx = browser.b.index = rbpos->idx; + + while (browser.b.top_idx != 0 && back != 0) { + pos = list_entry(pos->node.prev, struct objdump_line, node); + + --browser.b.top_idx; + --back; + } + + browser.b.top = pos; } - browser.width += 18; /* Percentage */ - ui_browser__show(&browser, self->ms.sym->name); - newtFormAddHotKey(browser.form, ' '); - ret = ui_browser__run(&browser, &es); - newtFormDestroy(browser.form); + browser.b.width += 18; /* Percentage */ + ui_browser__show(&browser.b, self->ms.sym->name); + newtFormAddHotKey(browser.b.form, ' '); + ret = ui_browser__run(&browser.b, &es); + newtFormDestroy(browser.b.form); newtPopWindow(); list_for_each_entry_safe(pos, n, &head, node) { list_del(&pos->node); -- cgit v0.10.2 From 9e22d6377ce6f31b1cc0bff16daeda2780495061 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Aug 2010 15:09:02 -0300 Subject: perf ui: Make SPACE work as PGDN in all browsers Not just on the annotate one. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index edbb7dd..83d5748 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c @@ -169,6 +169,7 @@ int ui_browser__show(struct ui_browser *self, const char *title) newtFormAddHotKey(self->form, NEWT_KEY_PGDN); newtFormAddHotKey(self->form, NEWT_KEY_HOME); newtFormAddHotKey(self->form, NEWT_KEY_END); + newtFormAddHotKey(self->form, ' '); newtFormAddComponent(self->form, self->sb); return 0; } diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 763592b..d2156ae 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c @@ -178,7 +178,6 @@ int hist_entry__tui_annotate(struct hist_entry *self) browser.b.width += 18; /* Percentage */ ui_browser__show(&browser.b, self->ms.sym->name); - newtFormAddHotKey(browser.b.form, ' '); ret = ui_browser__run(&browser.b, &es); newtFormDestroy(browser.b.form); newtPopWindow(); -- cgit v0.10.2 From f1e9214cc99644101d957c5c660946c6f2f86d7c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Aug 2010 15:14:53 -0300 Subject: perf annotate: Cycle thru sorted lines with samples The annotate TUI now starts centered on the line with most samples, i.e. the hottest line in the annotated function. Pressing TAB will center on the second hottest function and so on. Shift+TAB goes in the other direction. This way one can more easily sift thru the function hotspots. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index d2156ae..73e78ef 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c @@ -17,6 +17,7 @@ static void ui__error_window(const char *fmt, ...) struct annotate_browser { struct ui_browser b; struct rb_root entries; + struct rb_node *curr_hot; }; struct objdump_line_rb_node { @@ -110,12 +111,83 @@ static void objdump__insert_line(struct rb_root *self, rb_insert_color(&line->rb_node, self); } +static void annotate_browser__set_top(struct annotate_browser *self, + struct rb_node *nd) +{ + struct objdump_line_rb_node *rbpos; + struct objdump_line *pos; + unsigned back; + + ui_browser__refresh_dimensions(&self->b); + back = self->b.height / 2; + rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node); + pos = ((struct objdump_line *)rbpos) - 1; + self->b.top_idx = self->b.index = rbpos->idx; + + while (self->b.top_idx != 0 && back != 0) { + pos = list_entry(pos->node.prev, struct objdump_line, node); + + --self->b.top_idx; + --back; + } + + self->b.top = pos; + self->curr_hot = nd; +} + +static int annotate_browser__run(struct annotate_browser *self, + struct newtExitStruct *es) +{ + struct rb_node *nd; + struct hist_entry *he = self->b.priv; + + if (ui_browser__show(&self->b, he->ms.sym->name) < 0) + return -1; + + ui_helpline__fpush("<- or ESC: exit, TAB/shift+TAB: cycle thru samples"); + newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); + + nd = self->curr_hot; + if (nd) { + newtFormAddHotKey(self->b.form, NEWT_KEY_TAB); + newtFormAddHotKey(self->b.form, NEWT_KEY_UNTAB); + } + + while (1) { + ui_browser__run(&self->b, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + + switch (es->u.key) { + case NEWT_KEY_TAB: + nd = rb_prev(nd); + if (nd == NULL) + nd = rb_last(&self->entries); + annotate_browser__set_top(self, nd); + break; + case NEWT_KEY_UNTAB: + nd = rb_next(nd); + if (nd == NULL) + nd = rb_first(&self->entries); + annotate_browser__set_top(self, nd); + break; + default: + goto out; + } + } +out: + newtFormDestroy(self->b.form); + newtPopWindow(); + ui_helpline__pop(); + return 0; +} + int hist_entry__tui_annotate(struct hist_entry *self) { struct newtExitStruct es; struct objdump_line *pos, *n; struct objdump_line_rb_node *rbpos; - struct rb_node *nd; LIST_HEAD(head); struct annotate_browser browser = { .b = { @@ -156,35 +228,15 @@ int hist_entry__tui_annotate(struct hist_entry *self) /* * Position the browser at the hottest line. */ - nd = rb_last(&browser.entries); - if (nd != NULL) { - unsigned back; - - ui_browser__refresh_dimensions(&browser.b); - back = browser.b.height / 2; - rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node); - pos = ((struct objdump_line *)rbpos) - 1; - browser.b.top_idx = browser.b.index = rbpos->idx; - - while (browser.b.top_idx != 0 && back != 0) { - pos = list_entry(pos->node.prev, struct objdump_line, node); - - --browser.b.top_idx; - --back; - } - - browser.b.top = pos; - } + browser.curr_hot = rb_last(&browser.entries); + if (browser.curr_hot) + annotate_browser__set_top(&browser, browser.curr_hot); browser.b.width += 18; /* Percentage */ - ui_browser__show(&browser.b, self->ms.sym->name); - ret = ui_browser__run(&browser.b, &es); - newtFormDestroy(browser.b.form); - newtPopWindow(); + ret = annotate_browser__run(&browser, &es); list_for_each_entry_safe(pos, n, &head, node) { list_del(&pos->node); objdump_line__free(pos); } - ui_helpline__pop(); return ret; } -- cgit v0.10.2 From 59e8fe32fc0cc9dff6b0c269d099a49e004dc45e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Aug 2010 15:44:20 -0300 Subject: perf ui browser: Add ui_browser__show counterpart: __hide So that the common tasks of providing a helpline at __run entry and destroying the window and releasing resourses at exit can be abstracted away, reducing a bit more the coupling with libnewt. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index 83d5748..66f2d58 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c @@ -16,6 +16,7 @@ #include #include #include "browser.h" +#include "helpline.h" #include "../color.h" #include "../util.h" @@ -145,8 +146,11 @@ void ui_browser__reset_index(struct ui_browser *self) self->seek(self, 0, SEEK_SET); } -int ui_browser__show(struct ui_browser *self, const char *title) +int ui_browser__show(struct ui_browser *self, const char *title, + const char *helpline, ...) { + va_list ap; + if (self->form != NULL) { newtFormDestroy(self->form); newtPopWindow(); @@ -171,9 +175,21 @@ int ui_browser__show(struct ui_browser *self, const char *title) newtFormAddHotKey(self->form, NEWT_KEY_END); newtFormAddHotKey(self->form, ' '); newtFormAddComponent(self->form, self->sb); + + va_start(ap, helpline); + ui_helpline__vpush(helpline, ap); + va_end(ap); return 0; } +void ui_browser__hide(struct ui_browser *self) +{ + newtFormDestroy(self->form); + newtPopWindow(); + self->form = NULL; + ui_helpline__pop(); +} + int ui_browser__refresh(struct ui_browser *self) { int row; diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index 856e343..0b9f829 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h @@ -30,7 +30,9 @@ bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row); void ui_browser__refresh_dimensions(struct ui_browser *self); void ui_browser__reset_index(struct ui_browser *self); -int ui_browser__show(struct ui_browser *self, const char *title); +int ui_browser__show(struct ui_browser *self, const char *title, + const char *helpline, ...); +void ui_browser__hide(struct ui_browser *self); int ui_browser__refresh(struct ui_browser *self); int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es); diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 73e78ef..55ff792 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c @@ -141,10 +141,10 @@ static int annotate_browser__run(struct annotate_browser *self, struct rb_node *nd; struct hist_entry *he = self->b.priv; - if (ui_browser__show(&self->b, he->ms.sym->name) < 0) + if (ui_browser__show(&self->b, he->ms.sym->name, + "<- or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0) return -1; - ui_helpline__fpush("<- or ESC: exit, TAB/shift+TAB: cycle thru samples"); newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); nd = self->curr_hot; @@ -177,9 +177,7 @@ static int annotate_browser__run(struct annotate_browser *self, } } out: - newtFormDestroy(self->b.form); - newtPopWindow(); - ui_helpline__pop(); + ui_browser__hide(&self->b); return 0; } diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index cee7998..dd512b7 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c @@ -211,7 +211,8 @@ static int hist_browser__run(struct hist_browser *self, const char *title, nr_events, unit); newtDrawRootText(0, 0, str); - if (ui_browser__show(&self->b, title) < 0) + if (ui_browser__show(&self->b, title, + "Press '?' for help on key bindings") < 0) return -1; newtFormAddHotKey(self->b.form, 'A'); @@ -253,6 +254,8 @@ static int hist_browser__run(struct hist_browser *self, const char *title, return 0; } } + + ui_browser__hide(&self->b); return 0; } diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c index b79f0c9..142b825 100644 --- a/tools/perf/util/ui/browsers/map.c +++ b/tools/perf/util/ui/browsers/map.c @@ -100,11 +100,11 @@ static int map_browser__search(struct map_browser *self) static int map_browser__run(struct map_browser *self, struct newtExitStruct *es) { - if (ui_browser__show(&self->b, self->map->dso->long_name) < 0) + if (ui_browser__show(&self->b, self->map->dso->long_name, + "Press <- or ESC to exit, %s / to search", + verbose ? "" : "restart with -v to use") < 0) return -1; - ui_helpline__fpush("Press <- or ESC to exit, %s / to search", - verbose ? "" : "restart with -v to use"); newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); if (verbose) @@ -121,9 +121,7 @@ static int map_browser__run(struct map_browser *self, struct newtExitStruct *es) break; } - newtFormDestroy(self->b.form); - newtPopWindow(); - ui_helpline__pop(); + ui_browser__hide(&self->b); return 0; } diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c index ff58460..8d79daa 100644 --- a/tools/perf/util/ui/helpline.c +++ b/tools/perf/util/ui/helpline.c @@ -16,7 +16,7 @@ void ui_helpline__push(const char *msg) newtPushHelpLine(msg); } -static void ui_helpline__vpush(const char *fmt, va_list ap) +void ui_helpline__vpush(const char *fmt, va_list ap) { char *s; diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h index 5d1e5e7..ab6028d 100644 --- a/tools/perf/util/ui/helpline.h +++ b/tools/perf/util/ui/helpline.h @@ -4,6 +4,7 @@ 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); -- cgit v0.10.2 From 4694153c252a6ae19704b5bb66466050256395a4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Aug 2010 15:50:07 -0300 Subject: perf ui hist browser: Fixup key bindings To match what is shown when '?' or 'H' is pressed, i.e. the keybind help window. Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index dd512b7..dafdf67 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c @@ -215,12 +215,12 @@ static int hist_browser__run(struct hist_browser *self, const char *title, "Press '?' for help on key bindings") < 0) return -1; - newtFormAddHotKey(self->b.form, 'A'); newtFormAddHotKey(self->b.form, 'a'); newtFormAddHotKey(self->b.form, '?'); newtFormAddHotKey(self->b.form, 'h'); - newtFormAddHotKey(self->b.form, 'H'); newtFormAddHotKey(self->b.form, 'd'); + newtFormAddHotKey(self->b.form, 'D'); + newtFormAddHotKey(self->b.form, 't'); newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); @@ -232,7 +232,7 @@ static int hist_browser__run(struct hist_browser *self, const char *title, if (es->reason != NEWT_EXIT_HOTKEY) break; switch (es->u.key) { - case 'd': { /* Debug */ + case 'D': { /* Debug */ static int seq; struct hist_entry *h = rb_entry(self->b.top, struct hist_entry, rb_node); @@ -771,18 +771,17 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) default:; } - key = toupper(key); switch (key) { - case 'A': + case 'a': if (browser->selection->map == NULL && browser->selection->map->dso->annotate_warned) continue; goto do_annotate; - case 'D': + case 'd': goto zoom_dso; - case 'T': + case 't': goto zoom_thread; - case 'H': + case 'h': case '?': do_help: ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" -- cgit v0.10.2 From 88d89da64951377962334b684634cfc1468aa93f Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 12 Aug 2010 21:50:00 +0200 Subject: perf: Add back list_head data types This commit: de5d9bf: Move list types from to . Moved the list head data types out of list.h, breaking the build. Add them to the perf types.h as well. Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Frederic Weisbecker Cc: Paul Mackerras Cc: Steven Rostedt LKML-Reference: Signed-off-by: Ingo Molnar diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h index 196862a..12de3b8 100644 --- a/tools/perf/util/include/linux/types.h +++ b/tools/perf/util/include/linux/types.h @@ -6,4 +6,16 @@ #define DECLARE_BITMAP(name,bits) \ unsigned long name[BITS_TO_LONGS(bits)] +struct list_head { + struct list_head *next, *prev; +}; + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + #endif -- cgit v0.10.2