diff options
| author | Ingo Molnar <mingo@elte.hu> | 2010-05-12 02:14:29 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2010-05-12 02:14:29 -0400 |
| commit | 975fc2d5f20b071576e7c9920c4f1a1eae80f88d (patch) | |
| tree | 55be3953a712eb5246d9797d1e3796b22ffe99a2 | |
| parent | 8e6d5573af55435160d329f6ae3fe16a0abbdaec (diff) | |
| parent | ef7b93a11904c6ba10604233d318d9e8ec88cddc (diff) | |
Merge branch 'perf' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux-2.6 into perf/core
| -rw-r--r-- | tools/perf/Makefile | 12 | ||||
| -rw-r--r-- | tools/perf/builtin-annotate.c | 198 | ||||
| -rw-r--r-- | tools/perf/builtin-report.c | 24 | ||||
| -rw-r--r-- | tools/perf/util/hist.c | 243 | ||||
| -rw-r--r-- | tools/perf/util/hist.h | 44 | ||||
| -rw-r--r-- | tools/perf/util/newt.c | 501 | ||||
| -rw-r--r-- | tools/perf/util/session.h | 15 | ||||
| -rw-r--r-- | tools/perf/util/sort.h | 6 |
8 files changed, 688 insertions, 355 deletions
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 0ef5cfe52f2d..9c4dc30cdc13 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
| @@ -506,8 +506,8 @@ PERFLIBS = $(LIB_FILE) | |||
| 506 | -include config.mak | 506 | -include config.mak |
| 507 | 507 | ||
| 508 | ifndef NO_DWARF | 508 | ifndef NO_DWARF |
| 509 | 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) | 509 | 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) |
| 510 | msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/libdw-dev); | 510 | 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); |
| 511 | NO_DWARF := 1 | 511 | NO_DWARF := 1 |
| 512 | endif # Dwarf support | 512 | endif # Dwarf support |
| 513 | endif # NO_DWARF | 513 | endif # NO_DWARF |
| @@ -560,6 +560,8 @@ ifneq ($(shell sh -c "(echo '\#include <newt.h>'; echo 'int main(void) { newtIni | |||
| 560 | msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); | 560 | msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); |
| 561 | BASIC_CFLAGS += -DNO_NEWT_SUPPORT | 561 | BASIC_CFLAGS += -DNO_NEWT_SUPPORT |
| 562 | else | 562 | else |
| 563 | # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h | ||
| 564 | BASIC_CFLAGS += -I/usr/include/slang | ||
| 563 | EXTLIBS += -lnewt | 565 | EXTLIBS += -lnewt |
| 564 | LIB_OBJS += $(OUTPUT)util/newt.o | 566 | LIB_OBJS += $(OUTPUT)util/newt.o |
| 565 | endif | 567 | endif |
| @@ -592,6 +594,9 @@ endif | |||
| 592 | 594 | ||
| 593 | ifdef NO_DEMANGLE | 595 | ifdef NO_DEMANGLE |
| 594 | BASIC_CFLAGS += -DNO_DEMANGLE | 596 | BASIC_CFLAGS += -DNO_DEMANGLE |
| 597 | else ifdef HAVE_CPLUS_DEMANGLE | ||
| 598 | EXTLIBS += -liberty | ||
| 599 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE | ||
| 595 | else | 600 | else |
| 596 | 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") | 601 | 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") |
| 597 | 602 | ||
| @@ -945,6 +950,9 @@ $(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS | |||
| 945 | $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS | 950 | $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS |
| 946 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | 951 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< |
| 947 | 952 | ||
| 953 | $(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS | ||
| 954 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | ||
| 955 | |||
| 948 | $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS | 956 | $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS |
| 949 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | 957 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< |
| 950 | 958 | ||
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 3940964161b3..fd1b786c8f35 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
| @@ -34,68 +34,8 @@ static bool full_paths; | |||
| 34 | 34 | ||
| 35 | static bool print_line; | 35 | static bool print_line; |
| 36 | 36 | ||
| 37 | struct sym_hist { | ||
| 38 | u64 sum; | ||
| 39 | u64 ip[0]; | ||
| 40 | }; | ||
| 41 | |||
| 42 | struct sym_ext { | ||
| 43 | struct rb_node node; | ||
| 44 | double percent; | ||
| 45 | char *path; | ||
| 46 | }; | ||
| 47 | |||
| 48 | struct sym_priv { | ||
| 49 | struct sym_hist *hist; | ||
| 50 | struct sym_ext *ext; | ||
| 51 | }; | ||
| 52 | |||
| 53 | static const char *sym_hist_filter; | 37 | static const char *sym_hist_filter; |
| 54 | 38 | ||
| 55 | static int sym__alloc_hist(struct symbol *self) | ||
| 56 | { | ||
| 57 | struct sym_priv *priv = symbol__priv(self); | ||
| 58 | const int size = (sizeof(*priv->hist) + | ||
| 59 | (self->end - self->start) * sizeof(u64)); | ||
| 60 | |||
| 61 | priv->hist = zalloc(size); | ||
| 62 | return priv->hist == NULL ? -1 : 0; | ||
| 63 | } | ||
| 64 | |||
| 65 | /* | ||
| 66 | * collect histogram counts | ||
| 67 | */ | ||
| 68 | static int annotate__hist_hit(struct hist_entry *he, u64 ip) | ||
| 69 | { | ||
| 70 | unsigned int sym_size, offset; | ||
| 71 | struct symbol *sym = he->ms.sym; | ||
| 72 | struct sym_priv *priv; | ||
| 73 | struct sym_hist *h; | ||
| 74 | |||
| 75 | if (!sym || !he->ms.map) | ||
| 76 | return 0; | ||
| 77 | |||
| 78 | priv = symbol__priv(sym); | ||
| 79 | if (priv->hist == NULL && sym__alloc_hist(sym) < 0) | ||
| 80 | return -ENOMEM; | ||
| 81 | |||
| 82 | sym_size = sym->end - sym->start; | ||
| 83 | offset = ip - sym->start; | ||
| 84 | |||
| 85 | pr_debug3("%s: ip=%#Lx\n", __func__, he->ms.map->unmap_ip(he->ms.map, ip)); | ||
| 86 | |||
| 87 | if (offset >= sym_size) | ||
| 88 | return 0; | ||
| 89 | |||
| 90 | h = priv->hist; | ||
| 91 | h->sum++; | ||
| 92 | h->ip[offset]++; | ||
| 93 | |||
| 94 | pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->ms.sym->start, | ||
| 95 | he->ms.sym->name, ip, ip - he->ms.sym->start, h->ip[offset]); | ||
| 96 | return 0; | ||
| 97 | } | ||
| 98 | |||
| 99 | static int hists__add_entry(struct hists *self, struct addr_location *al) | 39 | static int hists__add_entry(struct hists *self, struct addr_location *al) |
| 100 | { | 40 | { |
| 101 | struct hist_entry *he; | 41 | struct hist_entry *he; |
| @@ -115,7 +55,7 @@ static int hists__add_entry(struct hists *self, struct addr_location *al) | |||
| 115 | if (he == NULL) | 55 | if (he == NULL) |
| 116 | return -ENOMEM; | 56 | return -ENOMEM; |
| 117 | 57 | ||
| 118 | return annotate__hist_hit(he, al->addr); | 58 | return hist_entry__inc_addr_samples(he, al->addr); |
| 119 | } | 59 | } |
| 120 | 60 | ||
| 121 | static int process_sample_event(event_t *event, struct perf_session *session) | 61 | 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) | |||
| 140 | return 0; | 80 | return 0; |
| 141 | } | 81 | } |
| 142 | 82 | ||
| 143 | struct objdump_line { | ||
| 144 | struct list_head node; | ||
| 145 | s64 offset; | ||
| 146 | char *line; | ||
| 147 | }; | ||
| 148 | |||
| 149 | static struct objdump_line *objdump_line__new(s64 offset, char *line) | ||
| 150 | { | ||
| 151 | struct objdump_line *self = malloc(sizeof(*self)); | ||
| 152 | |||
| 153 | if (self != NULL) { | ||
| 154 | self->offset = offset; | ||
| 155 | self->line = line; | ||
| 156 | } | ||
| 157 | |||
| 158 | return self; | ||
| 159 | } | ||
| 160 | |||
| 161 | static void objdump_line__free(struct objdump_line *self) | ||
| 162 | { | ||
| 163 | free(self->line); | ||
| 164 | free(self); | ||
| 165 | } | ||
| 166 | |||
| 167 | static void objdump__add_line(struct list_head *head, struct objdump_line *line) | ||
| 168 | { | ||
| 169 | list_add_tail(&line->node, head); | ||
| 170 | } | ||
| 171 | |||
| 172 | static struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
| 173 | struct objdump_line *pos) | ||
| 174 | { | ||
| 175 | list_for_each_entry_continue(pos, head, node) | ||
| 176 | if (pos->offset >= 0) | ||
| 177 | return pos; | ||
| 178 | |||
| 179 | return NULL; | ||
| 180 | } | ||
| 181 | |||
| 182 | static int parse_line(FILE *file, struct hist_entry *he, | ||
| 183 | struct list_head *head) | ||
| 184 | { | ||
| 185 | struct symbol *sym = he->ms.sym; | ||
| 186 | struct objdump_line *objdump_line; | ||
| 187 | char *line = NULL, *tmp, *tmp2; | ||
| 188 | size_t line_len; | ||
| 189 | s64 line_ip, offset = -1; | ||
| 190 | char *c; | ||
| 191 | |||
| 192 | if (getline(&line, &line_len, file) < 0) | ||
| 193 | return -1; | ||
| 194 | |||
| 195 | if (!line) | ||
| 196 | return -1; | ||
| 197 | |||
| 198 | c = strchr(line, '\n'); | ||
| 199 | if (c) | ||
| 200 | *c = 0; | ||
| 201 | |||
| 202 | line_ip = -1; | ||
| 203 | |||
| 204 | /* | ||
| 205 | * Strip leading spaces: | ||
| 206 | */ | ||
| 207 | tmp = line; | ||
| 208 | while (*tmp) { | ||
| 209 | if (*tmp != ' ') | ||
| 210 | break; | ||
| 211 | tmp++; | ||
| 212 | } | ||
| 213 | |||
| 214 | if (*tmp) { | ||
| 215 | /* | ||
| 216 | * Parse hexa addresses followed by ':' | ||
| 217 | */ | ||
| 218 | line_ip = strtoull(tmp, &tmp2, 16); | ||
| 219 | if (*tmp2 != ':') | ||
| 220 | line_ip = -1; | ||
| 221 | } | ||
| 222 | |||
| 223 | if (line_ip != -1) { | ||
| 224 | u64 start = map__rip_2objdump(he->ms.map, sym->start); | ||
| 225 | offset = line_ip - start; | ||
| 226 | } | ||
| 227 | |||
| 228 | objdump_line = objdump_line__new(offset, line); | ||
| 229 | if (objdump_line == NULL) { | ||
| 230 | free(line); | ||
| 231 | return -1; | ||
| 232 | } | ||
| 233 | objdump__add_line(head, objdump_line); | ||
| 234 | |||
| 235 | return 0; | ||
| 236 | } | ||
| 237 | |||
| 238 | static int objdump_line__print(struct objdump_line *self, | 83 | static int objdump_line__print(struct objdump_line *self, |
| 239 | struct list_head *head, | 84 | struct list_head *head, |
| 240 | struct hist_entry *he, u64 len) | 85 | struct hist_entry *he, u64 len) |
| @@ -439,27 +284,11 @@ static void annotate_sym(struct hist_entry *he) | |||
| 439 | struct symbol *sym = he->ms.sym; | 284 | struct symbol *sym = he->ms.sym; |
| 440 | const char *filename = dso->long_name, *d_filename; | 285 | const char *filename = dso->long_name, *d_filename; |
| 441 | u64 len; | 286 | u64 len; |
| 442 | char command[PATH_MAX*2]; | ||
| 443 | FILE *file; | ||
| 444 | LIST_HEAD(head); | 287 | LIST_HEAD(head); |
| 445 | struct objdump_line *pos, *n; | 288 | struct objdump_line *pos, *n; |
| 446 | 289 | ||
| 447 | if (!filename) | 290 | if (hist_entry__annotate(he, &head) < 0) |
| 448 | return; | ||
| 449 | |||
| 450 | if (dso->origin == DSO__ORIG_KERNEL) { | ||
| 451 | if (dso->annotate_warned) | ||
| 452 | return; | ||
| 453 | dso->annotate_warned = 1; | ||
| 454 | pr_err("Can't annotate %s: No vmlinux file was found in the " | ||
| 455 | "path:\n", sym->name); | ||
| 456 | vmlinux_path__fprintf(stderr); | ||
| 457 | return; | 291 | return; |
| 458 | } | ||
| 459 | |||
| 460 | pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, | ||
| 461 | filename, sym->name, map->unmap_ip(map, sym->start), | ||
| 462 | map->unmap_ip(map, sym->end)); | ||
| 463 | 292 | ||
| 464 | if (full_paths) | 293 | if (full_paths) |
| 465 | d_filename = filename; | 294 | d_filename = filename; |
| @@ -477,29 +306,6 @@ static void annotate_sym(struct hist_entry *he) | |||
| 477 | printf(" Percent | Source code & Disassembly of %s\n", d_filename); | 306 | printf(" Percent | Source code & Disassembly of %s\n", d_filename); |
| 478 | printf("------------------------------------------------\n"); | 307 | printf("------------------------------------------------\n"); |
| 479 | 308 | ||
| 480 | if (verbose >= 2) | ||
| 481 | printf("annotating [%p] %30s : [%p] %30s\n", | ||
| 482 | dso, dso->long_name, sym, sym->name); | ||
| 483 | |||
| 484 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s", | ||
| 485 | map__rip_2objdump(map, sym->start), | ||
| 486 | map__rip_2objdump(map, sym->end), | ||
| 487 | filename, filename); | ||
| 488 | |||
| 489 | if (verbose >= 3) | ||
| 490 | printf("doing: %s\n", command); | ||
| 491 | |||
| 492 | file = popen(command, "r"); | ||
| 493 | if (!file) | ||
| 494 | return; | ||
| 495 | |||
| 496 | while (!feof(file)) { | ||
| 497 | if (parse_line(file, he, &head) < 0) | ||
| 498 | break; | ||
| 499 | } | ||
| 500 | |||
| 501 | pclose(file); | ||
| 502 | |||
| 503 | if (verbose) | 309 | if (verbose) |
| 504 | hist_entry__print_hits(he); | 310 | hist_entry__print_hits(he); |
| 505 | 311 | ||
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index d7c75291e788..04de3387de3f 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, | |||
| 106 | if (he == NULL) | 106 | if (he == NULL) |
| 107 | goto out_free_syms; | 107 | goto out_free_syms; |
| 108 | err = 0; | 108 | err = 0; |
| 109 | if (symbol_conf.use_callchain) | 109 | if (symbol_conf.use_callchain) { |
| 110 | err = append_chain(he->callchain, data->callchain, syms); | 110 | err = append_chain(he->callchain, data->callchain, syms); |
| 111 | if (err) | ||
| 112 | goto out_free_syms; | ||
| 113 | } | ||
| 114 | /* | ||
| 115 | * Only in the newt browser we are doing integrated annotation, | ||
| 116 | * so we don't allocated the extra space needed because the stdio | ||
| 117 | * code will not use it. | ||
| 118 | */ | ||
| 119 | if (use_browser) | ||
| 120 | err = hist_entry__inc_addr_samples(he, al->addr); | ||
| 111 | out_free_syms: | 121 | out_free_syms: |
| 112 | free(syms); | 122 | free(syms); |
| 113 | return err; | 123 | return err; |
| @@ -301,10 +311,7 @@ static int __cmd_report(void) | |||
| 301 | hists__collapse_resort(hists); | 311 | hists__collapse_resort(hists); |
| 302 | hists__output_resort(hists); | 312 | hists__output_resort(hists); |
| 303 | if (use_browser) | 313 | if (use_browser) |
| 304 | perf_session__browse_hists(&hists->entries, | 314 | hists__browse(hists, help, input_name); |
| 305 | hists->nr_entries, | ||
| 306 | hists->stats.total, help, | ||
| 307 | input_name); | ||
| 308 | else { | 315 | else { |
| 309 | if (rb_first(&session->hists.entries) == | 316 | if (rb_first(&session->hists.entries) == |
| 310 | rb_last(&session->hists.entries)) | 317 | rb_last(&session->hists.entries)) |
| @@ -461,6 +468,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
| 461 | 468 | ||
| 462 | if (strcmp(input_name, "-") != 0) | 469 | if (strcmp(input_name, "-") != 0) |
| 463 | setup_browser(); | 470 | setup_browser(); |
| 471 | /* | ||
| 472 | * Only in the newt browser we are doing integrated annotation, | ||
| 473 | * so don't allocate extra space that won't be used in the stdio | ||
| 474 | * implementation. | ||
| 475 | */ | ||
| 476 | if (use_browser) | ||
| 477 | symbol_conf.priv_size = sizeof(struct sym_priv); | ||
| 464 | 478 | ||
| 465 | if (symbol__init() < 0) | 479 | if (symbol__init() < 0) |
| 466 | return -1; | 480 | return -1; |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index e34fd248067d..451d2e45d843 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
| @@ -784,3 +784,246 @@ print_entries: | |||
| 784 | 784 | ||
| 785 | return ret; | 785 | return ret; |
| 786 | } | 786 | } |
| 787 | |||
| 788 | enum hist_filter { | ||
| 789 | HIST_FILTER__DSO, | ||
| 790 | HIST_FILTER__THREAD, | ||
| 791 | }; | ||
| 792 | |||
| 793 | void hists__filter_by_dso(struct hists *self, const struct dso *dso) | ||
| 794 | { | ||
| 795 | struct rb_node *nd; | ||
| 796 | |||
| 797 | self->nr_entries = self->stats.total = 0; | ||
| 798 | self->max_sym_namelen = 0; | ||
| 799 | |||
| 800 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | ||
| 801 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
| 802 | |||
| 803 | if (symbol_conf.exclude_other && !h->parent) | ||
| 804 | continue; | ||
| 805 | |||
| 806 | if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { | ||
| 807 | h->filtered |= (1 << HIST_FILTER__DSO); | ||
| 808 | continue; | ||
| 809 | } | ||
| 810 | |||
| 811 | h->filtered &= ~(1 << HIST_FILTER__DSO); | ||
| 812 | if (!h->filtered) { | ||
| 813 | ++self->nr_entries; | ||
| 814 | self->stats.total += h->count; | ||
| 815 | if (h->ms.sym && | ||
| 816 | self->max_sym_namelen < h->ms.sym->namelen) | ||
| 817 | self->max_sym_namelen = h->ms.sym->namelen; | ||
| 818 | } | ||
| 819 | } | ||
| 820 | } | ||
| 821 | |||
| 822 | void hists__filter_by_thread(struct hists *self, const struct thread *thread) | ||
| 823 | { | ||
| 824 | struct rb_node *nd; | ||
| 825 | |||
| 826 | self->nr_entries = self->stats.total = 0; | ||
| 827 | self->max_sym_namelen = 0; | ||
| 828 | |||
| 829 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | ||
| 830 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
| 831 | |||
| 832 | if (thread != NULL && h->thread != thread) { | ||
| 833 | h->filtered |= (1 << HIST_FILTER__THREAD); | ||
| 834 | continue; | ||
| 835 | } | ||
| 836 | h->filtered &= ~(1 << HIST_FILTER__THREAD); | ||
| 837 | if (!h->filtered) { | ||
| 838 | ++self->nr_entries; | ||
| 839 | self->stats.total += h->count; | ||
| 840 | if (h->ms.sym && | ||
| 841 | self->max_sym_namelen < h->ms.sym->namelen) | ||
| 842 | self->max_sym_namelen = h->ms.sym->namelen; | ||
| 843 | } | ||
| 844 | } | ||
| 845 | } | ||
| 846 | |||
| 847 | static int symbol__alloc_hist(struct symbol *self) | ||
| 848 | { | ||
| 849 | struct sym_priv *priv = symbol__priv(self); | ||
| 850 | const int size = (sizeof(*priv->hist) + | ||
| 851 | (self->end - self->start) * sizeof(u64)); | ||
| 852 | |||
| 853 | priv->hist = zalloc(size); | ||
| 854 | return priv->hist == NULL ? -1 : 0; | ||
| 855 | } | ||
| 856 | |||
| 857 | int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) | ||
| 858 | { | ||
| 859 | unsigned int sym_size, offset; | ||
| 860 | struct symbol *sym = self->ms.sym; | ||
| 861 | struct sym_priv *priv; | ||
| 862 | struct sym_hist *h; | ||
| 863 | |||
| 864 | if (!sym || !self->ms.map) | ||
| 865 | return 0; | ||
| 866 | |||
| 867 | priv = symbol__priv(sym); | ||
| 868 | if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) | ||
| 869 | return -ENOMEM; | ||
| 870 | |||
| 871 | sym_size = sym->end - sym->start; | ||
| 872 | offset = ip - sym->start; | ||
| 873 | |||
| 874 | pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); | ||
| 875 | |||
| 876 | if (offset >= sym_size) | ||
| 877 | return 0; | ||
| 878 | |||
| 879 | h = priv->hist; | ||
| 880 | h->sum++; | ||
| 881 | h->ip[offset]++; | ||
| 882 | |||
| 883 | pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, | ||
| 884 | self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); | ||
| 885 | return 0; | ||
| 886 | } | ||
| 887 | |||
| 888 | static struct objdump_line *objdump_line__new(s64 offset, char *line) | ||
| 889 | { | ||
| 890 | struct objdump_line *self = malloc(sizeof(*self)); | ||
| 891 | |||
| 892 | if (self != NULL) { | ||
| 893 | self->offset = offset; | ||
| 894 | self->line = line; | ||
| 895 | } | ||
| 896 | |||
| 897 | return self; | ||
| 898 | } | ||
| 899 | |||
| 900 | void objdump_line__free(struct objdump_line *self) | ||
| 901 | { | ||
| 902 | free(self->line); | ||
| 903 | free(self); | ||
| 904 | } | ||
| 905 | |||
| 906 | static void objdump__add_line(struct list_head *head, struct objdump_line *line) | ||
| 907 | { | ||
| 908 | list_add_tail(&line->node, head); | ||
| 909 | } | ||
| 910 | |||
| 911 | struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
| 912 | struct objdump_line *pos) | ||
| 913 | { | ||
| 914 | list_for_each_entry_continue(pos, head, node) | ||
| 915 | if (pos->offset >= 0) | ||
| 916 | return pos; | ||
| 917 | |||
| 918 | return NULL; | ||
| 919 | } | ||
| 920 | |||
| 921 | static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, | ||
| 922 | struct list_head *head) | ||
| 923 | { | ||
| 924 | struct symbol *sym = self->ms.sym; | ||
| 925 | struct objdump_line *objdump_line; | ||
| 926 | char *line = NULL, *tmp, *tmp2, *c; | ||
| 927 | size_t line_len; | ||
| 928 | s64 line_ip, offset = -1; | ||
| 929 | |||
| 930 | if (getline(&line, &line_len, file) < 0) | ||
| 931 | return -1; | ||
| 932 | |||
| 933 | if (!line) | ||
| 934 | return -1; | ||
| 935 | |||
| 936 | while (line_len != 0 && isspace(line[line_len - 1])) | ||
| 937 | line[--line_len] = '\0'; | ||
| 938 | |||
| 939 | c = strchr(line, '\n'); | ||
| 940 | if (c) | ||
| 941 | *c = 0; | ||
| 942 | |||
| 943 | line_ip = -1; | ||
| 944 | |||
| 945 | /* | ||
| 946 | * Strip leading spaces: | ||
| 947 | */ | ||
| 948 | tmp = line; | ||
| 949 | while (*tmp) { | ||
| 950 | if (*tmp != ' ') | ||
| 951 | break; | ||
| 952 | tmp++; | ||
| 953 | } | ||
| 954 | |||
| 955 | if (*tmp) { | ||
| 956 | /* | ||
| 957 | * Parse hexa addresses followed by ':' | ||
| 958 | */ | ||
| 959 | line_ip = strtoull(tmp, &tmp2, 16); | ||
| 960 | if (*tmp2 != ':') | ||
| 961 | line_ip = -1; | ||
| 962 | } | ||
| 963 | |||
| 964 | if (line_ip != -1) { | ||
| 965 | u64 start = map__rip_2objdump(self->ms.map, sym->start); | ||
| 966 | offset = line_ip - start; | ||
| 967 | } | ||
| 968 | |||
| 969 | objdump_line = objdump_line__new(offset, line); | ||
| 970 | if (objdump_line == NULL) { | ||
| 971 | free(line); | ||
| 972 | return -1; | ||
| 973 | } | ||
| 974 | objdump__add_line(head, objdump_line); | ||
| 975 | |||
| 976 | return 0; | ||
| 977 | } | ||
| 978 | |||
| 979 | int hist_entry__annotate(struct hist_entry *self, struct list_head *head) | ||
| 980 | { | ||
| 981 | struct symbol *sym = self->ms.sym; | ||
| 982 | struct map *map = self->ms.map; | ||
| 983 | struct dso *dso = map->dso; | ||
| 984 | const char *filename = dso->long_name; | ||
| 985 | char command[PATH_MAX * 2]; | ||
| 986 | FILE *file; | ||
| 987 | u64 len; | ||
| 988 | |||
| 989 | if (!filename) | ||
| 990 | return -1; | ||
| 991 | |||
| 992 | if (dso->origin == DSO__ORIG_KERNEL) { | ||
| 993 | if (dso->annotate_warned) | ||
| 994 | return 0; | ||
| 995 | dso->annotate_warned = 1; | ||
| 996 | pr_err("Can't annotate %s: No vmlinux file was found in the " | ||
| 997 | "path:\n", sym->name); | ||
| 998 | vmlinux_path__fprintf(stderr); | ||
| 999 | return -1; | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, | ||
| 1003 | filename, sym->name, map->unmap_ip(map, sym->start), | ||
| 1004 | map->unmap_ip(map, sym->end)); | ||
| 1005 | |||
| 1006 | len = sym->end - sym->start; | ||
| 1007 | |||
| 1008 | pr_debug("annotating [%p] %30s : [%p] %30s\n", | ||
| 1009 | dso, dso->long_name, sym, sym->name); | ||
| 1010 | |||
| 1011 | snprintf(command, sizeof(command), | ||
| 1012 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", | ||
| 1013 | map__rip_2objdump(map, sym->start), | ||
| 1014 | map__rip_2objdump(map, sym->end), | ||
| 1015 | filename, filename); | ||
| 1016 | |||
| 1017 | pr_debug("Executing: %s\n", command); | ||
| 1018 | |||
| 1019 | file = popen(command, "r"); | ||
| 1020 | if (!file) | ||
| 1021 | return -1; | ||
| 1022 | |||
| 1023 | while (!feof(file)) | ||
| 1024 | if (hist_entry__parse_objdump_line(self, file, head) < 0) | ||
| 1025 | break; | ||
| 1026 | |||
| 1027 | pclose(file); | ||
| 1028 | return 0; | ||
| 1029 | } | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1b18d04195dc..ed9c06734965 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
| @@ -11,6 +11,32 @@ struct addr_location; | |||
| 11 | struct symbol; | 11 | struct symbol; |
| 12 | struct rb_root; | 12 | struct rb_root; |
| 13 | 13 | ||
| 14 | struct objdump_line { | ||
| 15 | struct list_head node; | ||
| 16 | s64 offset; | ||
| 17 | char *line; | ||
| 18 | }; | ||
| 19 | |||
| 20 | void objdump_line__free(struct objdump_line *self); | ||
| 21 | struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
| 22 | struct objdump_line *pos); | ||
| 23 | |||
| 24 | struct sym_hist { | ||
| 25 | u64 sum; | ||
| 26 | u64 ip[0]; | ||
| 27 | }; | ||
| 28 | |||
| 29 | struct sym_ext { | ||
| 30 | struct rb_node node; | ||
| 31 | double percent; | ||
| 32 | char *path; | ||
| 33 | }; | ||
| 34 | |||
| 35 | struct sym_priv { | ||
| 36 | struct sym_hist *hist; | ||
| 37 | struct sym_ext *ext; | ||
| 38 | }; | ||
| 39 | |||
| 14 | struct events_stats { | 40 | struct events_stats { |
| 15 | u64 total; | 41 | u64 total; |
| 16 | u64 lost; | 42 | u64 lost; |
| @@ -44,4 +70,22 @@ void hists__output_resort(struct hists *self); | |||
| 44 | void hists__collapse_resort(struct hists *self); | 70 | void hists__collapse_resort(struct hists *self); |
| 45 | size_t hists__fprintf(struct hists *self, struct hists *pair, | 71 | size_t hists__fprintf(struct hists *self, struct hists *pair, |
| 46 | bool show_displacement, FILE *fp); | 72 | bool show_displacement, FILE *fp); |
| 73 | |||
| 74 | int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); | ||
| 75 | int hist_entry__annotate(struct hist_entry *self, struct list_head *head); | ||
| 76 | |||
| 77 | void hists__filter_by_dso(struct hists *self, const struct dso *dso); | ||
| 78 | void hists__filter_by_thread(struct hists *self, const struct thread *thread); | ||
| 79 | |||
| 80 | #ifdef NO_NEWT_SUPPORT | ||
| 81 | static inline int hists__browse(struct hists self __used, | ||
| 82 | const char *helpline __used, | ||
| 83 | const char *input_name __used) | ||
| 84 | { | ||
| 85 | return 0; | ||
| 86 | } | ||
| 87 | #else | ||
| 88 | int hists__browse(struct hists *self, const char *helpline, | ||
| 89 | const char *input_name); | ||
| 90 | #endif | ||
| 47 | #endif /* __PERF_HIST_H */ | 91 | #endif /* __PERF_HIST_H */ |
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index e283a6e6b6e0..ba6acd04c082 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | #include <stdio.h> | 2 | #include <stdio.h> |
| 3 | #undef _GNU_SOURCE | 3 | #undef _GNU_SOURCE |
| 4 | 4 | ||
| 5 | #include <slang.h> | ||
| 5 | #include <stdlib.h> | 6 | #include <stdlib.h> |
| 6 | #include <newt.h> | 7 | #include <newt.h> |
| 7 | #include <sys/ttydefaults.h> | 8 | #include <sys/ttydefaults.h> |
| @@ -57,6 +58,43 @@ void ui_progress__delete(struct ui_progress *self) | |||
| 57 | free(self); | 58 | free(self); |
| 58 | } | 59 | } |
| 59 | 60 | ||
| 61 | static void ui_helpline__pop(void) | ||
| 62 | { | ||
| 63 | newtPopHelpLine(); | ||
| 64 | } | ||
| 65 | |||
| 66 | static void ui_helpline__push(const char *msg) | ||
| 67 | { | ||
| 68 | newtPushHelpLine(msg); | ||
| 69 | } | ||
| 70 | |||
| 71 | static void ui_helpline__vpush(const char *fmt, va_list ap) | ||
| 72 | { | ||
| 73 | char *s; | ||
| 74 | |||
| 75 | if (vasprintf(&s, fmt, ap) < 0) | ||
| 76 | vfprintf(stderr, fmt, ap); | ||
| 77 | else { | ||
| 78 | ui_helpline__push(s); | ||
| 79 | free(s); | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | static void ui_helpline__fpush(const char *fmt, ...) | ||
| 84 | { | ||
| 85 | va_list ap; | ||
| 86 | |||
| 87 | va_start(ap, fmt); | ||
| 88 | ui_helpline__vpush(fmt, ap); | ||
| 89 | va_end(ap); | ||
| 90 | } | ||
| 91 | |||
| 92 | static void ui_helpline__puts(const char *msg) | ||
| 93 | { | ||
| 94 | ui_helpline__pop(); | ||
| 95 | ui_helpline__push(msg); | ||
| 96 | } | ||
| 97 | |||
| 60 | static char browser__last_msg[1024]; | 98 | static char browser__last_msg[1024]; |
| 61 | 99 | ||
| 62 | int browser__show_help(const char *format, va_list ap) | 100 | int browser__show_help(const char *format, va_list ap) |
| @@ -69,8 +107,7 @@ int browser__show_help(const char *format, va_list ap) | |||
| 69 | backlog += ret; | 107 | backlog += ret; |
| 70 | 108 | ||
| 71 | if (browser__last_msg[backlog - 1] == '\n') { | 109 | if (browser__last_msg[backlog - 1] == '\n') { |
| 72 | newtPopHelpLine(); | 110 | ui_helpline__puts(browser__last_msg); |
| 73 | newtPushHelpLine(browser__last_msg); | ||
| 74 | newtRefresh(); | 111 | newtRefresh(); |
| 75 | backlog = 0; | 112 | backlog = 0; |
| 76 | } | 113 | } |
| @@ -135,6 +172,254 @@ static bool dialog_yesno(const char *msg) | |||
| 135 | return newtWinChoice(NULL, yes, no, (char *)msg) == 1; | 172 | return newtWinChoice(NULL, yes, no, (char *)msg) == 1; |
| 136 | } | 173 | } |
| 137 | 174 | ||
| 175 | #define HE_COLORSET_TOP 50 | ||
| 176 | #define HE_COLORSET_MEDIUM 51 | ||
| 177 | #define HE_COLORSET_NORMAL 52 | ||
| 178 | #define HE_COLORSET_SELECTED 53 | ||
| 179 | #define HE_COLORSET_CODE 54 | ||
| 180 | |||
| 181 | static int ui_browser__percent_color(double percent, bool current) | ||
| 182 | { | ||
| 183 | if (current) | ||
| 184 | return HE_COLORSET_SELECTED; | ||
| 185 | if (percent >= MIN_RED) | ||
| 186 | return HE_COLORSET_TOP; | ||
| 187 | if (percent >= MIN_GREEN) | ||
| 188 | return HE_COLORSET_MEDIUM; | ||
| 189 | return HE_COLORSET_NORMAL; | ||
| 190 | } | ||
| 191 | |||
| 192 | struct ui_browser { | ||
| 193 | newtComponent form, sb; | ||
| 194 | u64 index, first_visible_entry_idx; | ||
| 195 | void *first_visible_entry, *entries; | ||
| 196 | u16 top, left, width, height; | ||
| 197 | void *priv; | ||
| 198 | u32 nr_entries; | ||
| 199 | }; | ||
| 200 | |||
| 201 | static void ui_browser__refresh_dimensions(struct ui_browser *self) | ||
| 202 | { | ||
| 203 | int cols, rows; | ||
| 204 | newtGetScreenSize(&cols, &rows); | ||
| 205 | |||
| 206 | if (self->width > cols - 4) | ||
| 207 | self->width = cols - 4; | ||
| 208 | self->height = rows - 5; | ||
| 209 | if (self->height > self->nr_entries) | ||
| 210 | self->height = self->nr_entries; | ||
| 211 | self->top = (rows - self->height) / 2; | ||
| 212 | self->left = (cols - self->width) / 2; | ||
| 213 | } | ||
| 214 | |||
| 215 | static void ui_browser__reset_index(struct ui_browser *self) | ||
| 216 | { | ||
| 217 | self->index = self->first_visible_entry_idx = 0; | ||
| 218 | self->first_visible_entry = NULL; | ||
| 219 | } | ||
| 220 | |||
| 221 | static int objdump_line__show(struct objdump_line *self, struct list_head *head, | ||
| 222 | int width, struct hist_entry *he, int len, | ||
| 223 | bool current_entry) | ||
| 224 | { | ||
| 225 | if (self->offset != -1) { | ||
| 226 | struct symbol *sym = he->ms.sym; | ||
| 227 | unsigned int hits = 0; | ||
| 228 | double percent = 0.0; | ||
| 229 | int color; | ||
| 230 | struct sym_priv *priv = symbol__priv(sym); | ||
| 231 | struct sym_ext *sym_ext = priv->ext; | ||
| 232 | struct sym_hist *h = priv->hist; | ||
| 233 | s64 offset = self->offset; | ||
| 234 | struct objdump_line *next = objdump__get_next_ip_line(head, self); | ||
| 235 | |||
| 236 | while (offset < (s64)len && | ||
| 237 | (next == NULL || offset < next->offset)) { | ||
| 238 | if (sym_ext) { | ||
| 239 | percent += sym_ext[offset].percent; | ||
| 240 | } else | ||
| 241 | hits += h->ip[offset]; | ||
| 242 | |||
| 243 | ++offset; | ||
| 244 | } | ||
| 245 | |||
| 246 | if (sym_ext == NULL && h->sum) | ||
| 247 | percent = 100.0 * hits / h->sum; | ||
| 248 | |||
| 249 | color = ui_browser__percent_color(percent, current_entry); | ||
| 250 | SLsmg_set_color(color); | ||
| 251 | SLsmg_printf(" %7.2f ", percent); | ||
| 252 | if (!current_entry) | ||
| 253 | SLsmg_set_color(HE_COLORSET_CODE); | ||
| 254 | } else { | ||
| 255 | int color = ui_browser__percent_color(0, current_entry); | ||
| 256 | SLsmg_set_color(color); | ||
| 257 | SLsmg_write_nstring(" ", 9); | ||
| 258 | } | ||
| 259 | |||
| 260 | SLsmg_write_char(':'); | ||
| 261 | SLsmg_write_nstring(" ", 8); | ||
| 262 | if (!*self->line) | ||
| 263 | SLsmg_write_nstring(" ", width - 18); | ||
| 264 | else | ||
| 265 | SLsmg_write_nstring(self->line, width - 18); | ||
| 266 | |||
| 267 | return 0; | ||
| 268 | } | ||
| 269 | |||
| 270 | static int ui_browser__refresh_entries(struct ui_browser *self) | ||
| 271 | { | ||
| 272 | struct objdump_line *pos; | ||
| 273 | struct list_head *head = self->entries; | ||
| 274 | struct hist_entry *he = self->priv; | ||
| 275 | int row = 0; | ||
| 276 | int len = he->ms.sym->end - he->ms.sym->start; | ||
| 277 | |||
| 278 | if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) | ||
| 279 | self->first_visible_entry = head->next; | ||
| 280 | |||
| 281 | pos = list_entry(self->first_visible_entry, struct objdump_line, node); | ||
| 282 | |||
| 283 | list_for_each_entry_from(pos, head, node) { | ||
| 284 | bool current_entry = (self->first_visible_entry_idx + row) == self->index; | ||
| 285 | SLsmg_gotorc(self->top + row, self->left); | ||
| 286 | objdump_line__show(pos, head, self->width, | ||
| 287 | he, len, current_entry); | ||
| 288 | if (++row == self->height) | ||
| 289 | break; | ||
| 290 | } | ||
| 291 | |||
| 292 | SLsmg_set_color(HE_COLORSET_NORMAL); | ||
| 293 | SLsmg_fill_region(self->top + row, self->left, | ||
| 294 | self->height - row, self->width, ' '); | ||
| 295 | |||
| 296 | return 0; | ||
| 297 | } | ||
| 298 | |||
| 299 | static int ui_browser__run(struct ui_browser *self, const char *title, | ||
| 300 | struct newtExitStruct *es) | ||
| 301 | { | ||
| 302 | if (self->form) { | ||
| 303 | newtFormDestroy(self->form); | ||
| 304 | newtPopWindow(); | ||
| 305 | } | ||
| 306 | |||
| 307 | ui_browser__refresh_dimensions(self); | ||
| 308 | newtCenteredWindow(self->width + 2, self->height, title); | ||
| 309 | self->form = newt_form__new(); | ||
| 310 | if (self->form == NULL) | ||
| 311 | return -1; | ||
| 312 | |||
| 313 | self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, | ||
| 314 | HE_COLORSET_NORMAL, | ||
| 315 | HE_COLORSET_SELECTED); | ||
| 316 | if (self->sb == NULL) | ||
| 317 | return -1; | ||
| 318 | |||
| 319 | newtFormAddHotKey(self->form, NEWT_KEY_UP); | ||
| 320 | newtFormAddHotKey(self->form, NEWT_KEY_DOWN); | ||
| 321 | newtFormAddHotKey(self->form, NEWT_KEY_PGUP); | ||
| 322 | newtFormAddHotKey(self->form, NEWT_KEY_PGDN); | ||
| 323 | newtFormAddHotKey(self->form, NEWT_KEY_HOME); | ||
| 324 | newtFormAddHotKey(self->form, NEWT_KEY_END); | ||
| 325 | |||
| 326 | if (ui_browser__refresh_entries(self) < 0) | ||
| 327 | return -1; | ||
| 328 | newtFormAddComponent(self->form, self->sb); | ||
| 329 | |||
| 330 | while (1) { | ||
| 331 | unsigned int offset; | ||
| 332 | |||
| 333 | newtFormRun(self->form, es); | ||
| 334 | |||
| 335 | if (es->reason != NEWT_EXIT_HOTKEY) | ||
| 336 | break; | ||
| 337 | switch (es->u.key) { | ||
| 338 | case NEWT_KEY_DOWN: | ||
| 339 | if (self->index == self->nr_entries - 1) | ||
| 340 | break; | ||
| 341 | ++self->index; | ||
| 342 | if (self->index == self->first_visible_entry_idx + self->height) { | ||
| 343 | struct list_head *pos = self->first_visible_entry; | ||
| 344 | ++self->first_visible_entry_idx; | ||
| 345 | self->first_visible_entry = pos->next; | ||
| 346 | } | ||
| 347 | break; | ||
| 348 | case NEWT_KEY_UP: | ||
| 349 | if (self->index == 0) | ||
| 350 | break; | ||
| 351 | --self->index; | ||
| 352 | if (self->index < self->first_visible_entry_idx) { | ||
| 353 | struct list_head *pos = self->first_visible_entry; | ||
| 354 | --self->first_visible_entry_idx; | ||
| 355 | self->first_visible_entry = pos->prev; | ||
| 356 | } | ||
| 357 | break; | ||
| 358 | case NEWT_KEY_PGDN: | ||
| 359 | if (self->first_visible_entry_idx + self->height > self->nr_entries - 1) | ||
| 360 | break; | ||
| 361 | |||
| 362 | offset = self->height; | ||
| 363 | if (self->index + offset > self->nr_entries - 1) | ||
| 364 | offset = self->nr_entries - 1 - self->index; | ||
| 365 | self->index += offset; | ||
| 366 | self->first_visible_entry_idx += offset; | ||
| 367 | |||
| 368 | while (offset--) { | ||
| 369 | struct list_head *pos = self->first_visible_entry; | ||
| 370 | self->first_visible_entry = pos->next; | ||
| 371 | } | ||
| 372 | |||
| 373 | break; | ||
| 374 | case NEWT_KEY_PGUP: | ||
| 375 | if (self->first_visible_entry_idx == 0) | ||
| 376 | break; | ||
| 377 | |||
| 378 | if (self->first_visible_entry_idx < self->height) | ||
| 379 | offset = self->first_visible_entry_idx; | ||
| 380 | else | ||
| 381 | offset = self->height; | ||
| 382 | |||
| 383 | self->index -= offset; | ||
| 384 | self->first_visible_entry_idx -= offset; | ||
| 385 | |||
| 386 | while (offset--) { | ||
| 387 | struct list_head *pos = self->first_visible_entry; | ||
| 388 | self->first_visible_entry = pos->prev; | ||
| 389 | } | ||
| 390 | break; | ||
| 391 | case NEWT_KEY_HOME: | ||
| 392 | ui_browser__reset_index(self); | ||
| 393 | break; | ||
| 394 | case NEWT_KEY_END: { | ||
| 395 | struct list_head *head = self->entries; | ||
| 396 | offset = self->height - 1; | ||
| 397 | |||
| 398 | if (offset > self->nr_entries) | ||
| 399 | offset = self->nr_entries; | ||
| 400 | |||
| 401 | self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset; | ||
| 402 | self->first_visible_entry = head->prev; | ||
| 403 | while (offset-- != 0) { | ||
| 404 | struct list_head *pos = self->first_visible_entry; | ||
| 405 | self->first_visible_entry = pos->prev; | ||
| 406 | } | ||
| 407 | } | ||
| 408 | break; | ||
| 409 | case NEWT_KEY_ESCAPE: | ||
| 410 | case CTRL('c'): | ||
| 411 | case 'Q': | ||
| 412 | case 'q': | ||
| 413 | return 0; | ||
| 414 | default: | ||
| 415 | continue; | ||
| 416 | } | ||
| 417 | if (ui_browser__refresh_entries(self) < 0) | ||
| 418 | return -1; | ||
| 419 | } | ||
| 420 | return 0; | ||
| 421 | } | ||
| 422 | |||
| 138 | /* | 423 | /* |
| 139 | * When debugging newt problems it was useful to be able to "unroll" | 424 | * When debugging newt problems it was useful to be able to "unroll" |
| 140 | * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate | 425 | * 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, | |||
| 317 | return ret; | 602 | return ret; |
| 318 | } | 603 | } |
| 319 | 604 | ||
| 320 | static void map_symbol__annotate_browser(const struct map_symbol *self, | 605 | static void hist_entry__annotate_browser(struct hist_entry *self) |
| 321 | const char *input_name) | ||
| 322 | { | 606 | { |
| 323 | FILE *fp; | 607 | struct ui_browser browser; |
| 324 | int cols, rows; | ||
| 325 | newtComponent form, tree; | ||
| 326 | struct newtExitStruct es; | 608 | struct newtExitStruct es; |
| 327 | char *str; | 609 | struct objdump_line *pos, *n; |
| 328 | size_t line_len, max_line_len = 0; | 610 | LIST_HEAD(head); |
| 329 | size_t max_usable_width; | ||
| 330 | char *line = NULL; | ||
| 331 | 611 | ||
| 332 | if (self->sym == NULL) | 612 | if (self->ms.sym == NULL) |
| 333 | return; | 613 | return; |
| 334 | 614 | ||
| 335 | if (asprintf(&str, "perf annotate -i \"%s\" -d \"%s\" %s 2>&1 | expand", | 615 | if (hist_entry__annotate(self, &head) < 0) |
| 336 | input_name, self->map->dso->name, self->sym->name) < 0) | ||
| 337 | return; | 616 | return; |
| 338 | 617 | ||
| 339 | fp = popen(str, "r"); | 618 | ui_helpline__push("Press ESC to exit"); |
| 340 | if (fp == NULL) | ||
| 341 | goto out_free_str; | ||
| 342 | |||
| 343 | newtPushHelpLine("Press ESC to exit"); | ||
| 344 | newtGetScreenSize(&cols, &rows); | ||
| 345 | tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); | ||
| 346 | |||
| 347 | while (!feof(fp)) { | ||
| 348 | if (getline(&line, &line_len, fp) < 0 || !line_len) | ||
| 349 | break; | ||
| 350 | while (line_len != 0 && isspace(line[line_len - 1])) | ||
| 351 | line[--line_len] = '\0'; | ||
| 352 | 619 | ||
| 353 | if (line_len > max_line_len) | 620 | memset(&browser, 0, sizeof(browser)); |
| 354 | max_line_len = line_len; | 621 | browser.entries = &head; |
| 355 | newtListboxAppendEntry(tree, line, NULL); | 622 | browser.priv = self; |
| 623 | list_for_each_entry(pos, &head, node) { | ||
| 624 | size_t line_len = strlen(pos->line); | ||
| 625 | if (browser.width < line_len) | ||
| 626 | browser.width = line_len; | ||
| 627 | ++browser.nr_entries; | ||
| 356 | } | 628 | } |
| 357 | fclose(fp); | ||
| 358 | free(line); | ||
| 359 | 629 | ||
| 360 | max_usable_width = cols - 22; | 630 | browser.width += 18; /* Percentage */ |
| 361 | if (max_line_len > max_usable_width) | 631 | ui_browser__run(&browser, self->ms.sym->name, &es); |
| 362 | max_line_len = max_usable_width; | 632 | newtFormDestroy(browser.form); |
| 363 | |||
| 364 | newtListboxSetWidth(tree, max_line_len); | ||
| 365 | |||
| 366 | newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); | ||
| 367 | form = newt_form__new(); | ||
| 368 | newtFormAddComponent(form, tree); | ||
| 369 | |||
| 370 | newtFormRun(form, &es); | ||
| 371 | newtFormDestroy(form); | ||
| 372 | newtPopWindow(); | 633 | newtPopWindow(); |
| 373 | newtPopHelpLine(); | 634 | list_for_each_entry_safe(pos, n, &head, node) { |
| 374 | out_free_str: | 635 | list_del(&pos->node); |
| 375 | free(str); | 636 | objdump_line__free(pos); |
| 637 | } | ||
| 638 | ui_helpline__pop(); | ||
| 376 | } | 639 | } |
| 377 | 640 | ||
| 378 | static const void *newt__symbol_tree_get_current(newtComponent self) | 641 | static const void *newt__symbol_tree_get_current(newtComponent self) |
| @@ -410,8 +673,8 @@ static void hist_browser__delete(struct hist_browser *self) | |||
| 410 | free(self); | 673 | free(self); |
| 411 | } | 674 | } |
| 412 | 675 | ||
| 413 | static int hist_browser__populate(struct hist_browser *self, struct rb_root *hists, | 676 | static int hist_browser__populate(struct hist_browser *self, struct hists *hists, |
| 414 | u64 nr_hists, u64 session_total, const char *title) | 677 | const char *title) |
| 415 | { | 678 | { |
| 416 | int max_len = 0, idx, cols, rows; | 679 | int max_len = 0, idx, cols, rows; |
| 417 | struct ui_progress *progress; | 680 | struct ui_progress *progress; |
| @@ -426,7 +689,7 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his | |||
| 426 | } | 689 | } |
| 427 | 690 | ||
| 428 | snprintf(str, sizeof(str), "Samples: %Ld ", | 691 | snprintf(str, sizeof(str), "Samples: %Ld ", |
| 429 | session_total); | 692 | hists->stats.total); |
| 430 | newtDrawRootText(0, 0, str); | 693 | newtDrawRootText(0, 0, str); |
| 431 | 694 | ||
| 432 | newtGetScreenSize(NULL, &rows); | 695 | newtGetScreenSize(NULL, &rows); |
| @@ -442,24 +705,25 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his | |||
| 442 | newtComponentAddCallback(self->tree, hist_browser__selection, | 705 | newtComponentAddCallback(self->tree, hist_browser__selection, |
| 443 | &self->selection); | 706 | &self->selection); |
| 444 | 707 | ||
| 445 | progress = ui_progress__new("Adding entries to the browser...", nr_hists); | 708 | progress = ui_progress__new("Adding entries to the browser...", |
| 709 | hists->nr_entries); | ||
| 446 | if (progress == NULL) | 710 | if (progress == NULL) |
| 447 | return -1; | 711 | return -1; |
| 448 | 712 | ||
| 449 | idx = 0; | 713 | idx = 0; |
| 450 | for (nd = rb_first(hists); nd; nd = rb_next(nd)) { | 714 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
| 451 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 715 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
| 452 | int len; | 716 | int len; |
| 453 | 717 | ||
| 454 | if (h->filtered) | 718 | if (h->filtered) |
| 455 | continue; | 719 | continue; |
| 456 | 720 | ||
| 457 | len = hist_entry__append_browser(h, self->tree, session_total); | 721 | len = hist_entry__append_browser(h, self->tree, hists->stats.total); |
| 458 | if (len > max_len) | 722 | if (len > max_len) |
| 459 | max_len = len; | 723 | max_len = len; |
| 460 | if (symbol_conf.use_callchain) | 724 | if (symbol_conf.use_callchain) |
| 461 | hist_entry__append_callchain_browser(h, self->tree, | 725 | hist_entry__append_callchain_browser(h, self->tree, |
| 462 | session_total, idx++); | 726 | hists->stats.total, idx++); |
| 463 | ++curr_hist; | 727 | ++curr_hist; |
| 464 | if (curr_hist % 5) | 728 | if (curr_hist % 5) |
| 465 | ui_progress__update(progress, curr_hist); | 729 | ui_progress__update(progress, curr_hist); |
| @@ -490,58 +754,7 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his | |||
| 490 | return 0; | 754 | return 0; |
| 491 | } | 755 | } |
| 492 | 756 | ||
| 493 | enum hist_filter { | 757 | static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) |
| 494 | HIST_FILTER__DSO, | ||
| 495 | HIST_FILTER__THREAD, | ||
| 496 | }; | ||
| 497 | |||
| 498 | static u64 hists__filter_by_dso(struct rb_root *hists, const struct dso *dso, | ||
| 499 | u64 *session_total) | ||
| 500 | { | ||
| 501 | struct rb_node *nd; | ||
| 502 | u64 nr_hists = 0; | ||
| 503 | |||
| 504 | *session_total = 0; | ||
| 505 | |||
| 506 | for (nd = rb_first(hists); nd; nd = rb_next(nd)) { | ||
| 507 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
| 508 | |||
| 509 | if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { | ||
| 510 | h->filtered |= (1 << HIST_FILTER__DSO); | ||
| 511 | continue; | ||
| 512 | } | ||
| 513 | h->filtered &= ~(1 << HIST_FILTER__DSO); | ||
| 514 | ++nr_hists; | ||
| 515 | *session_total += h->count; | ||
| 516 | } | ||
| 517 | |||
| 518 | return nr_hists; | ||
| 519 | } | ||
| 520 | |||
| 521 | static u64 hists__filter_by_thread(struct rb_root *hists, const struct thread *thread, | ||
| 522 | u64 *session_total) | ||
| 523 | { | ||
| 524 | struct rb_node *nd; | ||
| 525 | u64 nr_hists = 0; | ||
| 526 | |||
| 527 | *session_total = 0; | ||
| 528 | |||
| 529 | for (nd = rb_first(hists); nd; nd = rb_next(nd)) { | ||
| 530 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
| 531 | |||
| 532 | if (thread != NULL && h->thread != thread) { | ||
| 533 | h->filtered |= (1 << HIST_FILTER__THREAD); | ||
| 534 | continue; | ||
| 535 | } | ||
| 536 | h->filtered &= ~(1 << HIST_FILTER__THREAD); | ||
| 537 | ++nr_hists; | ||
| 538 | *session_total += h->count; | ||
| 539 | } | ||
| 540 | |||
| 541 | return nr_hists; | ||
| 542 | } | ||
| 543 | |||
| 544 | static struct thread *hist_browser__selected_thread(struct hist_browser *self) | ||
| 545 | { | 758 | { |
| 546 | int *indexes; | 759 | int *indexes; |
| 547 | 760 | ||
| @@ -557,7 +770,13 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self) | |||
| 557 | } | 770 | } |
| 558 | return NULL; | 771 | return NULL; |
| 559 | out: | 772 | out: |
| 560 | return *(struct thread **)(self->selection + 1); | 773 | return container_of(self->selection, struct hist_entry, ms); |
| 774 | } | ||
| 775 | |||
| 776 | static struct thread *hist_browser__selected_thread(struct hist_browser *self) | ||
| 777 | { | ||
| 778 | struct hist_entry *he = hist_browser__selected_entry(self); | ||
| 779 | return he ? he->thread : NULL; | ||
| 561 | } | 780 | } |
| 562 | 781 | ||
| 563 | static int hist_browser__title(char *bf, size_t size, const char *input_name, | 782 | 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, | |||
| 577 | return printed ?: snprintf(bf, size, "Report: %s", input_name); | 796 | return printed ?: snprintf(bf, size, "Report: %s", input_name); |
| 578 | } | 797 | } |
| 579 | 798 | ||
| 580 | int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, | 799 | int hists__browse(struct hists *self, const char *helpline, const char *input_name) |
| 581 | u64 session_total, const char *helpline, | ||
| 582 | const char *input_name) | ||
| 583 | { | 800 | { |
| 584 | struct hist_browser *browser = hist_browser__new(); | 801 | struct hist_browser *browser = hist_browser__new(); |
| 585 | const struct thread *thread_filter = NULL; | 802 | const struct thread *thread_filter = NULL; |
| @@ -591,11 +808,11 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, | |||
| 591 | if (browser == NULL) | 808 | if (browser == NULL) |
| 592 | return -1; | 809 | return -1; |
| 593 | 810 | ||
| 594 | newtPushHelpLine(helpline); | 811 | ui_helpline__push(helpline); |
| 595 | 812 | ||
| 596 | hist_browser__title(msg, sizeof(msg), input_name, | 813 | hist_browser__title(msg, sizeof(msg), input_name, |
| 597 | dso_filter, thread_filter); | 814 | dso_filter, thread_filter); |
| 598 | if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) | 815 | if (hist_browser__populate(browser, self, msg) < 0) |
| 599 | goto out; | 816 | goto out; |
| 600 | 817 | ||
| 601 | while (1) { | 818 | while (1) { |
| @@ -653,46 +870,48 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, | |||
| 653 | continue; | 870 | continue; |
| 654 | do_annotate: | 871 | do_annotate: |
| 655 | if (choice == annotate) { | 872 | if (choice == annotate) { |
| 873 | struct hist_entry *he; | ||
| 874 | |||
| 656 | if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { | 875 | if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { |
| 657 | newtPopHelpLine(); | 876 | ui_helpline__puts("No vmlinux file found, can't " |
| 658 | newtPushHelpLine("No vmlinux file found, can't " | ||
| 659 | "annotate with just a " | 877 | "annotate with just a " |
| 660 | "kallsyms file"); | 878 | "kallsyms file"); |
| 661 | continue; | 879 | continue; |
| 662 | } | 880 | } |
| 663 | map_symbol__annotate_browser(browser->selection, input_name); | 881 | |
| 882 | he = hist_browser__selected_entry(browser); | ||
| 883 | if (he == NULL) | ||
| 884 | continue; | ||
| 885 | |||
| 886 | hist_entry__annotate_browser(he); | ||
| 664 | } else if (choice == zoom_dso) { | 887 | } else if (choice == zoom_dso) { |
| 665 | if (dso_filter) { | 888 | if (dso_filter) { |
| 666 | newtPopHelpLine(); | 889 | ui_helpline__pop(); |
| 667 | dso_filter = NULL; | 890 | dso_filter = NULL; |
| 668 | } else { | 891 | } else { |
| 669 | snprintf(msg, sizeof(msg), | 892 | ui_helpline__fpush("To zoom out press -> + \"Zoom out of %s DSO\"", |
| 670 | "To zoom out press -> + \"Zoom out of %s DSO\"", | 893 | dso->kernel ? "the Kernel" : dso->short_name); |
| 671 | dso->kernel ? "the Kernel" : dso->short_name); | ||
| 672 | newtPushHelpLine(msg); | ||
| 673 | dso_filter = dso; | 894 | dso_filter = dso; |
| 674 | } | 895 | } |
| 675 | nr_hists = hists__filter_by_dso(hists, dso_filter, &session_total); | 896 | hists__filter_by_dso(self, dso_filter); |
| 676 | hist_browser__title(msg, sizeof(msg), input_name, | 897 | hist_browser__title(msg, sizeof(msg), input_name, |
| 677 | dso_filter, thread_filter); | 898 | dso_filter, thread_filter); |
| 678 | if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) | 899 | if (hist_browser__populate(browser, self, msg) < 0) |
| 679 | goto out; | 900 | goto out; |
| 680 | } else if (choice == zoom_thread) { | 901 | } else if (choice == zoom_thread) { |
| 681 | if (thread_filter) { | 902 | if (thread_filter) { |
| 682 | newtPopHelpLine(); | 903 | ui_helpline__pop(); |
| 683 | thread_filter = NULL; | 904 | thread_filter = NULL; |
| 684 | } else { | 905 | } else { |
| 685 | snprintf(msg, sizeof(msg), | 906 | ui_helpline__fpush("To zoom out press -> + \"Zoom out of %s(%d) thread\"", |
| 686 | "To zoom out press -> + \"Zoom out of %s(%d) thread\"", | 907 | thread->comm_set ? thread->comm : "", |
| 687 | (thread->comm_set ? thread->comm : ""), | 908 | thread->pid); |
| 688 | thread->pid); | ||
| 689 | newtPushHelpLine(msg); | ||
| 690 | thread_filter = thread; | 909 | thread_filter = thread; |
| 691 | } | 910 | } |
| 692 | nr_hists = hists__filter_by_thread(hists, thread_filter, &session_total); | 911 | hists__filter_by_thread(self, thread_filter); |
| 693 | hist_browser__title(msg, sizeof(msg), input_name, | 912 | hist_browser__title(msg, sizeof(msg), input_name, |
| 694 | dso_filter, thread_filter); | 913 | dso_filter, thread_filter); |
| 695 | if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) | 914 | if (hist_browser__populate(browser, self, msg) < 0) |
| 696 | goto out; | 915 | goto out; |
| 697 | } | 916 | } |
| 698 | } | 917 | } |
| @@ -702,15 +921,35 @@ out: | |||
| 702 | return err; | 921 | return err; |
| 703 | } | 922 | } |
| 704 | 923 | ||
| 924 | static struct newtPercentTreeColors { | ||
| 925 | const char *topColorFg, *topColorBg; | ||
| 926 | const char *mediumColorFg, *mediumColorBg; | ||
| 927 | const char *normalColorFg, *normalColorBg; | ||
| 928 | const char *selColorFg, *selColorBg; | ||
| 929 | const char *codeColorFg, *codeColorBg; | ||
| 930 | } defaultPercentTreeColors = { | ||
| 931 | "red", "lightgray", | ||
| 932 | "green", "lightgray", | ||
| 933 | "black", "lightgray", | ||
| 934 | "lightgray", "magenta", | ||
| 935 | "blue", "lightgray", | ||
| 936 | }; | ||
| 937 | |||
| 705 | void setup_browser(void) | 938 | void setup_browser(void) |
| 706 | { | 939 | { |
| 940 | struct newtPercentTreeColors *c = &defaultPercentTreeColors; | ||
| 707 | if (!isatty(1)) | 941 | if (!isatty(1)) |
| 708 | return; | 942 | return; |
| 709 | 943 | ||
| 710 | use_browser = true; | 944 | use_browser = true; |
| 711 | newtInit(); | 945 | newtInit(); |
| 712 | newtCls(); | 946 | newtCls(); |
| 713 | newtPushHelpLine(" "); | 947 | ui_helpline__puts(" "); |
| 948 | SLtt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); | ||
| 949 | SLtt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); | ||
| 950 | SLtt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); | ||
| 951 | SLtt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); | ||
| 952 | SLtt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); | ||
| 714 | } | 953 | } |
| 715 | 954 | ||
| 716 | void exit_browser(bool wait_for_ok) | 955 | void exit_browser(bool wait_for_ok) |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 46190f94b547..ce00fa6cdeda 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); | |||
| 102 | int do_read(int fd, void *buf, size_t size); | 102 | int do_read(int fd, void *buf, size_t size); |
| 103 | void perf_session__update_sample_type(struct perf_session *self); | 103 | void perf_session__update_sample_type(struct perf_session *self); |
| 104 | 104 | ||
| 105 | #ifdef NO_NEWT_SUPPORT | ||
| 106 | static inline int perf_session__browse_hists(struct rb_root *hists __used, | ||
| 107 | u64 nr_hists __used, | ||
| 108 | u64 session_total __used, | ||
| 109 | const char *helpline __used, | ||
| 110 | const char *input_name __used) | ||
| 111 | { | ||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | #else | ||
| 115 | int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, | ||
| 116 | u64 session_total, const char *helpline, | ||
| 117 | const char *input_name); | ||
| 118 | #endif | ||
| 119 | |||
| 120 | static inline | 105 | static inline |
| 121 | struct machine *perf_session__find_host_machine(struct perf_session *self) | 106 | struct machine *perf_session__find_host_machine(struct perf_session *self) |
| 122 | { | 107 | { |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index b7c54eeed9c9..af301acc461c 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
| @@ -48,12 +48,6 @@ struct hist_entry { | |||
| 48 | u64 count_us; | 48 | u64 count_us; |
| 49 | u64 count_guest_sys; | 49 | u64 count_guest_sys; |
| 50 | u64 count_guest_us; | 50 | u64 count_guest_us; |
| 51 | |||
| 52 | /* | ||
| 53 | * XXX WARNING! | ||
| 54 | * thread _has_ to come after ms, see | ||
| 55 | * hist_browser__selected_thread in util/newt.c | ||
| 56 | */ | ||
| 57 | struct map_symbol ms; | 51 | struct map_symbol ms; |
| 58 | struct thread *thread; | 52 | struct thread *thread; |
| 59 | u64 ip; | 53 | u64 ip; |
