]> bbs.cooldavid.org Git - net-next-2.6.git/commitdiff
Merge branch 'perf' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux-2...
authorIngo Molnar <mingo@elte.hu>
Wed, 12 May 2010 06:14:29 +0000 (08:14 +0200)
committerIngo Molnar <mingo@elte.hu>
Wed, 12 May 2010 06:14:29 +0000 (08:14 +0200)
tools/perf/Makefile
tools/perf/builtin-annotate.c
tools/perf/builtin-report.c
tools/perf/util/hist.c
tools/perf/util/hist.h
tools/perf/util/newt.c
tools/perf/util/session.h
tools/perf/util/sort.h

index 0ef5cfe52f2d1c33399567b1ecc5e1522676e786..9c4dc30cdc13bba5ed018be4ddaed077cadd4e1a 100644 (file)
@@ -506,8 +506,8 @@ PERFLIBS = $(LIB_FILE)
 -include config.mak
 
 ifndef NO_DWARF
-ifneq ($(shell sh -c "(echo '\#include <dwarf.h>'; echo '\#include <libdw.h>'; 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 <dwarf.h>'; echo '\#include <libdw.h>'; echo '\#include <version.h>'; 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 <newt.h>'; 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 <bfd.h>'; 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)"' $<
 
index 3940964161b388574916283d5eef4f9b91d8f4b5..fd1b786c8f35a510fd3b82cfa3584bcca0a570f5 100644 (file)
@@ -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);
 
index d7c75291e788fa41f51c111674fef0dbaf8869a4..04de3387de3ff20b4177b94c9ca3cadef9899a39 100644 (file)
@@ -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;
index e34fd248067d5bb351c6356c62eb0ca0b789362b..451d2e45d843d01b35c369b50129d6db223c2585 100644 (file)
@@ -784,3 +784,246 @@ print_entries:
 
        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;
+}
index 1b18d04195dcab69a441b66f9a9ba30e2b7ce287..ed9c06734965f54d2224b8dce91aac217e7a0505 100644 (file)
@@ -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 */
index e283a6e6b6e051956575926ef824bdd76f8360fa..ba6acd04c082fac6be1feff82a210a10ad94cf84 100644 (file)
@@ -2,6 +2,7 @@
 #include <stdio.h>
 #undef _GNU_SOURCE
 
+#include <slang.h>
 #include <stdlib.h>
 #include <newt.h>
 #include <sys/ttydefaults.h>
@@ -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 @@ out:
        return err;
 }
 
+static struct newtPercentTreeColors {
+       const char *topColorFg, *topColorBg;
+       const char *mediumColorFg, *mediumColorBg;
+       const char *normalColorFg, *normalColorBg;
+       const char *selColorFg, *selColorBg;
+       const char *codeColorFg, *codeColorBg;
+} defaultPercentTreeColors = {
+       "red",       "lightgray",
+       "green",     "lightgray",
+       "black",     "lightgray",
+       "lightgray", "magenta",
+       "blue",      "lightgray",
+};
+
 void setup_browser(void)
 {
+       struct newtPercentTreeColors *c = &defaultPercentTreeColors;
        if (!isatty(1))
                return;
 
        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)
index 46190f94b5478926fe29a450e7f3ee8680eb4bc7..ce00fa6cdeda1e97ddf86c46270444659a8d1341 100644 (file)
@@ -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)
 {
index b7c54eeed9c9307ce7b04f2e831392f6692cce70..af301acc461ce16715b2f2fc5d696fda91ad8e20 100644 (file)
@@ -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;