diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 0ef5cfe52f2d1c33399567b1ecc5e1522676e786..9c4dc30cdc13bba5ed018be4ddaed077cadd4e1a 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -506,8 +506,8 @@ PERFLIBS = $(LIB_FILE) -include config.mak ifndef NO_DWARF -ifneq ($(shell sh -c "(echo '\#include '; echo '\#include '; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) - msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/libdw-dev); +ifneq ($(shell sh -c "(echo '\#include '; echo '\#include '; echo '\#include '; echo '\#ifndef _ELFUTILS_PREREQ'; echo '\#error'; echo '\#endif'; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) + msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); NO_DWARF := 1 endif # Dwarf support endif # NO_DWARF @@ -560,6 +560,8 @@ ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { newtIni msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); BASIC_CFLAGS += -DNO_NEWT_SUPPORT else + # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h + BASIC_CFLAGS += -I/usr/include/slang EXTLIBS += -lnewt LIB_OBJS += $(OUTPUT)util/newt.o endif @@ -592,6 +594,9 @@ endif ifdef NO_DEMANGLE BASIC_CFLAGS += -DNO_DEMANGLE +else ifdef HAVE_CPLUS_DEMANGLE + EXTLIBS += -liberty + BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE else has_bfd := $(shell sh -c "(echo '\#include '; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd "$(QUIET_STDERR)" && echo y") @@ -945,6 +950,9 @@ $(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/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/builtin-annotate.c b/tools/perf/builtin-annotate.c index 3940964161b388574916283d5eef4f9b91d8f4b5..fd1b786c8f35a510fd3b82cfa3584bcca0a570f5 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -34,68 +34,8 @@ static bool full_paths; static bool print_line; -struct sym_hist { - u64 sum; - u64 ip[0]; -}; - -struct sym_ext { - struct rb_node node; - double percent; - char *path; -}; - -struct sym_priv { - struct sym_hist *hist; - struct sym_ext *ext; -}; - static const char *sym_hist_filter; -static int sym__alloc_hist(struct symbol *self) -{ - struct sym_priv *priv = symbol__priv(self); - const int size = (sizeof(*priv->hist) + - (self->end - self->start) * sizeof(u64)); - - priv->hist = zalloc(size); - return priv->hist == NULL ? -1 : 0; -} - -/* - * collect histogram counts - */ -static int annotate__hist_hit(struct hist_entry *he, u64 ip) -{ - unsigned int sym_size, offset; - struct symbol *sym = he->ms.sym; - struct sym_priv *priv; - struct sym_hist *h; - - if (!sym || !he->ms.map) - return 0; - - priv = symbol__priv(sym); - if (priv->hist == NULL && sym__alloc_hist(sym) < 0) - return -ENOMEM; - - sym_size = sym->end - sym->start; - offset = ip - sym->start; - - pr_debug3("%s: ip=%#Lx\n", __func__, he->ms.map->unmap_ip(he->ms.map, ip)); - - if (offset >= sym_size) - return 0; - - h = priv->hist; - h->sum++; - h->ip[offset]++; - - pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->ms.sym->start, - he->ms.sym->name, ip, ip - he->ms.sym->start, h->ip[offset]); - return 0; -} - static int hists__add_entry(struct hists *self, struct addr_location *al) { struct hist_entry *he; @@ -115,7 +55,7 @@ static int hists__add_entry(struct hists *self, struct addr_location *al) if (he == NULL) return -ENOMEM; - return annotate__hist_hit(he, al->addr); + return hist_entry__inc_addr_samples(he, al->addr); } static int process_sample_event(event_t *event, struct perf_session *session) @@ -140,101 +80,6 @@ static int process_sample_event(event_t *event, struct perf_session *session) return 0; } -struct objdump_line { - struct list_head node; - s64 offset; - char *line; -}; - -static struct objdump_line *objdump_line__new(s64 offset, char *line) -{ - struct objdump_line *self = malloc(sizeof(*self)); - - if (self != NULL) { - self->offset = offset; - self->line = line; - } - - return self; -} - -static void objdump_line__free(struct objdump_line *self) -{ - free(self->line); - free(self); -} - -static void objdump__add_line(struct list_head *head, struct objdump_line *line) -{ - list_add_tail(&line->node, head); -} - -static struct objdump_line *objdump__get_next_ip_line(struct list_head *head, - struct objdump_line *pos) -{ - list_for_each_entry_continue(pos, head, node) - if (pos->offset >= 0) - return pos; - - return NULL; -} - -static int parse_line(FILE *file, struct hist_entry *he, - struct list_head *head) -{ - struct symbol *sym = he->ms.sym; - struct objdump_line *objdump_line; - char *line = NULL, *tmp, *tmp2; - size_t line_len; - s64 line_ip, offset = -1; - char *c; - - if (getline(&line, &line_len, file) < 0) - return -1; - - if (!line) - return -1; - - c = strchr(line, '\n'); - if (c) - *c = 0; - - line_ip = -1; - - /* - * Strip leading spaces: - */ - tmp = line; - while (*tmp) { - if (*tmp != ' ') - break; - tmp++; - } - - if (*tmp) { - /* - * Parse hexa addresses followed by ':' - */ - line_ip = strtoull(tmp, &tmp2, 16); - if (*tmp2 != ':') - line_ip = -1; - } - - if (line_ip != -1) { - u64 start = map__rip_2objdump(he->ms.map, sym->start); - offset = line_ip - start; - } - - objdump_line = objdump_line__new(offset, line); - if (objdump_line == NULL) { - free(line); - return -1; - } - objdump__add_line(head, objdump_line); - - return 0; -} - static int objdump_line__print(struct objdump_line *self, struct list_head *head, struct hist_entry *he, u64 len) @@ -439,27 +284,11 @@ static void annotate_sym(struct hist_entry *he) struct symbol *sym = he->ms.sym; const char *filename = dso->long_name, *d_filename; u64 len; - char command[PATH_MAX*2]; - FILE *file; LIST_HEAD(head); struct objdump_line *pos, *n; - if (!filename) - return; - - if (dso->origin == DSO__ORIG_KERNEL) { - if (dso->annotate_warned) - return; - dso->annotate_warned = 1; - pr_err("Can't annotate %s: No vmlinux file was found in the " - "path:\n", sym->name); - vmlinux_path__fprintf(stderr); + if (hist_entry__annotate(he, &head) < 0) return; - } - - pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, - filename, sym->name, map->unmap_ip(map, sym->start), - map->unmap_ip(map, sym->end)); if (full_paths) d_filename = filename; @@ -477,29 +306,6 @@ static void annotate_sym(struct hist_entry *he) printf(" Percent | Source code & Disassembly of %s\n", d_filename); printf("------------------------------------------------\n"); - if (verbose >= 2) - printf("annotating [%p] %30s : [%p] %30s\n", - dso, dso->long_name, sym, sym->name); - - sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s", - map__rip_2objdump(map, sym->start), - map__rip_2objdump(map, sym->end), - filename, filename); - - if (verbose >= 3) - printf("doing: %s\n", command); - - file = popen(command, "r"); - if (!file) - return; - - while (!feof(file)) { - if (parse_line(file, he, &head) < 0) - break; - } - - pclose(file); - if (verbose) hist_entry__print_hits(he); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index d7c75291e788fa41f51c111674fef0dbaf8869a4..04de3387de3ff20b4177b94c9ca3cadef9899a39 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -106,8 +106,18 @@ static int perf_session__add_hist_entry(struct perf_session *self, if (he == NULL) goto out_free_syms; err = 0; - if (symbol_conf.use_callchain) + if (symbol_conf.use_callchain) { err = append_chain(he->callchain, data->callchain, syms); + if (err) + goto out_free_syms; + } + /* + * Only in the newt browser we are doing integrated annotation, + * so we don't allocated the extra space needed because the stdio + * code will not use it. + */ + if (use_browser) + err = hist_entry__inc_addr_samples(he, al->addr); out_free_syms: free(syms); return err; @@ -301,10 +311,7 @@ static int __cmd_report(void) hists__collapse_resort(hists); hists__output_resort(hists); if (use_browser) - perf_session__browse_hists(&hists->entries, - hists->nr_entries, - hists->stats.total, help, - input_name); + hists__browse(hists, help, input_name); else { if (rb_first(&session->hists.entries) == rb_last(&session->hists.entries)) @@ -461,6 +468,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) if (strcmp(input_name, "-") != 0) setup_browser(); + /* + * Only in the newt browser we are doing integrated annotation, + * so don't allocate extra space that won't be used in the stdio + * implementation. + */ + if (use_browser) + symbol_conf.priv_size = sizeof(struct sym_priv); if (symbol__init() < 0) return -1; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index e34fd248067d5bb351c6356c62eb0ca0b789362b..451d2e45d843d01b35c369b50129d6db223c2585 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -784,3 +784,246 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, return ret; } + +enum hist_filter { + HIST_FILTER__DSO, + HIST_FILTER__THREAD, +}; + +void hists__filter_by_dso(struct hists *self, const struct dso *dso) +{ + struct rb_node *nd; + + self->nr_entries = self->stats.total = 0; + self->max_sym_namelen = 0; + + for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (symbol_conf.exclude_other && !h->parent) + continue; + + if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { + h->filtered |= (1 << HIST_FILTER__DSO); + continue; + } + + h->filtered &= ~(1 << HIST_FILTER__DSO); + if (!h->filtered) { + ++self->nr_entries; + self->stats.total += h->count; + if (h->ms.sym && + self->max_sym_namelen < h->ms.sym->namelen) + self->max_sym_namelen = h->ms.sym->namelen; + } + } +} + +void hists__filter_by_thread(struct hists *self, const struct thread *thread) +{ + struct rb_node *nd; + + self->nr_entries = self->stats.total = 0; + self->max_sym_namelen = 0; + + for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (thread != NULL && h->thread != thread) { + h->filtered |= (1 << HIST_FILTER__THREAD); + continue; + } + h->filtered &= ~(1 << HIST_FILTER__THREAD); + if (!h->filtered) { + ++self->nr_entries; + self->stats.total += h->count; + if (h->ms.sym && + self->max_sym_namelen < h->ms.sym->namelen) + self->max_sym_namelen = h->ms.sym->namelen; + } + } +} + +static int symbol__alloc_hist(struct symbol *self) +{ + struct sym_priv *priv = symbol__priv(self); + const int size = (sizeof(*priv->hist) + + (self->end - self->start) * sizeof(u64)); + + priv->hist = zalloc(size); + return priv->hist == NULL ? -1 : 0; +} + +int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) +{ + unsigned int sym_size, offset; + struct symbol *sym = self->ms.sym; + struct sym_priv *priv; + struct sym_hist *h; + + if (!sym || !self->ms.map) + return 0; + + priv = symbol__priv(sym); + if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) + return -ENOMEM; + + sym_size = sym->end - sym->start; + offset = ip - sym->start; + + pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); + + if (offset >= sym_size) + return 0; + + h = priv->hist; + h->sum++; + h->ip[offset]++; + + pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, + self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); + return 0; +} + +static struct objdump_line *objdump_line__new(s64 offset, char *line) +{ + struct objdump_line *self = malloc(sizeof(*self)); + + if (self != NULL) { + self->offset = offset; + self->line = line; + } + + return self; +} + +void objdump_line__free(struct objdump_line *self) +{ + free(self->line); + free(self); +} + +static void objdump__add_line(struct list_head *head, struct objdump_line *line) +{ + list_add_tail(&line->node, head); +} + +struct objdump_line *objdump__get_next_ip_line(struct list_head *head, + struct objdump_line *pos) +{ + list_for_each_entry_continue(pos, head, node) + if (pos->offset >= 0) + return pos; + + return NULL; +} + +static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, + struct list_head *head) +{ + struct symbol *sym = self->ms.sym; + struct objdump_line *objdump_line; + char *line = NULL, *tmp, *tmp2, *c; + size_t line_len; + s64 line_ip, offset = -1; + + if (getline(&line, &line_len, file) < 0) + return -1; + + if (!line) + return -1; + + while (line_len != 0 && isspace(line[line_len - 1])) + line[--line_len] = '\0'; + + c = strchr(line, '\n'); + if (c) + *c = 0; + + line_ip = -1; + + /* + * Strip leading spaces: + */ + tmp = line; + while (*tmp) { + if (*tmp != ' ') + break; + tmp++; + } + + if (*tmp) { + /* + * Parse hexa addresses followed by ':' + */ + line_ip = strtoull(tmp, &tmp2, 16); + if (*tmp2 != ':') + line_ip = -1; + } + + if (line_ip != -1) { + u64 start = map__rip_2objdump(self->ms.map, sym->start); + offset = line_ip - start; + } + + objdump_line = objdump_line__new(offset, line); + if (objdump_line == NULL) { + free(line); + return -1; + } + objdump__add_line(head, objdump_line); + + return 0; +} + +int hist_entry__annotate(struct hist_entry *self, struct list_head *head) +{ + struct symbol *sym = self->ms.sym; + struct map *map = self->ms.map; + struct dso *dso = map->dso; + const char *filename = dso->long_name; + char command[PATH_MAX * 2]; + FILE *file; + u64 len; + + if (!filename) + return -1; + + if (dso->origin == DSO__ORIG_KERNEL) { + if (dso->annotate_warned) + return 0; + dso->annotate_warned = 1; + pr_err("Can't annotate %s: No vmlinux file was found in the " + "path:\n", sym->name); + vmlinux_path__fprintf(stderr); + return -1; + } + + pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, + filename, sym->name, map->unmap_ip(map, sym->start), + map->unmap_ip(map, sym->end)); + + len = sym->end - sym->start; + + pr_debug("annotating [%p] %30s : [%p] %30s\n", + dso, dso->long_name, sym, sym->name); + + snprintf(command, sizeof(command), + "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", + map__rip_2objdump(map, sym->start), + map__rip_2objdump(map, sym->end), + filename, filename); + + pr_debug("Executing: %s\n", command); + + file = popen(command, "r"); + if (!file) + return -1; + + while (!feof(file)) + if (hist_entry__parse_objdump_line(self, file, head) < 0) + break; + + pclose(file); + return 0; +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1b18d04195dcab69a441b66f9a9ba30e2b7ce287..ed9c06734965f54d2224b8dce91aac217e7a0505 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -11,6 +11,32 @@ struct addr_location; struct symbol; struct rb_root; +struct objdump_line { + struct list_head node; + s64 offset; + char *line; +}; + +void objdump_line__free(struct objdump_line *self); +struct objdump_line *objdump__get_next_ip_line(struct list_head *head, + struct objdump_line *pos); + +struct sym_hist { + u64 sum; + u64 ip[0]; +}; + +struct sym_ext { + struct rb_node node; + double percent; + char *path; +}; + +struct sym_priv { + struct sym_hist *hist; + struct sym_ext *ext; +}; + struct events_stats { u64 total; u64 lost; @@ -44,4 +70,22 @@ void hists__output_resort(struct hists *self); void hists__collapse_resort(struct hists *self); 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); + +void hists__filter_by_dso(struct hists *self, const struct dso *dso); +void hists__filter_by_thread(struct hists *self, const struct thread *thread); + +#ifdef NO_NEWT_SUPPORT +static inline int hists__browse(struct hists self __used, + const char *helpline __used, + const char *input_name __used) +{ + return 0; +} +#else +int hists__browse(struct hists *self, const char *helpline, + const char *input_name); +#endif #endif /* __PERF_HIST_H */ diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index e283a6e6b6e051956575926ef824bdd76f8360fa..ba6acd04c082fac6be1feff82a210a10ad94cf84 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -2,6 +2,7 @@ #include #undef _GNU_SOURCE +#include #include #include #include @@ -57,6 +58,43 @@ 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 char browser__last_msg[1024]; int browser__show_help(const char *format, va_list ap) @@ -69,8 +107,7 @@ int browser__show_help(const char *format, va_list ap) backlog += ret; if (browser__last_msg[backlog - 1] == '\n') { - newtPopHelpLine(); - newtPushHelpLine(browser__last_msg); + ui_helpline__puts(browser__last_msg); newtRefresh(); backlog = 0; } @@ -135,6 +172,254 @@ static bool dialog_yesno(const char *msg) return newtWinChoice(NULL, yes, no, (char *)msg) == 1; } +#define HE_COLORSET_TOP 50 +#define HE_COLORSET_MEDIUM 51 +#define HE_COLORSET_NORMAL 52 +#define HE_COLORSET_SELECTED 53 +#define HE_COLORSET_CODE 54 + +static int ui_browser__percent_color(double percent, bool current) +{ + if (current) + return HE_COLORSET_SELECTED; + if (percent >= MIN_RED) + return HE_COLORSET_TOP; + if (percent >= MIN_GREEN) + return HE_COLORSET_MEDIUM; + return HE_COLORSET_NORMAL; +} + +struct ui_browser { + newtComponent form, sb; + u64 index, first_visible_entry_idx; + void *first_visible_entry, *entries; + u16 top, left, width, height; + void *priv; + u32 nr_entries; +}; + +static void ui_browser__refresh_dimensions(struct ui_browser *self) +{ + int cols, rows; + newtGetScreenSize(&cols, &rows); + + if (self->width > cols - 4) + self->width = cols - 4; + self->height = rows - 5; + if (self->height > self->nr_entries) + self->height = self->nr_entries; + self->top = (rows - self->height) / 2; + self->left = (cols - self->width) / 2; +} + +static void ui_browser__reset_index(struct ui_browser *self) +{ + self->index = self->first_visible_entry_idx = 0; + self->first_visible_entry = NULL; +} + +static int objdump_line__show(struct objdump_line *self, struct list_head *head, + int width, struct hist_entry *he, int len, + bool current_entry) +{ + if (self->offset != -1) { + struct symbol *sym = he->ms.sym; + unsigned int hits = 0; + double percent = 0.0; + int color; + struct sym_priv *priv = symbol__priv(sym); + struct sym_ext *sym_ext = priv->ext; + struct sym_hist *h = priv->hist; + s64 offset = self->offset; + struct objdump_line *next = objdump__get_next_ip_line(head, self); + + while (offset < (s64)len && + (next == NULL || offset < next->offset)) { + if (sym_ext) { + percent += sym_ext[offset].percent; + } else + hits += h->ip[offset]; + + ++offset; + } + + if (sym_ext == NULL && h->sum) + percent = 100.0 * hits / h->sum; + + color = ui_browser__percent_color(percent, current_entry); + SLsmg_set_color(color); + SLsmg_printf(" %7.2f ", percent); + if (!current_entry) + SLsmg_set_color(HE_COLORSET_CODE); + } else { + int color = ui_browser__percent_color(0, current_entry); + SLsmg_set_color(color); + SLsmg_write_nstring(" ", 9); + } + + SLsmg_write_char(':'); + SLsmg_write_nstring(" ", 8); + if (!*self->line) + SLsmg_write_nstring(" ", width - 18); + else + SLsmg_write_nstring(self->line, width - 18); + + return 0; +} + +static int ui_browser__refresh_entries(struct ui_browser *self) +{ + struct objdump_line *pos; + struct list_head *head = self->entries; + struct hist_entry *he = self->priv; + int row = 0; + int len = he->ms.sym->end - he->ms.sym->start; + + if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) + self->first_visible_entry = head->next; + + pos = list_entry(self->first_visible_entry, struct objdump_line, node); + + list_for_each_entry_from(pos, head, node) { + bool current_entry = (self->first_visible_entry_idx + row) == self->index; + SLsmg_gotorc(self->top + row, self->left); + objdump_line__show(pos, head, self->width, + he, len, current_entry); + if (++row == self->height) + break; + } + + SLsmg_set_color(HE_COLORSET_NORMAL); + SLsmg_fill_region(self->top + row, self->left, + self->height - row, self->width, ' '); + + return 0; +} + +static int ui_browser__run(struct ui_browser *self, const char *title, + struct newtExitStruct *es) +{ + if (self->form) { + newtFormDestroy(self->form); + newtPopWindow(); + } + + ui_browser__refresh_dimensions(self); + newtCenteredWindow(self->width + 2, self->height, title); + self->form = newt_form__new(); + if (self->form == NULL) + return -1; + + self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, + HE_COLORSET_NORMAL, + HE_COLORSET_SELECTED); + if (self->sb == NULL) + return -1; + + newtFormAddHotKey(self->form, NEWT_KEY_UP); + newtFormAddHotKey(self->form, NEWT_KEY_DOWN); + newtFormAddHotKey(self->form, NEWT_KEY_PGUP); + newtFormAddHotKey(self->form, NEWT_KEY_PGDN); + newtFormAddHotKey(self->form, NEWT_KEY_HOME); + newtFormAddHotKey(self->form, NEWT_KEY_END); + + if (ui_browser__refresh_entries(self) < 0) + return -1; + newtFormAddComponent(self->form, self->sb); + + while (1) { + unsigned int offset; + + newtFormRun(self->form, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + switch (es->u.key) { + case NEWT_KEY_DOWN: + if (self->index == self->nr_entries - 1) + break; + ++self->index; + if (self->index == self->first_visible_entry_idx + self->height) { + struct list_head *pos = self->first_visible_entry; + ++self->first_visible_entry_idx; + self->first_visible_entry = pos->next; + } + break; + case NEWT_KEY_UP: + if (self->index == 0) + break; + --self->index; + if (self->index < self->first_visible_entry_idx) { + struct list_head *pos = self->first_visible_entry; + --self->first_visible_entry_idx; + self->first_visible_entry = pos->prev; + } + break; + case NEWT_KEY_PGDN: + if (self->first_visible_entry_idx + self->height > self->nr_entries - 1) + break; + + offset = self->height; + if (self->index + offset > self->nr_entries - 1) + offset = self->nr_entries - 1 - self->index; + self->index += offset; + self->first_visible_entry_idx += offset; + + while (offset--) { + struct list_head *pos = self->first_visible_entry; + self->first_visible_entry = pos->next; + } + + break; + case NEWT_KEY_PGUP: + if (self->first_visible_entry_idx == 0) + break; + + if (self->first_visible_entry_idx < self->height) + offset = self->first_visible_entry_idx; + else + offset = self->height; + + self->index -= offset; + self->first_visible_entry_idx -= offset; + + while (offset--) { + struct list_head *pos = self->first_visible_entry; + self->first_visible_entry = pos->prev; + } + break; + case NEWT_KEY_HOME: + ui_browser__reset_index(self); + break; + case NEWT_KEY_END: { + struct list_head *head = self->entries; + offset = self->height - 1; + + if (offset > self->nr_entries) + offset = self->nr_entries; + + self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset; + self->first_visible_entry = head->prev; + while (offset-- != 0) { + struct list_head *pos = self->first_visible_entry; + self->first_visible_entry = pos->prev; + } + } + break; + case NEWT_KEY_ESCAPE: + case CTRL('c'): + case 'Q': + case 'q': + return 0; + default: + continue; + } + if (ui_browser__refresh_entries(self) < 0) + return -1; + } + return 0; +} + /* * When debugging newt problems it was useful to be able to "unroll" * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate @@ -317,62 +602,40 @@ static size_t hist_entry__append_browser(struct hist_entry *self, return ret; } -static void map_symbol__annotate_browser(const struct map_symbol *self, - const char *input_name) +static void hist_entry__annotate_browser(struct hist_entry *self) { - FILE *fp; - int cols, rows; - newtComponent form, tree; + struct ui_browser browser; struct newtExitStruct es; - char *str; - size_t line_len, max_line_len = 0; - size_t max_usable_width; - char *line = NULL; + struct objdump_line *pos, *n; + LIST_HEAD(head); - if (self->sym == NULL) + if (self->ms.sym == NULL) return; - if (asprintf(&str, "perf annotate -i \"%s\" -d \"%s\" %s 2>&1 | expand", - input_name, self->map->dso->name, self->sym->name) < 0) + if (hist_entry__annotate(self, &head) < 0) return; - fp = popen(str, "r"); - if (fp == NULL) - goto out_free_str; - - newtPushHelpLine("Press ESC to exit"); - newtGetScreenSize(&cols, &rows); - tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); - - while (!feof(fp)) { - if (getline(&line, &line_len, fp) < 0 || !line_len) - break; - while (line_len != 0 && isspace(line[line_len - 1])) - line[--line_len] = '\0'; + ui_helpline__push("Press ESC to exit"); - if (line_len > max_line_len) - max_line_len = line_len; - newtListboxAppendEntry(tree, line, NULL); + memset(&browser, 0, sizeof(browser)); + browser.entries = &head; + browser.priv = self; + list_for_each_entry(pos, &head, node) { + size_t line_len = strlen(pos->line); + if (browser.width < line_len) + browser.width = line_len; + ++browser.nr_entries; } - fclose(fp); - free(line); - max_usable_width = cols - 22; - if (max_line_len > max_usable_width) - max_line_len = max_usable_width; - - newtListboxSetWidth(tree, max_line_len); - - newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); - form = newt_form__new(); - newtFormAddComponent(form, tree); - - newtFormRun(form, &es); - newtFormDestroy(form); + browser.width += 18; /* Percentage */ + ui_browser__run(&browser, self->ms.sym->name, &es); + newtFormDestroy(browser.form); newtPopWindow(); - newtPopHelpLine(); -out_free_str: - free(str); + list_for_each_entry_safe(pos, n, &head, node) { + list_del(&pos->node); + objdump_line__free(pos); + } + ui_helpline__pop(); } static const void *newt__symbol_tree_get_current(newtComponent self) @@ -410,8 +673,8 @@ static void hist_browser__delete(struct hist_browser *self) free(self); } -static int hist_browser__populate(struct hist_browser *self, struct rb_root *hists, - u64 nr_hists, u64 session_total, const char *title) +static int hist_browser__populate(struct hist_browser *self, struct hists *hists, + const char *title) { int max_len = 0, idx, cols, rows; struct ui_progress *progress; @@ -426,7 +689,7 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his } snprintf(str, sizeof(str), "Samples: %Ld ", - session_total); + hists->stats.total); newtDrawRootText(0, 0, str); newtGetScreenSize(NULL, &rows); @@ -442,24 +705,25 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his newtComponentAddCallback(self->tree, hist_browser__selection, &self->selection); - progress = ui_progress__new("Adding entries to the browser...", nr_hists); + progress = ui_progress__new("Adding entries to the browser...", + hists->nr_entries); if (progress == NULL) return -1; idx = 0; - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { + for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); int len; if (h->filtered) continue; - len = hist_entry__append_browser(h, self->tree, session_total); + len = hist_entry__append_browser(h, self->tree, hists->stats.total); if (len > max_len) max_len = len; if (symbol_conf.use_callchain) hist_entry__append_callchain_browser(h, self->tree, - session_total, idx++); + hists->stats.total, idx++); ++curr_hist; if (curr_hist % 5) ui_progress__update(progress, curr_hist); @@ -490,58 +754,7 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his return 0; } -enum hist_filter { - HIST_FILTER__DSO, - HIST_FILTER__THREAD, -}; - -static u64 hists__filter_by_dso(struct rb_root *hists, const struct dso *dso, - u64 *session_total) -{ - struct rb_node *nd; - u64 nr_hists = 0; - - *session_total = 0; - - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - - if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { - h->filtered |= (1 << HIST_FILTER__DSO); - continue; - } - h->filtered &= ~(1 << HIST_FILTER__DSO); - ++nr_hists; - *session_total += h->count; - } - - return nr_hists; -} - -static u64 hists__filter_by_thread(struct rb_root *hists, const struct thread *thread, - u64 *session_total) -{ - struct rb_node *nd; - u64 nr_hists = 0; - - *session_total = 0; - - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - - if (thread != NULL && h->thread != thread) { - h->filtered |= (1 << HIST_FILTER__THREAD); - continue; - } - h->filtered &= ~(1 << HIST_FILTER__THREAD); - ++nr_hists; - *session_total += h->count; - } - - return nr_hists; -} - -static struct thread *hist_browser__selected_thread(struct hist_browser *self) +static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) { int *indexes; @@ -557,7 +770,13 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self) } return NULL; out: - return *(struct thread **)(self->selection + 1); + return container_of(self->selection, struct hist_entry, ms); +} + +static struct thread *hist_browser__selected_thread(struct hist_browser *self) +{ + struct hist_entry *he = hist_browser__selected_entry(self); + return he ? he->thread : NULL; } static int hist_browser__title(char *bf, size_t size, const char *input_name, @@ -577,9 +796,7 @@ static int hist_browser__title(char *bf, size_t size, const char *input_name, return printed ?: snprintf(bf, size, "Report: %s", input_name); } -int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline, - const char *input_name) +int hists__browse(struct hists *self, const char *helpline, const char *input_name) { struct hist_browser *browser = hist_browser__new(); const struct thread *thread_filter = NULL; @@ -591,11 +808,11 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, if (browser == NULL) return -1; - newtPushHelpLine(helpline); + ui_helpline__push(helpline); hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) + if (hist_browser__populate(browser, self, msg) < 0) goto out; while (1) { @@ -653,46 +870,48 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, continue; do_annotate: if (choice == annotate) { + struct hist_entry *he; + if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { - newtPopHelpLine(); - newtPushHelpLine("No vmlinux file found, can't " + ui_helpline__puts("No vmlinux file found, can't " "annotate with just a " "kallsyms file"); continue; } - map_symbol__annotate_browser(browser->selection, input_name); + + he = hist_browser__selected_entry(browser); + if (he == NULL) + continue; + + hist_entry__annotate_browser(he); } else if (choice == zoom_dso) { if (dso_filter) { - newtPopHelpLine(); + ui_helpline__pop(); dso_filter = NULL; } else { - snprintf(msg, sizeof(msg), - "To zoom out press -> + \"Zoom out of %s DSO\"", - dso->kernel ? "the Kernel" : dso->short_name); - newtPushHelpLine(msg); + ui_helpline__fpush("To zoom out press -> + \"Zoom out of %s DSO\"", + dso->kernel ? "the Kernel" : dso->short_name); dso_filter = dso; } - nr_hists = hists__filter_by_dso(hists, dso_filter, &session_total); + hists__filter_by_dso(self, dso_filter); hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) + if (hist_browser__populate(browser, self, msg) < 0) goto out; } else if (choice == zoom_thread) { if (thread_filter) { - newtPopHelpLine(); + ui_helpline__pop(); thread_filter = NULL; } else { - snprintf(msg, sizeof(msg), - "To zoom out press -> + \"Zoom out of %s(%d) thread\"", - (thread->comm_set ? thread->comm : ""), - thread->pid); - newtPushHelpLine(msg); + ui_helpline__fpush("To zoom out press -> + \"Zoom out of %s(%d) thread\"", + thread->comm_set ? thread->comm : "", + thread->pid); thread_filter = thread; } - nr_hists = hists__filter_by_thread(hists, thread_filter, &session_total); + hists__filter_by_thread(self, thread_filter); hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) + if (hist_browser__populate(browser, self, msg) < 0) goto out; } } @@ -702,15 +921,35 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, return err; } +static struct newtPercentTreeColors { + const char *topColorFg, *topColorBg; + const char *mediumColorFg, *mediumColorBg; + const char *normalColorFg, *normalColorBg; + const char *selColorFg, *selColorBg; + const char *codeColorFg, *codeColorBg; +} defaultPercentTreeColors = { + "red", "lightgray", + "green", "lightgray", + "black", "lightgray", + "lightgray", "magenta", + "blue", "lightgray", +}; + void setup_browser(void) { + struct newtPercentTreeColors *c = &defaultPercentTreeColors; if (!isatty(1)) return; use_browser = true; newtInit(); newtCls(); - newtPushHelpLine(" "); + ui_helpline__puts(" "); + SLtt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); + SLtt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); + SLtt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); + SLtt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); + SLtt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); } void exit_browser(bool wait_for_ok) diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 46190f94b5478926fe29a450e7f3ee8680eb4bc7..ce00fa6cdeda1e97ddf86c46270444659a8d1341 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -102,21 +102,6 @@ int perf_session__create_kernel_maps(struct perf_session *self); int do_read(int fd, void *buf, size_t size); void perf_session__update_sample_type(struct perf_session *self); -#ifdef NO_NEWT_SUPPORT -static inline int perf_session__browse_hists(struct rb_root *hists __used, - u64 nr_hists __used, - u64 session_total __used, - const char *helpline __used, - const char *input_name __used) -{ - return 0; -} -#else -int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline, - const char *input_name); -#endif - static inline struct machine *perf_session__find_host_machine(struct perf_session *self) { diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index b7c54eeed9c9307ce7b04f2e831392f6692cce70..af301acc461ce16715b2f2fc5d696fda91ad8e20 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -48,12 +48,6 @@ struct hist_entry { u64 count_us; u64 count_guest_sys; u64 count_guest_us; - - /* - * XXX WARNING! - * thread _has_ to come after ms, see - * hist_browser__selected_thread in util/newt.c - */ struct map_symbol ms; struct thread *thread; u64 ip;