diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-26 17:03:38 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-26 17:03:38 +0200 |
commit | 7115e3fcf45514db7525a05365b10454ff7f345e (patch) | |
tree | 17450e6337d559cc35dae6a7a73abab01ac63f00 /tools/perf/util | |
parent | 1f6e05171bb5cc32a4d6437ab2269fc21d169ca7 (diff) | |
parent | c752d04066a36ae30b29795f3fa3f536292c1f8c (diff) |
Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
* 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (121 commits)
perf symbols: Increase symbol KSYM_NAME_LEN size
perf hists browser: Refuse 'a' hotkey on non symbolic views
perf ui browser: Use libslang to read keys
perf tools: Fix tracing info recording
perf hists browser: Elide DSO column when it is set to just one DSO, ditto for threads
perf hists: Don't consider filtered entries when calculating column widths
perf hists: Don't decay total_period for filtered entries
perf hists browser: Honour symbol_conf.show_{nr_samples,total_period}
perf hists browser: Do not exit on tab key with single event
perf annotate browser: Don't change selection line when returning from callq
perf tools: handle endianness of feature bitmap
perf tools: Add prelink suggestion to dso update message
perf script: Fix unknown feature comment
perf hists browser: Apply the dso and thread filters when merging new batches
perf hists: Move the dso and thread filters from hist_browser
perf ui browser: Honour the xterm colors
perf top tui: Give color hints just on the percentage, like on --stdio
perf ui browser: Make the colors configurable and change the defaults
perf tui: Remove unneeded call to newtCls on startup
perf hists: Don't format the percentage on hist_entry__snprintf
...
Fix up conflicts in arch/x86/kernel/kprobes.c manually.
Ingo's tree did the insane "add volatile to const array", which just
doesn't make sense ("volatile const"?). But we could remove the const
*and* make the array volatile to make doubly sure that gcc doesn't
optimize it away..
Also fix up kernel/trace/ring_buffer.c non-data-conflicts manually: the
reader_lock has been turned into a raw lock by the core locking merge,
and there was a new user of it introduced in this perf core merge. Make
sure that new use also uses the raw accessor functions.
Diffstat (limited to 'tools/perf/util')
33 files changed, 2552 insertions, 918 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index e01af2b1a46..bc8f4773d4d 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -16,6 +16,8 @@ #include "annotate.h" #include <pthread.h> +const char *disassembler_style; + int symbol__annotate_init(struct map *map __used, struct symbol *sym) { struct annotation *notes = symbol__annotation(sym); @@ -323,10 +325,15 @@ fallback: dso, dso->long_name, sym, sym->name); snprintf(command, sizeof(command), - "objdump --start-address=0x%016" PRIx64 - " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand", + "objdump %s%s --start-address=0x%016" PRIx64 + " --stop-address=0x%016" PRIx64 + " -d %s %s -C %s|grep -v %s|expand", + disassembler_style ? "-M " : "", + disassembler_style ? disassembler_style : "", map__rip_2objdump(map, sym->start), map__rip_2objdump(map, sym->end), + symbol_conf.annotate_asm_raw ? "" : "--no-show-raw", + symbol_conf.annotate_src ? "-S" : "", symfs_filename, filename); pr_debug("Executing: %s\n", command); diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index c2c28689680..d9072523d34 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -91,13 +91,18 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, #ifdef NO_NEWT_SUPPORT static inline int symbol__tui_annotate(struct symbol *sym __used, struct map *map __used, - int evidx __used, int refresh __used) + int evidx __used, + void(*timer)(void *arg) __used, + void *arg __used, int delay_secs __used) { return 0; } #else int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, - int refresh); + int nr_events, void(*timer)(void *arg), void *arg, + int delay_secs); #endif +extern const char *disassembler_style; + #endif /* __PERF_ANNOTATE_H */ diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index e191eb9a667..521c38a7919 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -200,7 +200,7 @@ static int __color_vfprintf(FILE *fp, const char *color, const char *fmt, * Auto-detect: */ if (perf_use_color_default < 0) { - if (isatty(1) || pager_in_use()) + if (isatty(fileno(fp)) || pager_in_use()) perf_use_color_default = 1; else perf_use_color_default = 0; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 72e9f4886b6..2f6bc89027d 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -533,3 +533,9 @@ bool perf_evlist__sample_id_all(const struct perf_evlist *evlist) first = list_entry(evlist->entries.next, struct perf_evsel, node); return first->attr.sample_id_all; } + +void perf_evlist__set_selected(struct perf_evlist *evlist, + struct perf_evsel *evsel) +{ + evlist->selected = evsel; +} diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index f3491500274..6be71fc5779 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -25,6 +25,7 @@ struct perf_evlist { struct pollfd *pollfd; struct thread_map *threads; struct cpu_map *cpus; + struct perf_evsel *selected; }; struct perf_evsel; @@ -56,6 +57,9 @@ void perf_evlist__munmap(struct perf_evlist *evlist); void perf_evlist__disable(struct perf_evlist *evlist); void perf_evlist__enable(struct perf_evlist *evlist); +void perf_evlist__set_selected(struct perf_evlist *evlist, + struct perf_evsel *evsel); + static inline void perf_evlist__set_maps(struct perf_evlist *evlist, struct cpu_map *cpus, struct thread_map *threads) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index e389815078d..b46f6e4bff3 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -39,6 +39,7 @@ void perf_evsel__init(struct perf_evsel *evsel, evsel->idx = idx; evsel->attr = *attr; INIT_LIST_HEAD(&evsel->node); + hists__init(&evsel->hists); } struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index b6c1ad123ca..76c0b2c49eb 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -7,6 +7,7 @@ #include <stdlib.h> #include <linux/list.h> #include <linux/kernel.h> +#include <sys/utsname.h> #include "evlist.h" #include "evsel.h" @@ -17,12 +18,19 @@ #include "session.h" #include "symbol.h" #include "debug.h" +#include "cpumap.h" static bool no_buildid_cache = false; static int event_count; static struct perf_trace_event_type *events; +static u32 header_argc; +static const char **header_argv; + +static int dsos__write_buildid_table(struct perf_header *header, int fd); +static int perf_session__cache_build_ids(struct perf_session *session); + int perf_header__push_event(u64 id, const char *name) { if (strlen(name) > MAX_EVENT_NAME) @@ -110,6 +118,1020 @@ static int write_padded(int fd, const void *bf, size_t count, return err; } +static int do_write_string(int fd, const char *str) +{ + u32 len, olen; + int ret; + + olen = strlen(str) + 1; + len = ALIGN(olen, NAME_ALIGN); + + /* write len, incl. \0 */ + ret = do_write(fd, &len, sizeof(len)); + if (ret < 0) + return ret; + + return write_padded(fd, str, olen, len); +} + +static char *do_read_string(int fd, struct perf_header *ph) +{ + ssize_t sz, ret; + u32 len; + char *buf; + + sz = read(fd, &len, sizeof(len)); + if (sz < (ssize_t)sizeof(len)) + return NULL; + + if (ph->needs_swap) + len = bswap_32(len); + + buf = malloc(len); + if (!buf) + return NULL; + + ret = read(fd, buf, len); + if (ret == (ssize_t)len) { + /* + * strings are padded by zeroes + * thus the actual strlen of buf + * may be less than len + */ + return buf; + } + + free(buf); + return NULL; +} + +int +perf_header__set_cmdline(int argc, const char **argv) +{ + int i; + + header_argc = (u32)argc; + + /* do not include NULL termination */ + header_argv = calloc(argc, sizeof(char *)); + if (!header_argv) + return -ENOMEM; + + /* + * must copy argv contents because it gets moved + * around during option parsing + */ + for (i = 0; i < argc ; i++) + header_argv[i] = argv[i]; + + return 0; +} + +static int write_trace_info(int fd, struct perf_header *h __used, + struct perf_evlist *evlist) +{ + return read_tracing_data(fd, &evlist->entries); +} + + +static int write_build_id(int fd, struct perf_header *h, + struct perf_evlist *evlist __used) +{ + struct perf_session *session; + int err; + + session = container_of(h, struct perf_session, header); + + err = dsos__write_buildid_table(h, fd); + if (err < 0) { + pr_debug("failed to write buildid table\n"); + return err; + } + if (!no_buildid_cache) + perf_session__cache_build_ids(session); + + return 0; +} + +static int write_hostname(int fd, struct perf_header *h __used, + struct perf_evlist *evlist __used) +{ + struct utsname uts; + int ret; + + ret = uname(&uts); + if (ret < 0) + return -1; + + return do_write_string(fd, uts.nodename); +} + +static int write_osrelease(int fd, struct perf_header *h __used, + struct perf_evlist *evlist __used) +{ + struct utsname uts; + int ret; + + ret = uname(&uts); + if (ret < 0) + return -1; + + return do_write_string(fd, uts.release); +} + +static int write_arch(int fd, struct perf_header *h __used, + struct perf_evlist *evlist __used) +{ + struct utsname uts; + int ret; + + ret = uname(&uts); + if (ret < 0) + return -1; + + return do_write_string(fd, uts.machine); +} + +static int write_version(int fd, struct perf_header *h __used, + struct perf_evlist *evlist __used) +{ + return do_write_string(fd, perf_version_string); +} + +static int write_cpudesc(int fd, struct perf_header *h __used, + struct perf_evlist *evlist __used) +{ +#ifndef CPUINFO_PROC +#define CPUINFO_PROC NULL +#endif + FILE *file; + char *buf = NULL; + char *s, *p; + const char *search = CPUINFO_PROC; + size_t len = 0; + int ret = -1; + + if (!search) + return -1; + + file = fopen("/proc/cpuinfo", "r"); + if (!file) + return -1; + + while (getline(&buf, &len, file) > 0) { + ret = strncmp(buf, search, strlen(search)); + if (!ret) + break; + } + + if (ret) + goto done; + + s = buf; + + p = strchr(buf, ':'); + if (p && *(p+1) == ' ' && *(p+2)) + s = p + 2; + p = strchr(s, '\n'); + if (p) + *p = '\0'; + + /* squash extra space characters (branding string) */ + p = s; + while (*p) { + if (isspace(*p)) { + char *r = p + 1; + char *q = r; + *p = ' '; + while (*q && isspace(*q)) + q++; + if (q != (p+1)) + while ((*r++ = *q++)); + } + p++; + } + ret = do_write_string(fd, s); +done: + free(buf); + fclose(file); + return ret; +} + +static int write_nrcpus(int fd, struct perf_header *h __used, + struct perf_evlist *evlist __used) +{ + long nr; + u32 nrc, nra; + int ret; + + nr = sysconf(_SC_NPROCESSORS_CONF); + if (nr < 0) + return -1; + + nrc = (u32)(nr & UINT_MAX); + + nr = sysconf(_SC_NPROCESSORS_ONLN); + if (nr < 0) + return -1; + + nra = (u32)(nr & UINT_MAX); + + ret = do_write(fd, &nrc, sizeof(nrc)); + if (ret < 0) + return ret; + + return do_write(fd, &nra, sizeof(nra)); +} + +static int write_event_desc(int fd, struct perf_header *h __used, + struct perf_evlist *evlist) +{ + struct perf_evsel *attr; + u32 nre = 0, nri, sz; + int ret; + + list_for_each_entry(attr, &evlist->entries, node) + nre++; + + /* + * write number of events + */ + ret = do_write(fd, &nre, sizeof(nre)); + if (ret < 0) + return ret; + + /* + * size of perf_event_attr struct + */ + sz = (u32)sizeof(attr->attr); + ret = do_write(fd, &sz, sizeof(sz)); + if (ret < 0) + return ret; + + list_for_each_entry(attr, &evlist->entries, node) { + + ret = do_write(fd, &attr->attr, sz); + if (ret < 0) + return ret; + /* + * write number of unique id per event + * there is one id per instance of an event + * + * copy into an nri to be independent of the + * type of ids, + */ + nri = attr->ids; + ret = do_write(fd, &nri, sizeof(nri)); + if (ret < 0) + return ret; + + /* + * write event string as passed on cmdline + */ + ret = do_write_string(fd, attr->name); + if (ret < 0) + return ret; + /* + * write unique ids for this event + */ + ret = do_write(fd, attr->id, attr->ids * sizeof(u64)); + if (ret < 0) + return ret; + } + return 0; +} + +static int write_cmdline(int fd, struct perf_header *h __used, + struct perf_evlist *evlist __used) +{ + char buf[MAXPATHLEN]; + char proc[32]; + u32 i, n; + int ret; + + /* + * actual atual path to perf binary + */ + sprintf(proc, "/proc/%d/exe", getpid()); + ret = readlink(proc, buf, sizeof(buf)); + if (ret <= 0) + return -1; + + /* readlink() does not add null termination */ + buf[ret] = '\0'; + + /* account for binary path */ + n = header_argc + 1; + + ret = do_write(fd, &n, sizeof(n)); + if (ret < 0) + return ret; + + ret = do_write_string(fd, buf); + if (ret < 0) + return ret; + + for (i = 0 ; i < header_argc; i++) { + ret = do_write_string(fd, header_argv[i]); + if (ret < 0) + return ret; + } + return 0; +} + +#define CORE_SIB_FMT \ + "/sys/devices/system/cpu/cpu%d/topology/core_siblings_list" +#define THRD_SIB_FMT \ + "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list" + +struct cpu_topo { + u32 core_sib; + u32 thread_sib; + char **core_siblings; + char **thread_siblings; +}; + +static int build_cpu_topo(struct cpu_topo *tp, int cpu) +{ + FILE *fp; + char filename[MAXPATHLEN]; + char *buf = NULL, *p; + size_t len = 0; + u32 i = 0; + int ret = -1; + + sprintf(filename, CORE_SIB_FMT, cpu); + fp = fopen(filename, "r"); + if (!fp) + return -1; + + if (getline(&buf, &len, fp) <= 0) + goto done; + + fclose(fp); + + p = strchr(buf, '\n'); + if (p) + *p = '\0'; + + for (i = 0; i < tp->core_sib; i++) { + if (!strcmp(buf, tp->core_siblings[i])) + break; + } + if (i == tp->core_sib) { + tp->core_siblings[i] = buf; + tp->core_sib++; + buf = NULL; + len = 0; + } + + sprintf(filename, THRD_SIB_FMT, cpu); + fp = fopen(filename, "r"); + if (!fp) + goto done; + + if (getline(&buf, &len, fp) <= 0) + goto done; + + p = strchr(buf, '\n'); + if (p) + *p = '\0'; + + for (i = 0; i < tp->thread_sib; i++) { + if (!strcmp(buf, tp->thread_siblings[i])) + break; + } + if (i == tp->thread_sib) { + tp->thread_siblings[i] = buf; + tp->thread_sib++; + buf = NULL; + } + ret = 0; +done: + if(fp) + fclose(fp); + free(buf); + return ret; +} + +static void free_cpu_topo(struct cpu_topo *tp) +{ + u32 i; + + if (!tp) + return; + + for (i = 0 ; i < tp->core_sib; i++) + free(tp->core_siblings[i]); + + for (i = 0 ; i < tp->thread_sib; i++) + free(tp->thread_siblings[i]); + + free(tp); +} + +static struct cpu_topo *build_cpu_topology(void) +{ + struct cpu_topo *tp; + void *addr; + u32 nr, i; + size_t sz; + long ncpus; + int ret = -1; + + ncpus = sysconf(_SC_NPROCESSORS_CONF); + if (ncpus < 0) + return NULL; + + nr = (u32)(ncpus & UINT_MAX); + + sz = nr * sizeof(char *); + + addr = calloc(1, sizeof(*tp) + 2 * sz); + if (!addr) + return NULL; + + tp = addr; + + addr += sizeof(*tp); + tp->core_siblings = addr; + addr += sz; + tp->thread_siblings = addr; + + for (i = 0; i < nr; i++) { + ret = build_cpu_topo(tp, i); + if (ret < 0) + break; + } + if (ret) { + free_cpu_topo(tp); + tp = NULL; + } + return tp; +} + +static int write_cpu_topology(int fd, struct perf_header *h __used, + struct perf_evlist *evlist __used) +{ + struct cpu_topo *tp; + u32 i; + int ret; + + tp = build_cpu_topology(); + if (!tp) + return -1; + + ret = do_write(fd, &tp->core_sib, sizeof(tp->core_sib)); + if (ret < 0) + goto done; + + for (i = 0; i < tp->core_sib; i++) { + ret = do_write_string(fd, tp->core_siblings[i]); + if (ret < 0) + goto done; + } + ret = do_write(fd, &tp->thread_sib, sizeof(tp->thread_sib)); + if (ret < 0) + goto done; + + for (i = 0; i < tp->thread_sib; i++) { + ret = do_write_string(fd, tp->thread_siblings[i]); + if (ret < 0) + break; + } +done: + free_cpu_topo(tp); + return ret; +} + + + +static int write_total_mem(int fd, struct perf_header *h __used, + struct perf_evlist *evlist __used) +{ + char *buf = NULL; + FILE *fp; + size_t len = 0; + int ret = -1, n; + uint64_t mem; + + fp = fopen("/proc/meminfo", "r"); + if (!fp) + return -1; + + while (getline(&buf, &len, fp) > 0) { + ret = strncmp(buf, "MemTotal:", 9); + if (!ret) + break; + } + if (!ret) { + n = sscanf(buf, "%*s %"PRIu64, &mem); + if (n == 1) + ret = do_write(fd, &mem, sizeof(mem)); + } + free(buf); + fclose(fp); + return ret; +} + +static int write_topo_node(int fd, int node) +{ + char str[MAXPATHLEN]; + char field[32]; + char *buf = NULL, *p; + size_t len = 0; + FILE *fp; + u64 mem_total, mem_free, mem; + int ret = -1; + + sprintf(str, "/sys/devices/system/node/node%d/meminfo", node); + fp = fopen(str, "r"); + if (!fp) + return -1; + + while (getline(&buf, &len, fp) > 0) { + /* skip over invalid lines */ + if (!strchr(buf, ':')) + continue; + if (sscanf(buf, "%*s %*d %s %"PRIu64, field, &mem) != 2) + goto done; + if (!strcmp(field, "MemTotal:")) + mem_total = mem; + if (!strcmp(field, "MemFree:")) + mem_free = mem; + } + + fclose(fp); + + ret = do_write(fd, &mem_total, sizeof(u64)); + if (ret) + goto done; + + ret = do_write(fd, &mem_free, sizeof(u64)); + if (ret) + goto done; + + ret = -1; + sprintf(str, "/sys/devices/system/node/node%d/cpulist", node); + + fp = fopen(str, "r"); + if (!fp) + goto done; + + if (getline(&buf, &len, fp) <= 0) + goto done; + + p = strchr(buf, '\n'); + if (p) + *p = '\0'; + + ret = do_write_string(fd, buf); +done: + free(buf); + fclose(fp); + return ret; +} + +static int write_numa_topology(int fd, struct perf_header *h __used, + struct perf_evlist *evlist __used) +{ + char *buf = NULL; + size_t len = 0; + FILE *fp; + struct cpu_map *node_map = NULL; + char *c; + u32 nr, i, j; + int ret = -1; + + fp = fopen("/sys/devices/system/node/online", "r"); + if (!fp) + return -1; + + if (getline(&buf, &len, fp) <= 0) + goto done; + + c = strchr(buf, '\n'); + if (c) + *c = '\0'; + + node_map = cpu_map__new(buf); + if (!node_map) + goto done; + + nr = (u32)node_map->nr; + + ret = do_write(fd, &nr, sizeof(nr)); + if (ret < 0) + goto done; + + for (i = 0; i < nr; i++) { + j = (u32)node_map->map[i]; + ret = do_write(fd, &j, sizeof(j)); + if (ret < 0) + break; + + ret = write_topo_node(fd, i); + if (ret < 0) + break; + } +done: + free(buf); + fclose(fp); + free(node_map); + return ret; +} + +/* + * default get_cpuid(): nothing gets recorded + * actual implementation must be in arch/$(ARCH)/util/header.c + */ +int __attribute__((weak)) get_cpuid(char *buffer __used, size_t sz __used) +{ + return -1; +} + +static int write_cpuid(int fd, struct perf_header *h __used, + struct perf_evlist *evlist __used) +{ + char buffer[64]; + int ret; + + ret = get_cpuid(buffer, sizeof(buffer)); + if (!ret) + goto write_it; + + return -1; +write_it: + return do_write_string(fd, buffer); +} + +static void print_hostname(struct perf_header *ph, int fd, FILE *fp) +{ + char *str = do_read_string(fd, ph); + fprintf(fp, "# hostname : %s\n", str); + free(str); +} + +static void print_osrelease(struct perf_header *ph, int fd, FILE *fp) +{ + char *str = do_read_string(fd, ph); + fprintf(fp, "# os release : %s\n", str); + free(str); +} + +static void print_arch(struct perf_header *ph, int fd, FILE *fp) +{ + char *str = do_read_string(fd, ph); + fprintf(fp, "# arch : %s\n", str); + free(str); +} + +static void print_cpudesc(struct perf_header *ph, int fd, FILE *fp) +{ + char *str = do_read_string(fd, ph); + fprintf(fp, "# cpudesc : %s\n", str); + free(str); +} + +static void print_nrcpus(struct perf_header *ph, int fd, FILE *fp) +{ + ssize_t ret; + u32 nr; + + ret = read(fd, &nr, sizeof(nr)); + if (ret != (ssize_t)sizeof(nr)) + nr = -1; /* interpreted as error */ + + if (ph->needs_swap) + nr = bswap_32(nr); + + fprintf(fp, "# nrcpus online : %u\n", nr); + + ret = read(fd, &nr, sizeof(nr)); + if (ret != (ssize_t)sizeof(nr)) + nr = -1; /* interpreted as error */ + + if (ph->needs_swap) + nr = bswap_32(nr); + + fprintf(fp, "# nrcpus avail : %u\n", nr); +} + +static void print_version(struct perf_header *ph, int fd, FILE *fp) +{ + char *str = do_read_string(fd, ph); + fprintf(fp, "# perf version : %s\n", str); + free(str); +} + +static void print_cmdline(struct perf_header *ph, int fd, FILE *fp) +{ + ssize_t ret; + char *str; + u32 nr, i; + + ret = read(fd, &nr, sizeof(nr)); + if (ret != (ssize_t)sizeof(nr)) + return; + + if (ph->needs_swap) + nr = bswap_32(nr); + + fprintf(fp, "# cmdline : "); + + for (i = 0; i < nr; i++) { + str = do_read_string(fd, ph); + fprintf(fp, "%s ", str); + free(str); + } + fputc('\n', fp); +} + +static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp) +{ + ssize_t ret; + u32 nr, i; + char *str; + + ret = read(fd, &nr, sizeof(nr)); + if (ret != (ssize_t)sizeof(nr)) + return; + + if (ph->needs_swap) + nr = bswap_32(nr); + + for (i = 0; i < nr; i++) { + str = do_read_string(fd, ph); + fprintf(fp, "# sibling cores : %s\n", str); + free(str); + } + + ret = read(fd, &nr, sizeof(nr)); + if (ret != (ssize_t)sizeof(nr)) + return; + + if (ph->needs_swap) + nr = bswap_32(nr); + + for (i = 0; i < nr; i++) { + str = do_read_string(fd, ph); + fprintf(fp, "# sibling threads : %s\n", str); + free(str); + } +} + +static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) +{ + struct perf_event_attr attr; + uint64_t id; + void *buf = NULL; + char *str; + u32 nre, sz, nr, i, j, msz; + int ret; + + /* number of events */ + ret = read(fd, &nre, sizeof(nre)); + if (ret != (ssize_t)sizeof(nre)) + goto error; + + if (ph->needs_swap) + nre = bswap_32(nre); + + ret = read(fd, &sz, sizeof(sz)); + if (ret != (ssize_t)sizeof(sz)) + goto error; + + if (ph->needs_swap) + sz = bswap_32(sz); + + /* + * ensure it is at least to our ABI rev + */ + if (sz < (u32)sizeof(attr)) + goto error; + + memset(&attr, 0, sizeof(attr)); + + /* read entire region to sync up to next field */ + buf = malloc(sz); + if (!buf) + goto error; + + msz = sizeof(attr); + if (sz < msz) + msz = sz; + + for (i = 0 ; i < nre; i++) { + + ret = read(fd, buf, sz); + if (ret != (ssize_t)sz) + goto error; + + if (ph->needs_swap) + perf_event__attr_swap(buf); + + memcpy(&attr, buf, msz); + + ret = read(fd, &nr, sizeof(nr)); + if (ret != (ssize_t)sizeof(nr)) + goto error; + + if (ph->needs_swap) + nr = bswap_32(nr); + + str = do_read_string(fd, ph); + fprintf(fp, "# event : name = %s, ", str); + free(str); + + fprintf(fp, "type = %d, config = 0x%"PRIx64 + ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64, + attr.type, + (u64)attr.config, + (u64)attr.config1, + (u64)attr.config2); + + fprintf(fp, ", excl_usr = %d, excl_kern = %d", + attr.exclude_user, + attr.exclude_kernel); + + if (nr) + fprintf(fp, ", id = {"); + + for (j = 0 ; j < nr; j++) { + ret = read(fd, &id, sizeof(id)); + if (ret != (ssize_t)sizeof(id)) + goto error; + + if (ph->needs_swap) + id = bswap_64(id); + + if (j) + fputc(',', fp); + + fprintf(fp, " %"PRIu64, id); + } + if (nr && j == nr) + fprintf(fp, " }"); + fputc('\n', fp); + } + free(buf); + return; +error: + fprintf(fp, "# event desc: not available or unable to read\n"); +} + +static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp) +{ + uint64_t mem; + ssize_t ret; + + ret = read(fd, &mem, sizeof(mem)); + if (ret != sizeof(mem)) + goto error; + + if (h->needs_swap) + mem = bswap_64(mem); + + fprintf(fp, "# total memory : %"PRIu64" kB\n", mem); + return; +error: + fprintf(fp, "# total memory : unknown\n"); +} + +static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp) +{ + ssize_t ret; + u32 nr, c, i; + char *str; + uint64_t mem_total, mem_free; + + /* nr nodes */ + ret = read(fd, &nr, sizeof(nr)); + if (ret != (ssize_t)sizeof(nr)) + goto error; + + if (h->needs_swap) + nr = bswap_32(nr); + + for (i = 0; i < nr; i++) { + + /* node number */ + ret = read(fd, &c, sizeof(c)); + if (ret != (ssize_t)sizeof(c)) + goto error; + + if (h->needs_swap) + c = bswap_32(c); + + ret = read(fd, &mem_total, sizeof(u64)); + if (ret != sizeof(u64)) + goto error; + + ret = read(fd, &mem_free, sizeof(u64)); + if (ret != sizeof(u64)) + goto error; + + if (h->needs_swap) { + mem_total = bswap_64(mem_total); + mem_free = bswap_64(mem_free); + } + + fprintf(fp, "# node%u meminfo : total = %"PRIu64" kB," + " free = %"PRIu64" kB\n", + c, + mem_total, + mem_free); + + str = do_read_string(fd, h); + fprintf(fp, "# node%u cpu list : %s\n", c, str); + free(str); + } + return; +error: + fprintf(fp, "# numa topology : not available\n"); +} + +static void print_cpuid(struct perf_header *ph, int fd, FILE *fp) +{ + char *str = do_read_string(fd, ph); + fprintf(fp, "# cpuid : %s\n", str); + free(str); +} + +struct feature_ops { + int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); + void (*print)(struct perf_header *h, int fd, FILE *fp); + const char *name; + bool full_only; +}; + +#define FEAT_OPA(n, w, p) \ + [n] = { .name = #n, .write = w, .print = p } +#define FEAT_OPF(n, w, p) \ + [n] = { .name = #n, .write = w, .print = p, .full_only = true } + +static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { + FEAT_OPA(HEADER_TRACE_INFO, write_trace_info, NULL), + FEAT_OPA(HEADER_BUILD_ID, write_build_id, NULL), + FEAT_OPA(HEADER_HOSTNAME, write_hostname, print_hostname), + FEAT_OPA(HEADER_OSRELEASE, write_osrelease, print_osrelease), + FEAT_OPA(HEADER_VERSION, write_version, print_version), + FEAT_OPA(HEADER_ARCH, write_arch, print_arch), + FEAT_OPA(HEADER_NRCPUS, write_nrcpus, print_nrcpus), + FEAT_OPA(HEADER_CPUDESC, write_cpudesc, print_cpudesc), + FEAT_OPA(HEADER_CPUID, write_cpuid, print_cpuid), + FEAT_OPA(HEADER_TOTAL_MEM, write_total_mem, print_total_mem), + FEAT_OPA(HEADER_EVENT_DESC, write_event_desc, print_event_desc), + FEAT_OPA(HEADER_CMDLINE, write_cmdline, print_cmdline), + FEAT_OPF(HEADER_CPU_TOPOLOGY, write_cpu_topology, print_cpu_topology), + FEAT_OPF(HEADER_NUMA_TOPOLOGY, write_numa_topology, print_numa_topology), +}; + +struct header_print_data { + FILE *fp; + bool full; /* extended list of headers */ +}; + +static int perf_file_section__fprintf_info(struct perf_file_section *section, + struct perf_header *ph, + int feat, int fd, void *data) +{ + struct header_print_data *hd = data; + + if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { + pr_debug("Failed to lseek to %" PRIu64 " offset for feature " + "%d, continuing...\n", section->offset, feat); + return 0; + } + if (feat < HEADER_TRACE_INFO || feat >= HEADER_LAST_FEATURE) { + pr_warning("unknown feature %d\n", feat); + return -1; + } + if (!feat_ops[feat].print) + return 0; + + if (!feat_ops[feat].full_only || hd->full) + feat_ops[feat].print(ph, fd, hd->fp); + else + fprintf(hd->fp, "# %s info available, use -I to display\n", + feat_ops[feat].name); + + return 0; +} + +int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) +{ + struct header_print_data hd; + struct perf_header *header = &session->header; + int fd = session->fd; + hd.fp = fp; + hd.full = full; + + perf_header__process_sections(header, fd, &hd, + perf_file_section__fprintf_info); + return 0; +} + #define dsos__for_each_with_build_id(pos, head) \ list_for_each_entry(pos, head, node) \ if (!pos->has_build_id) \ @@ -267,7 +1289,7 @@ int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) if (access(linkname, F_OK)) goto out_free; - if (readlink(linkname, filename, size) < 0) + if (readlink(linkname, filename, size - 1) < 0) goto out_free; if (unlink(linkname)) @@ -356,15 +1378,41 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with return ret; } +static int do_write_feat(int fd, struct perf_header *h, int type, + struct perf_file_section **p, + struct perf_evlist *evlist) +{ + int err; + int ret = 0; + + if (perf_header__has_feat(h, type)) { + + (*p)->offset = lseek(fd, 0, SEEK_CUR); + + err = feat_ops[type].write(fd, h, evlist); + if (err < 0) { + pr_debug("failed to write feature %d\n", type); + + /* undo anything written */ + lseek(fd, (*p)->offset, SEEK_SET); + + return -1; + } + (*p)->size = lseek(fd, 0, SEEK_CUR) - (*p)->offset; + (*p)++; + } + return ret; +} + static int perf_header__adds_write(struct perf_header *header, struct perf_evlist *evlist, int fd) { int nr_sections; struct perf_session *session; - struct perf_file_section *feat_sec; + struct perf_file_section *feat_sec, *p; int sec_size; u64 sec_start; - int idx = 0, err; + int err; session = container_of(header, struct perf_session, header); @@ -376,7 +1424,7 @@ static int perf_header__adds_write(struct perf_header *header, if (!nr_sections) return 0; - feat_sec = calloc(sizeof(*feat_sec), nr_sections); + feat_sec = p = calloc(sizeof(*feat_sec), nr_sections); if (feat_sec == NULL) return -ENOMEM; @@ -385,36 +1433,69 @@ static int perf_header__adds_write(struct perf_header *header, sec_start = header->data_offset + header->data_size; lseek(fd, sec_start + sec_size, SEEK_SET); - if (perf_header__has_feat(header, HEADER_TRACE_INFO)) { - struct perf_file_section *trace_sec; - - trace_sec = &feat_sec[idx++]; + err = do_write_feat(fd, header, HEADER_TRACE_INFO, &p, evlist); + if (err) + goto out_free; - /* Write trace info */ - trace_sec->offset = lseek(fd, 0, SEEK_CUR); - read_tracing_data(fd, &evlist->entries); - trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; + err = do_write_feat(fd, header, HEADER_BUILD_ID, &p, evlist); + if (err) { + perf_header__clear_feat(header, HEADER_BUILD_ID); + goto out_free; } - if (perf_header__has_feat(header, HEADER_BUILD_ID)) { - struct perf_file_section *buildid_sec; + err = do_write_feat(fd, header, HEADER_HOSTNAME, &p, evlist); + if (err) + perf_header__clear_feat(header, HEADER_HOSTNAME); - buildid_sec = &feat_sec[idx++]; + err = do_write_feat(fd, header, HEADER_OSRELEASE, &p, evlist); + if (err) + perf_header__clear_feat(header, HEADER_OSRELEASE); - /* Write build-ids */ - buildid_sec->offset = lseek(fd, 0, SEEK_CUR); - err = dsos__write_buildid_table(header, fd); - if (err < 0) { - pr_debug("failed to write buildid table\n"); - goto out_free; - } - buildid_sec->size = lseek(fd, 0, SEEK_CUR) - - buildid_sec->offset; - if (!no_buildid_cache) - perf_session__cache_build_ids(session); - } + err = do_write_feat(fd, header, HEADER_VERSION, &p, evlist); + if (err) + perf_header__clear_feat(header, HEADER_VERSION); + + err = do_write_feat(fd, header, HEADER_ARCH, &p, evlist); + if (err) + perf_header__clear_feat(header, HEADER_ARCH); + + err = do_write_feat(fd, header, HEADER_NRCPUS, &p, evlist); + if (err) + perf_header__clear_feat(header, HEADER_NRCPUS); + + err = do_write_feat(fd, header, HEADER_CPUDESC, &p, evlist); + if (err) + perf_header__clear_feat(header, HEADER_CPUDESC); + + err = do_write_feat(fd, header, HEADER_CPUID, &p, evlist); + if (err) + perf_header__clear_feat(header, HEADER_CPUID); + + err = do_write_feat(fd, header, HEADER_TOTAL_MEM, &p, evlist); + if (err) + perf_header__clear_feat(header, HEADER_TOTAL_MEM); + + err = do_write_feat(fd, header, HEADER_CMDLINE, &p, evlist); + if (err) + perf_header__clear_feat(header, HEADER_CMDLINE); + + err = do_write_feat(fd, header, HEADER_EVENT_DESC, &p, evlist); + if (err) + perf_header__clear_feat(header, HEADER_EVENT_DESC); + + err = do_write_feat(fd, header, HEADER_CPU_TOPOLOGY, &p, evlist); + if (err) + perf_header__clear_feat(header, HEADER_CPU_TOPOLOGY); + + err = do_write_feat(fd, header, HEADER_NUMA_TOPOLOGY, &p, evlist); + if (err) + perf_header__clear_feat(header, HEADER_NUMA_TOPOLOGY); lseek(fd, sec_start, SEEK_SET); + /* + * may write more than needed due to dropped feature, but + * this is okay, reader will skip the mising entries + */ err = do_write(fd, feat_sec, sec_size); if (err < 0) pr_debug("failed to write feature section\n"); @@ -554,9 +1635,10 @@ static int perf_header__getbuffer64(struct perf_header *header, } int perf_header__process_sections(struct perf_header *header, int fd, + void *data, int (*process)(struct perf_file_section *section, - struct perf_header *ph, - int feat, int fd)) + struct perf_header *ph, + int feat, int fd, void *data)) { struct perf_file_section *feat_sec; int nr_sections; @@ -584,7 +1666,7 @@ int perf_header__process_sections(struct perf_header *header, int fd, if (perf_header__has_feat(header, feat)) { struct perf_file_section *sec = &feat_sec[idx++]; - err = process(sec, header, feat, fd); + err = process(sec, header, feat, fd, data); if (err < 0) break; } @@ -621,21 +1703,41 @@ int perf_file_header__read(struct perf_file_header *header, bitmap_zero(header->adds_features, HEADER_FEAT_BITS); else return -1; + } else if (ph->needs_swap) { + unsigned int i; + /* + * feature bitmap is declared as an array of unsigned longs -- + * not good since its size can differ between the host that + * generated the data file and the host analyzing the file. + * + * We need to handle endianness, but we don't know the size of + * the unsigned long where the file was generated. Take a best + * guess at determining it: try 64-bit swap first (ie., file + * created on a 64-bit host), and check if the hostname feature + * bit is set (this feature bit is forced on as of fbe96f2). + * If the bit is not, undo the 64-bit swap and try a 32-bit + * swap. If the hostname bit is still not set (e.g., older data + * file), punt and fallback to the original behavior -- + * clearing all feature bits and setting buildid. + */ + for (i = 0; i < BITS_TO_LONGS(HEADER_FEAT_BITS); ++i) + header->adds_features[i] = bswap_64(header->adds_features[i]); + + if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { + for (i = 0; i < BITS_TO_LONGS(HEADER_FEAT_BITS); ++i) { + header->adds_features[i] = bswap_64(header->adds_features[i]); + header->adds_features[i] = bswap_32(header->adds_features[i]); + } + } + + if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { + bitmap_zero(header->adds_features, HEADER_FEAT_BITS); + set_bit(HEADER_BUILD_ID, header->adds_features); + } } memcpy(&ph->adds_features, &header->adds_features, sizeof(ph->adds_features)); - /* - * FIXME: hack that assumes that if we need swap the perf.data file - * may be coming from an arch with a different word-size, ergo different - * DEFINE_BITMAP format, investigate more later, but for now its mostly - * safe to assume that we have a build-id section. Trace files probably - * have several other issues in this realm anyway... - */ - if (ph->needs_swap) { - memset(&ph->adds_features, 0, sizeof(ph->adds_features)); - perf_header__set_feat(ph, HEADER_BUILD_ID); - } ph->event_offset = header->event_types.offset; ph->event_size = header->event_types.size; @@ -796,7 +1898,7 @@ out: static int perf_file_section__process(struct perf_file_section *section, struct perf_header *ph, - int feat, int fd) + int feat, int fd, void *data __used) { if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { pr_debug("Failed to lseek to %" PRIu64 " offset for feature " @@ -813,6 +1915,21 @@ static int perf_file_section__process(struct perf_file_section *section, if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) pr_debug("Failed to read buildids, continuing...\n"); break; + + case HEADER_HOSTNAME: + case HEADER_OSRELEASE: + case HEADER_VERSION: + case HEADER_ARCH: + case HEADER_NRCPUS: + case HEADER_CPUDESC: + case HEADER_CPUID: + case HEADER_TOTAL_MEM: + case HEADER_CMDLINE: + case HEADER_EVENT_DESC: + case HEADER_CPU_TOPOLOGY: + case HEADER_NUMA_TOPOLOGY: + break; + default: pr_debug("unknown feature %d, continuing...\n", feat); } @@ -935,7 +2052,8 @@ int perf_session__read_header(struct perf_session *session, int fd) event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); } - perf_header__process_sections(header, fd, perf_file_section__process); + perf_header__process_sections(header, fd, NULL, + perf_file_section__process); lseek(fd, header->data_offset, SEEK_SET); @@ -1100,15 +2218,29 @@ int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, struct perf_session *session __unused) { union perf_event ev; + struct tracing_data *tdata; ssize_t size = 0, aligned_size = 0, padding; int err __used = 0; + /* + * We are going to store the size of the data followed + * by the data contents. Since the fd descriptor is a pipe, + * we cannot seek back to store the size of the data once + * we know it. Instead we: + * + * - write the tracing data to the temp file + * - get/write the data size to pipe + * - write the tracing data from the temp file + * to the pipe + */ + tdata = tracing_data_get(&evlist->entries, fd, true); + if (!tdata) + return -1; + memset(&ev, 0, sizeof(ev)); ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; - size = read_tracing_data_size(fd, &evlist->entries); - if (size <= 0) - return size; + size = tdata->size; aligned_size = ALIGN(size, sizeof(u64)); padding = aligned_size - size; ev.tracing_data.header.size = sizeof(ev.tracing_data); @@ -1116,7 +2248,12 @@ int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, process(&ev, NULL, session); - err = read_tracing_data(fd, &evlist->entries); + /* + * The put function will copy all the tracing data + * stored in temp file to the pipe. + */ + tracing_data_put(tdata); + write_padded(fd, NULL, 0, padding); return aligned_size; diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 1886256768a..3d5a742f4a2 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -12,6 +12,20 @@ enum { HEADER_TRACE_INFO = 1, HEADER_BUILD_ID, + + HEADER_HOSTNAME, + HEADER_OSRELEASE, + HEADER_VERSION, + HEADER_ARCH, + HEADER_NRCPUS, + HEADER_CPUDESC, + HEADER_CPUID, + HEADER_TOTAL_MEM, + HEADER_CMDLINE, + HEADER_EVENT_DESC, + HEADER_CPU_TOPOLOGY, + HEADER_NUMA_TOPOLOGY, + HEADER_LAST_FEATURE, }; @@ -68,10 +82,15 @@ void perf_header__set_feat(struct perf_header *header, int feat); void perf_header__clear_feat(struct perf_header *header, int feat); bool perf_header__has_feat(const struct perf_header *header, int feat); +int perf_header__set_cmdline(int argc, const char **argv); + int perf_header__process_sections(struct perf_header *header, int fd, + void *data, int (*process)(struct perf_file_section *section, - struct perf_header *ph, - int feat, int fd)); + struct perf_header *ph, + int feat, int fd, void *data)); + +int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, const char *name, bool is_kallsyms); @@ -104,4 +123,10 @@ int perf_event__synthesize_build_id(struct dso *pos, u16 misc, struct perf_session *session); int perf_event__process_build_id(union perf_event *event, struct perf_session *session); + +/* + * arch specific callback + */ +int get_cpuid(char *buffer, size_t sz); + #endif /* __PERF_HEADER_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 677e1da6bb3..f6a993963a1 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -6,6 +6,11 @@ #include "sort.h" #include <math.h> +static bool hists__filter_entry_by_dso(struct hists *hists, + struct hist_entry *he); +static bool hists__filter_entry_by_thread(struct hists *hists, + struct hist_entry *he); + enum hist_filter { HIST_FILTER__DSO, HIST_FILTER__THREAD, @@ -18,56 +23,56 @@ struct callchain_param callchain_param = { .order = ORDER_CALLEE }; -u16 hists__col_len(struct hists *self, enum hist_column col) +u16 hists__col_len(struct hists *hists, enum hist_column col) { - return self->col_len[col]; + return hists->col_len[col]; } -void hists__set_col_len(struct hists *self, enum hist_column col, u16 len) +void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len) { - self->col_len[col] = len; + hists->col_len[col] = len; } -bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len) +bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len) { - if (len > hists__col_len(self, col)) { - hists__set_col_len(self, col, len); + if (len > hists__col_len(hists, col)) { + hists__set_col_len(hists, col, len); return true; } return false; } -static void hists__reset_col_len(struct hists *self) +static void hists__reset_col_len(struct hists *hists) { enum hist_column col; for (col = 0; col < HISTC_NR_COLS; ++col) - hists__set_col_len(self, col, 0); + hists__set_col_len(hists, col, 0); } -static void hists__calc_col_len(struct hists *self, struct hist_entry *h) +static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) { u16 len; if (h->ms.sym) - hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); + hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen); else { const unsigned int unresolved_col_width = BITS_PER_LONG / 4; - if (hists__col_len(self, HISTC_DSO) < unresolved_col_width && + if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width && !symbol_conf.col_width_list_str && !symbol_conf.field_sep && !symbol_conf.dso_list) - hists__set_col_len(self, HISTC_DSO, + hists__set_col_len(hists, HISTC_DSO, unresolved_col_width); } len = thread__comm_len(h->thread); - if (hists__new_col_len(self, HISTC_COMM, len)) - hists__set_col_len(self, HISTC_THREAD, len + 6); + if (hists__new_col_len(hists, HISTC_COMM, len)) + hists__set_col_len(hists, HISTC_THREAD, len + 6); if (h->ms.map) { len = dso__name_len(h->ms.map->dso); - hists__new_col_len(self, HISTC_DSO, len); + hists__new_col_len(hists, HISTC_DSO, len); } } @@ -92,6 +97,67 @@ static void hist_entry__add_cpumode_period(struct hist_entry *self, } } +static void hist_entry__decay(struct hist_entry *he) +{ + he->period = (he->period * 7) / 8; + he->nr_events = (he->nr_events * 7) / 8; +} + +static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) +{ + u64 prev_period = he->period; + + if (prev_period == 0) + return true; + + hist_entry__decay(he); + + if (!he->filtered) + hists->stats.total_period -= prev_period - he->period; + + return he->period == 0; +} + +static void __hists__decay_entries(struct hists *hists, bool zap_user, + bool zap_kernel, bool threaded) +{ + struct rb_node *next = rb_first(&hists->entries); + struct hist_entry *n; + + while (next) { + n = rb_entry(next, struct hist_entry, rb_node); + next = rb_next(&n->rb_node); + /* + * We may be annotating this, for instance, so keep it here in + * case some it gets new samples, we'll eventually free it when + * the user stops browsing and it agains gets fully decayed. + */ + if (((zap_user && n->level == '.') || + (zap_kernel && n->level != '.') || + hists__decay_entry(hists, n)) && + !n->used) { + rb_erase(&n->rb_node, &hists->entries); + + if (sort__need_collapse || threaded) + rb_erase(&n->rb_node_in, &hists->entries_collapsed); + + hist_entry__free(n); + --hists->nr_entries; + } + } +} + +void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) +{ + return __hists__decay_entries(hists, zap_user, zap_kernel, false); +} + +void hists__decay_entries_threaded(struct hists *hists, + bool zap_user, bool zap_kernel) +{ + return __hists__decay_entries(hists, zap_user, zap_kernel, true); +} + /* * histogram, sorted on item, collects periods */ @@ -113,11 +179,12 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) return self; } -static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h) +static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) { if (!h->filtered) { - hists__calc_col_len(self, h); - ++self->nr_entries; + hists__calc_col_len(hists, h); + ++hists->nr_entries; + hists->stats.total_period += h->period; } } @@ -128,11 +195,11 @@ static u8 symbol__parent_filter(const struct symbol *parent) return 0; } -struct hist_entry *__hists__add_entry(struct hists *self, +struct hist_entry *__hists__add_entry(struct hists *hists, struct addr_location *al, struct symbol *sym_parent, u64 period) { - struct rb_node **p = &self->entries.rb_node; + struct rb_node **p; struct rb_node *parent = NULL; struct hist_entry *he; struct hist_entry entry = { @@ -150,9 +217,13 @@ struct hist_entry *__hists__add_entry(struct hists *self, }; int cmp; + pthread_mutex_lock(&hists->lock); + + p = &hists->entries_in->rb_node; + while (*p != NULL) { parent = *p; - he = rb_entry(parent, struct hist_entry, rb_node); + he = rb_entry(parent, struct hist_entry, rb_node_in); cmp = hist_entry__cmp(&entry, he); @@ -170,12 +241,14 @@ struct hist_entry *__hists__add_entry(struct hists *self, he = hist_entry__new(&entry); if (!he) - return NULL; - rb_link_node(&he->rb_node, parent, p); - rb_insert_color(&he->rb_node, &self->entries); - hists__inc_nr_entries(self, he); + goto out_unlock; + + rb_link_node(&he->rb_node_in, parent, p); + rb_insert_color(&he->rb_node_in, hists->entries_in); out: hist_entry__add_cpumode_period(he, al->cpumode, period); +out_unlock: + pthread_mutex_unlock(&hists->lock); return he; } @@ -222,7 +295,7 @@ void hist_entry__free(struct hist_entry *he) * collapse the histogram */ -static bool hists__collapse_insert_entry(struct hists *self, +static bool hists__collapse_insert_entry(struct hists *hists, struct rb_root *root, struct hist_entry *he) { @@ -233,15 +306,16 @@ static bool hists__collapse_insert_entry(struct hists *self, while (*p != NULL) { parent = *p; - iter = rb_entry(parent, struct hist_entry, rb_node); + iter = rb_entry(parent, struct hist_entry, rb_node_in); cmp = hist_entry__collapse(iter, he); if (!cmp) { iter->period += he->period; + iter->nr_events += he->nr_events; if (symbol_conf.use_callchain) { - callchain_cursor_reset(&self->callchain_cursor); - callchain_merge(&self->callchain_cursor, iter->callchain, + callchain_cursor_reset(&hists->callchain_cursor); + callchain_merge(&hists->callchain_cursor, iter->callchain, he->callchain); } hist_entry__free(he); @@ -254,35 +328,70 @@ static bool hists__collapse_insert_entry(struct hists *self, p = &(*p)->rb_right; } - rb_link_node(&he->rb_node, parent, p); - rb_insert_color(&he->rb_node, root); + rb_link_node(&he->rb_node_in, parent, p); + rb_insert_color(&he->rb_node_in, root); return true; } -void hists__collapse_resort(struct hists *self) +static struct rb_root *hists__get_rotate_entries_in(struct hists *hists) +{ + struct rb_root *root; + + pthread_mutex_lock(&hists->lock); + + root = hists->entries_in; + if (++hists->entries_in > &hists->entries_in_array[1]) + hists->entries_in = &hists->entries_in_array[0]; + + pthread_mutex_unlock(&hists->lock); + + return root; +} + +static void hists__apply_filters(struct hists *hists, struct hist_entry *he) +{ + hists__filter_entry_by_dso(hists, he); + hists__filter_entry_by_thread(hists, he); +} + +static void __hists__collapse_resort(struct hists *hists, bool threaded) { - struct rb_root tmp; + struct rb_root *root; struct rb_node *next; struct hist_entry *n; - if (!sort__need_collapse) + if (!sort__need_collapse && !threaded) return; - tmp = RB_ROOT; - next = rb_first(&self->entries); - self->nr_entries = 0; - hists__reset_col_len(self); + root = hists__get_rotate_entries_in(hists); + next = rb_first(root); + hists->stats.total_period = 0; while (next) { - n = rb_entry(next, struct hist_entry, rb_node); - next = rb_next(&n->rb_node); - - rb_erase(&n->rb_node, &self->entries); - if (hists__collapse_insert_entry(self, &tmp, n)) - hists__inc_nr_entries(self, n); + n = rb_entry(next, struct hist_entry, rb_node_in); + next = rb_next(&n->rb_node_in); + + rb_erase(&n->rb_node_in, root); + if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n)) { + /* + * If it wasn't combined with one of the entries already + * collapsed, we need to apply the filters that may have + * been set by, say, the hist_browser. + */ + hists__apply_filters(hists, n); + hists__inc_nr_entries(hists, n); + } } +} - self->entries = tmp; +void hists__collapse_resort(struct hists *hists) +{ + return __hists__collapse_resort(hists, false); +} + +void hists__collapse_resort_threaded(struct hists *hists) +{ + return __hists__collapse_resort(hists, true); } /* @@ -315,31 +424,43 @@ static void __hists__insert_output_entry(struct rb_root *entries, rb_insert_color(&he->rb_node, entries); } -void hists__output_resort(struct hists *self) +static void __hists__output_resort(struct hists *hists, bool threaded) { - struct rb_root tmp; + struct rb_root *root; struct rb_node *next; struct hist_entry *n; u64 min_callchain_hits; - min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100); + min_callchain_hits = hists->stats.total_period * (callchain_param.min_percent / 100); + + if (sort__need_collapse || threaded) + root = &hists->entries_collapsed; + else + root = hists->entries_in; - tmp = RB_ROOT; - next = rb_first(&self->entries); + next = rb_first(root); + hists->entries = RB_ROOT; - self->nr_entries = 0; - hists__reset_col_len(self); + hists->nr_entries = 0; + hists__reset_col_len(hists); while (next) { - n = rb_entry(next, struct hist_entry, rb_node); - next = rb_next(&n->rb_node); + n = rb_entry(next, struct hist_entry, rb_node_in); + next = rb_next(&n->rb_node_in); - rb_erase(&n->rb_node, &self->entries); - __hists__insert_output_entry(&tmp, n, min_callchain_hits); - hists__inc_nr_entries(self, n); + __hists__insert_output_entry(&hists->entries, n, min_callchain_hits); + hists__inc_nr_entries(hists, n); } +} - self->entries = tmp; +void hists__output_resort(struct hists *hists) +{ + return __hists__output_resort(hists, false); +} + +void hists__output_resort_threaded(struct hists *hists) +{ + return __hists__output_resort(hists, true); } static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) @@ -594,12 +715,27 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, return ret; } -int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, - struct hists *hists, struct hists *pair_hists, - bool show_displacement, long displacement, - bool color, u64 session_total) +void hists__output_recalc_col_len(struct hists *hists, int max_rows) +{ + struct rb_node *next = rb_first(&hists->entries); + struct hist_entry *n; + int row = 0; + + hists__reset_col_len(hists); + + while (next && row++ < max_rows) { + n = rb_entry(next, struct hist_entry, rb_node); + if (!n->filtered) + hists__calc_col_len(hists, n); + next = rb_next(&n->rb_node); + } +} + +static int hist_entry__pcnt_snprintf(struct hist_entry *self, char *s, + size_t size, struct hists *pair_hists, + bool show_displacement, long displacement, + bool color, u64 session_total) { - struct sort_entry *se; u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; u64 nr_events; const char *sep = symbol_conf.field_sep; @@ -664,6 +800,13 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); } + if (symbol_conf.show_total_period) { + if (sep) + ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period); + else + ret += snprintf(s + ret, size - ret, " %12" PRIu64, period); + } + if (pair_hists) { char bf[32]; double old_percent = 0, new_percent = 0, diff; @@ -698,26 +841,42 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, } } + return ret; +} + +int hist_entry__snprintf(struct hist_entry *he, char *s, size_t size, + struct hists *hists) +{ + const char *sep = symbol_conf.field_sep; + struct sort_entry *se; + int ret = 0; + list_for_each_entry(se, &hist_entry__sort_list, list) { if (se->elide) continue; ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); - ret += se->se_snprintf(self, s + ret, size - ret, + ret += se->se_snprintf(he, s + ret, size - ret, hists__col_len(hists, se->se_width_idx)); } return ret; } -int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, +int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists, struct hists *pair_hists, bool show_displacement, long displacement, FILE *fp, u64 session_total) { char bf[512]; - hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists, - show_displacement, displacement, - true, session_total); + int ret; + + if (size == 0 || size > sizeof(bf)) + size = sizeof(bf); + + ret = hist_entry__pcnt_snprintf(he, bf, size, pair_hists, + show_displacement, displacement, + true, session_total); + hist_entry__snprintf(he, bf + ret, size - ret, hists); return fprintf(fp, "%s\n", bf); } @@ -738,8 +897,9 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, left_margin); } -size_t hists__fprintf(struct hists *self, struct hists *pair, - bool show_displacement, FILE *fp) +size_t hists__fprintf(struct hists *hists, struct hists *pair, + bool show_displacement, bool show_header, int max_rows, + int max_cols, FILE *fp) { struct sort_entry *se; struct rb_node *nd; @@ -749,9 +909,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, unsigned int width; const char *sep = symbol_conf.field_sep; const char *col_width = symbol_conf.col_width_list_str; + int nr_rows = 0; init_rem_hits(); + if (!show_header) + goto print_entries; + fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); if (symbol_conf.show_nr_samples) { @@ -761,6 +925,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, fputs(" Samples ", fp); } + if (symbol_conf.show_total_period) { + if (sep) + ret += fprintf(fp, "%cPeriod", *sep); + else + ret += fprintf(fp, " Period "); + } + if (symbol_conf.show_cpu_utilization) { if (sep) { ret += fprintf(fp, "%csys", *sep); @@ -803,18 +974,21 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, width = strlen(se->se_header); if (symbol_conf.col_width_list_str) { if (col_width) { - hists__set_col_len(self, se->se_width_idx, + hists__set_col_len(hists, se->se_width_idx, atoi(col_width)); col_width = strchr(col_width, ','); if (col_width) ++col_width; } } - if (!hists__new_col_len(self, se->se_width_idx, width)) - width = hists__col_len(self, se->se_width_idx); + if (!hists__new_col_len(hists, se->se_width_idx, width)) + width = hists__col_len(hists, se->se_width_idx); fprintf(fp, " %*s", width, se->se_header); } + fprintf(fp, "\n"); + if (max_rows && ++nr_rows >= max_rows) + goto out; if (sep) goto print_entries; @@ -822,6 +996,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, fprintf(fp, "# ........"); if (symbol_conf.show_nr_samples) fprintf(fp, " .........."); + if (symbol_conf.show_total_period) + fprintf(fp, " ............"); if (pair) { fprintf(fp, " .........."); if (show_displacement) @@ -834,17 +1010,23 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, continue; fprintf(fp, " "); - width = hists__col_len(self, se->se_width_idx); + width = hists__col_len(hists, se->se_width_idx); if (width == 0) width = strlen(se->se_header); for (i = 0; i < width; i++) fprintf(fp, "."); } - fprintf(fp, "\n#\n"); + fprintf(fp, "\n"); + if (max_rows && ++nr_rows >= max_rows) + goto out; + + fprintf(fp, "#\n"); + if (max_rows && ++nr_rows >= max_rows) + goto out; print_entries: - for (nd = rb_first(&self->entries); 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); if (h->filtered) @@ -858,19 +1040,22 @@ print_entries: displacement = 0; ++position; } - ret += hist_entry__fprintf(h, self, pair, show_displacement, - displacement, fp, self->stats.total_period); + ret += hist_entry__fprintf(h, max_cols, hists, pair, show_displacement, + displacement, fp, hists->stats.total_period); if (symbol_conf.use_callchain) - ret += hist_entry__fprintf_callchain(h, self, fp, - self->stats.total_period); + ret += hist_entry__fprintf_callchain(h, hists, fp, + hists->stats.total_period); + if (max_rows && ++nr_rows >= max_rows) + goto out; + if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, MAP__FUNCTION, verbose, fp); fprintf(fp, "%.10s end\n", graph_dotted_line); } } - +out: free(rem_sq_bracket); return ret; @@ -879,7 +1064,7 @@ print_entries: /* * See hists__fprintf to match the column widths */ -unsigned int hists__sort_list_width(struct hists *self) +unsigned int hists__sort_list_width(struct hists *hists) { struct sort_entry *se; int ret = 9; /* total % */ @@ -896,9 +1081,12 @@ unsigned int hists__sort_list_width(struct hists *self) if (symbol_conf.show_nr_samples) ret += 11; + if (symbol_conf.show_total_period) + ret += 13; + list_for_each_entry(se, &hist_entry__sort_list, list) if (!se->elide) - ret += 2 + hists__col_len(self, se->se_width_idx); + ret += 2 + hists__col_len(hists, se->se_width_idx); if (verbose) /* Addr + origin */ ret += 3 + BITS_PER_LONG / 4; @@ -906,63 +1094,84 @@ unsigned int hists__sort_list_width(struct hists *self) return ret; } -static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, +static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h, enum hist_filter filter) { h->filtered &= ~(1 << filter); if (h->filtered) return; - ++self->nr_entries; + ++hists->nr_entries; if (h->ms.unfolded) - self->nr_entries += h->nr_rows; + hists->nr_entries += h->nr_rows; h->row_offset = 0; - self->stats.total_period += h->period; - self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; + hists->stats.total_period += h->period; + hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; - hists__calc_col_len(self, h); + hists__calc_col_len(hists, h); } -void hists__filter_by_dso(struct hists *self, const struct dso *dso) + +static bool hists__filter_entry_by_dso(struct hists *hists, + struct hist_entry *he) +{ + if (hists->dso_filter != NULL && + (he->ms.map == NULL || he->ms.map->dso != hists->dso_filter)) { + he->filtered |= (1 << HIST_FILTER__DSO); + return true; + } + + return false; +} + +void hists__filter_by_dso(struct hists *hists) { struct rb_node *nd; - self->nr_entries = self->stats.total_period = 0; - self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; - hists__reset_col_len(self); + hists->nr_entries = hists->stats.total_period = 0; + hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; + hists__reset_col_len(hists); - for (nd = rb_first(&self->entries); 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); 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); + if (hists__filter_entry_by_dso(hists, h)) continue; - } - hists__remove_entry_filter(self, h, HIST_FILTER__DSO); + hists__remove_entry_filter(hists, h, HIST_FILTER__DSO); } } -void hists__filter_by_thread(struct hists *self, const struct thread *thread) +static bool hists__filter_entry_by_thread(struct hists *hists, + struct hist_entry *he) +{ + if (hists->thread_filter != NULL && + he->thread != hists->thread_filter) { + he->filtered |= (1 << HIST_FILTER__THREAD); + return true; + } + + return false; +} + +void hists__filter_by_thread(struct hists *hists) { struct rb_node *nd; - self->nr_entries = self->stats.total_period = 0; - self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; - hists__reset_col_len(self); + hists->nr_entries = hists->stats.total_period = 0; + hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; + hists__reset_col_len(hists); - for (nd = rb_first(&self->entries); 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); - if (thread != NULL && h->thread != thread) { - h->filtered |= (1 << HIST_FILTER__THREAD); + if (hists__filter_entry_by_thread(hists, h)) continue; - } - hists__remove_entry_filter(self, h, HIST_FILTER__THREAD); + hists__remove_entry_filter(hists, h, HIST_FILTER__THREAD); } } @@ -976,13 +1185,13 @@ int hist_entry__annotate(struct hist_entry *he, size_t privsize) return symbol__annotate(he->ms.sym, he->ms.map, privsize); } -void hists__inc_nr_events(struct hists *self, u32 type) +void hists__inc_nr_events(struct hists *hists, u32 type) { - ++self->stats.nr_events[0]; - ++self->stats.nr_events[type]; + ++hists->stats.nr_events[0]; + ++hists->stats.nr_events[type]; } -size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) +size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) { int i; size_t ret = 0; @@ -990,7 +1199,7 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { const char *name; - if (self->stats.nr_events[i] == 0) + if (hists->stats.nr_events[i] == 0) continue; name = perf_event__name(i); @@ -998,8 +1207,18 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) continue; ret += fprintf(fp, "%16s events: %10d\n", name, - self->stats.nr_events[i]); + hists->stats.nr_events[i]); } return ret; } + +void hists__init(struct hists *hists) +{ + memset(hists, 0, sizeof(*hists)); + hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; + hists->entries_in = &hists->entries_in_array[0]; + hists->entries_collapsed = RB_ROOT; + hists->entries = RB_ROOT; + pthread_mutex_init(&hists->lock, NULL); +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 3beb97c4d82..ff93ddc91c5 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -2,6 +2,7 @@ #define __PERF_HIST_H #include <linux/types.h> +#include <pthread.h> #include "callchain.h" extern struct callchain_param callchain_param; @@ -42,9 +43,18 @@ enum hist_column { HISTC_NR_COLS, /* Last entry */ }; +struct thread; +struct dso; + struct hists { + struct rb_root entries_in_array[2]; + struct rb_root *entries_in; struct rb_root entries; + struct rb_root entries_collapsed; u64 nr_entries; + const struct thread *thread_filter; + const struct dso *dso_filter; + pthread_mutex_t lock; struct events_stats stats; u64 event_stream; u16 col_len[HISTC_NR_COLS]; @@ -52,34 +62,42 @@ struct hists { struct callchain_cursor callchain_cursor; }; +void hists__init(struct hists *hists); + struct hist_entry *__hists__add_entry(struct hists *self, struct addr_location *al, struct symbol *parent, u64 period); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); -int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, +int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists, struct hists *pair_hists, bool show_displacement, - long displacement, FILE *fp, u64 total); + long displacement, FILE *fp, u64 session_total); int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, - struct hists *hists, struct hists *pair_hists, - bool show_displacement, long displacement, - bool color, u64 total); + struct hists *hists); void hist_entry__free(struct hist_entry *); void hists__output_resort(struct hists *self); +void hists__output_resort_threaded(struct hists *hists); void hists__collapse_resort(struct hists *self); +void hists__collapse_resort_threaded(struct hists *hists); + +void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); +void hists__decay_entries_threaded(struct hists *hists, bool zap_user, + bool zap_kernel); +void hists__output_recalc_col_len(struct hists *hists, int max_rows); void hists__inc_nr_events(struct hists *self, u32 type); size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); size_t hists__fprintf(struct hists *self, struct hists *pair, - bool show_displacement, FILE *fp); + bool show_displacement, bool show_header, + int max_rows, int max_cols, FILE *fp); int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); int hist_entry__annotate(struct hist_entry *self, size_t privsize); -void hists__filter_by_dso(struct hists *self, const struct dso *dso); -void hists__filter_by_thread(struct hists *self, const struct thread *thread); +void hists__filter_by_dso(struct hists *hists); +void hists__filter_by_thread(struct hists *hists); u16 hists__col_len(struct hists *self, enum hist_column col); void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); @@ -90,26 +108,33 @@ struct perf_evlist; #ifdef NO_NEWT_SUPPORT static inline int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used, - const char *help __used) + const char *help __used, + void(*timer)(void *arg) __used, + void *arg __used, + int refresh __used) { return 0; } static inline int hist_entry__tui_annotate(struct hist_entry *self __used, - int evidx __used) + int evidx __used, + int nr_events __used, + void(*timer)(void *arg) __used, + void *arg __used, + int delay_secs __used) { return 0; } -#define KEY_LEFT -1 -#define KEY_RIGHT -2 +#define K_LEFT -1 +#define K_RIGHT -2 #else -#include <newt.h> -int hist_entry__tui_annotate(struct hist_entry *self, int evidx); - -#define KEY_LEFT NEWT_KEY_LEFT -#define KEY_RIGHT NEWT_KEY_RIGHT +#include "ui/keysyms.h" +int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events, + void(*timer)(void *arg), void *arg, int delay_secs); -int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help); +int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, + void(*timer)(void *arg), void *arg, + int refresh); #endif unsigned int hists__sort_list_width(struct hists *self); diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index a16ecab5229..78284b13e80 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -18,6 +18,13 @@ static inline int is_anon_memory(const char *filename) return strcmp(filename, "//anon") == 0; } +static inline int is_no_dso_memory(const char *filename) +{ + return !strcmp(filename, "[stack]") || + !strcmp(filename, "[vdso]") || + !strcmp(filename, "[heap]"); +} + void map__init(struct map *self, enum map_type type, u64 start, u64 end, u64 pgoff, struct dso *dso) { @@ -42,9 +49,10 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, if (self != NULL) { char newfilename[PATH_MAX]; struct dso *dso; - int anon; + int anon, no_dso; anon = is_anon_memory(filename); + no_dso = is_no_dso_memory(filename); if (anon) { snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); @@ -57,12 +65,16 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, map__init(self, type, start, start + len, pgoff, dso); - if (anon) { -set_identity: + if (anon || no_dso) { self->map_ip = self->unmap_ip = identity__map_ip; - } else if (strcmp(filename, "[vdso]") == 0) { - dso__set_loaded(dso, self->type); - goto set_identity; + + /* + * Set memory without DSO as loaded. All map__find_* + * functions still return NULL, and we avoid the + * unnecessary map__load warning. + */ + if (no_dso) + dso__set_loaded(dso, self->type); } } return self; @@ -127,8 +139,8 @@ int map__load(struct map *self, symbol_filter_t filter) if (len > sizeof(DSO__DELETED) && strcmp(name + real_len + 1, DSO__DELETED) == 0) { - pr_warning("%.*s was updated, restart the long " - "running apps that use it!\n", + pr_warning("%.*s was updated (is prelink enabled?). " + "Restart the long running apps that use it!\n", (int)real_len, name); } else { pr_warning("no symbols found in %s, maybe install " @@ -220,55 +232,55 @@ u64 map__objdump_2ip(struct map *map, u64 addr) return ip; } -void map_groups__init(struct map_groups *self) +void map_groups__init(struct map_groups *mg) { int i; for (i = 0; i < MAP__NR_TYPES; ++i) { - self->maps[i] = RB_ROOT; - INIT_LIST_HEAD(&self->removed_maps[i]); + mg->maps[i] = RB_ROOT; + INIT_LIST_HEAD(&mg->removed_maps[i]); } - self->machine = NULL; + mg->machine = NULL; } -static void maps__delete(struct rb_root *self) +static void maps__delete(struct rb_root *maps) { - struct rb_node *next = rb_first(self); + struct rb_node *next = rb_first(maps); while (next) { struct map *pos = rb_entry(next, struct map, rb_node); next = rb_next(&pos->rb_node); - rb_erase(&pos->rb_node, self); + rb_erase(&pos->rb_node, maps); map__delete(pos); } } -static void maps__delete_removed(struct list_head *self) +static void maps__delete_removed(struct list_head *maps) { struct map *pos, *n; - list_for_each_entry_safe(pos, n, self, node) { + list_for_each_entry_safe(pos, n, maps, node) { list_del(&pos->node); map__delete(pos); } } -void map_groups__exit(struct map_groups *self) +void map_groups__exit(struct map_groups *mg) { int i; for (i = 0; i < MAP__NR_TYPES; ++i) { - maps__delete(&self->maps[i]); - maps__delete_removed(&self->removed_maps[i]); + maps__delete(&mg->maps[i]); + maps__delete_removed(&mg->removed_maps[i]); } } -void map_groups__flush(struct map_groups *self) +void map_groups__flush(struct map_groups *mg) { int type; for (type = 0; type < MAP__NR_TYPES; type++) { - struct rb_root *root = &self->maps[type]; + struct rb_root *root = &mg->maps[type]; struct rb_node *next = rb_first(root); while (next) { @@ -280,17 +292,17 @@ void map_groups__flush(struct map_groups *self) * instance in some hist_entry instances, so * just move them to a separate list. */ - list_add_tail(&pos->node, &self->removed_maps[pos->type]); + list_add_tail(&pos->node, &mg->removed_maps[pos->type]); } } } -struct symbol *map_groups__find_symbol(struct map_groups *self, +struct symbol *map_groups__find_symbol(struct map_groups *mg, enum map_type type, u64 addr, struct map **mapp, symbol_filter_t filter) { - struct map *map = map_groups__find(self, type, addr); + struct map *map = map_groups__find(mg, type, addr); if (map != NULL) { if (mapp != NULL) @@ -301,7 +313,7 @@ struct symbol *map_groups__find_symbol(struct map_groups *self, return NULL; } -struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, +struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, enum map_type type, const char *name, struct map **mapp, @@ -309,7 +321,7 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, { struct rb_node *nd; - for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { + for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { struct map *pos = rb_entry(nd, struct map, rb_node); struct symbol *sym = map__find_symbol_by_name(pos, name, filter); @@ -323,13 +335,13 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, return NULL; } -size_t __map_groups__fprintf_maps(struct map_groups *self, +size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type, int verbose, FILE *fp) { size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); struct rb_node *nd; - for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { + for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { struct map *pos = rb_entry(nd, struct map, rb_node); printed += fprintf(fp, "Map:"); printed += map__fprintf(pos, fp); @@ -342,22 +354,22 @@ size_t __map_groups__fprintf_maps(struct map_groups *self, return printed; } -size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp) +size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp) { size_t printed = 0, i; for (i = 0; i < MAP__NR_TYPES; ++i) - printed += __map_groups__fprintf_maps(self, i, verbose, fp); + printed += __map_groups__fprintf_maps(mg, i, verbose, fp); return printed; } -static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, +static size_t __map_groups__fprintf_removed_maps(struct map_groups *mg, enum map_type type, int verbose, FILE *fp) { struct map *pos; size_t printed = 0; - list_for_each_entry(pos, &self->removed_maps[type], node) { + list_for_each_entry(pos, &mg->removed_maps[type], node) { printed += fprintf(fp, "Map:"); printed += map__fprintf(pos, fp); if (verbose > 1) { @@ -368,26 +380,26 @@ static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, return printed; } -static size_t map_groups__fprintf_removed_maps(struct map_groups *self, +static size_t map_groups__fprintf_removed_maps(struct map_groups *mg, int verbose, FILE *fp) { size_t printed = 0, i; for (i = 0; i < MAP__NR_TYPES; ++i) - printed += __map_groups__fprintf_removed_maps(self, i, verbose, fp); + printed += __map_groups__fprintf_removed_maps(mg, i, verbose, fp); return printed; } -size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp) +size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp) { - size_t printed = map_groups__fprintf_maps(self, verbose, fp); + size_t printed = map_groups__fprintf_maps(mg, verbose, fp); printed += fprintf(fp, "Removed maps:\n"); - return printed + map_groups__fprintf_removed_maps(self, verbose, fp); + return printed + map_groups__fprintf_removed_maps(mg, verbose, fp); } -int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, +int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, int verbose, FILE *fp) { - struct rb_root *root = &self->maps[map->type]; + struct rb_root *root = &mg->maps[map->type]; struct rb_node *next = rb_first(root); int err = 0; @@ -418,7 +430,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, } before->end = map->start - 1; - map_groups__insert(self, before); + map_groups__insert(mg, before); if (verbose >= 2) map__fprintf(before, fp); } @@ -432,7 +444,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, } after->start = map->end + 1; - map_groups__insert(self, after); + map_groups__insert(mg, after); if (verbose >= 2) map__fprintf(after, fp); } @@ -441,7 +453,7 @@ move_map: * If we have references, just move them to a separate list. */ if (pos->referenced) - list_add_tail(&pos->node, &self->removed_maps[map->type]); + list_add_tail(&pos->node, &mg->removed_maps[map->type]); else map__delete(pos); @@ -455,7 +467,7 @@ move_map: /* * XXX This should not really _copy_ te maps, but refcount them. */ -int map_groups__clone(struct map_groups *self, +int map_groups__clone(struct map_groups *mg, struct map_groups *parent, enum map_type type) { struct rb_node *nd; @@ -464,7 +476,7 @@ int map_groups__clone(struct map_groups *self, struct map *new = map__clone(map); if (new == NULL) return -ENOMEM; - map_groups__insert(self, new); + map_groups__insert(mg, new); } return 0; } diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index b397c038372..890d85545d0 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -123,17 +123,17 @@ void map__fixup_end(struct map *self); void map__reloc_vmlinux(struct map *self); -size_t __map_groups__fprintf_maps(struct map_groups *self, +size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type, int verbose, FILE *fp); void maps__insert(struct rb_root *maps, struct map *map); -void maps__remove(struct rb_root *self, struct map *map); +void maps__remove(struct rb_root *maps, struct map *map); struct map *maps__find(struct rb_root *maps, u64 addr); -void map_groups__init(struct map_groups *self); -void map_groups__exit(struct map_groups *self); -int map_groups__clone(struct map_groups *self, +void map_groups__init(struct map_groups *mg); +void map_groups__exit(struct map_groups *mg); +int map_groups__clone(struct map_groups *mg, struct map_groups *parent, enum map_type type); -size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); -size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp); +size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp); +size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp); typedef void (*machine__process_t)(struct machine *self, void *data); @@ -162,29 +162,29 @@ static inline bool machine__is_host(struct machine *self) return self ? self->pid == HOST_KERNEL_ID : false; } -static inline void map_groups__insert(struct map_groups *self, struct map *map) +static inline void map_groups__insert(struct map_groups *mg, struct map *map) { - maps__insert(&self->maps[map->type], map); - map->groups = self; + maps__insert(&mg->maps[map->type], map); + map->groups = mg; } -static inline void map_groups__remove(struct map_groups *self, struct map *map) +static inline void map_groups__remove(struct map_groups *mg, struct map *map) { - maps__remove(&self->maps[map->type], map); + maps__remove(&mg->maps[map->type], map); } -static inline struct map *map_groups__find(struct map_groups *self, +static inline struct map *map_groups__find(struct map_groups *mg, enum map_type type, u64 addr) { - return maps__find(&self->maps[type], addr); + return maps__find(&mg->maps[type], addr); } -struct symbol *map_groups__find_symbol(struct map_groups *self, +struct symbol *map_groups__find_symbol(struct map_groups *mg, enum map_type type, u64 addr, struct map **mapp, symbol_filter_t filter); -struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, +struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, enum map_type type, const char *name, struct map **mapp, @@ -208,11 +208,11 @@ struct symbol *machine__find_kernel_function(struct machine *self, u64 addr, } static inline -struct symbol *map_groups__find_function_by_name(struct map_groups *self, +struct symbol *map_groups__find_function_by_name(struct map_groups *mg, const char *name, struct map **mapp, symbol_filter_t filter) { - return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter); + return map_groups__find_symbol_by_name(mg, MAP__FUNCTION, name, mapp, filter); } static inline @@ -225,13 +225,13 @@ struct symbol *machine__find_kernel_function_by_name(struct machine *self, filter); } -int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, +int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, int verbose, FILE *fp); -struct map *map_groups__find_by_name(struct map_groups *self, +struct map *map_groups__find_by_name(struct map_groups *mg, enum map_type type, const char *name); struct map *machine__new_module(struct machine *self, u64 start, const char *filename); -void map_groups__flush(struct map_groups *self); +void map_groups__flush(struct map_groups *mg); #endif /* __PERF_MAP_H */ diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 1c7bfa5fe0a..eb25900e221 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -1956,8 +1956,10 @@ static int __del_trace_probe_event(int fd, struct str_node *ent) pr_debug("Writing event: %s\n", buf); ret = write(fd, buf, strlen(buf)); - if (ret < 0) + if (ret < 0) { + ret = -errno; goto error; + } printf("Remove event: %s\n", ent->s); return 0; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 72458d9da5b..20e011c99a9 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1326,3 +1326,22 @@ int perf_session__cpu_bitmap(struct perf_session *session, return 0; } + +void perf_session__fprintf_info(struct perf_session *session, FILE *fp, + bool full) +{ + struct stat st; + int ret; + + if (session == NULL || fp == NULL) + return; + + ret = fstat(session->fd, &st); + if (ret == -1) + return; + + fprintf(fp, "# ========\n"); + fprintf(fp, "# captured on: %s", ctime(&st.st_ctime)); + perf_header__fprintf_info(session, fp, full); + fprintf(fp, "# ========\n#\n"); +} diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 974d0cbee5e..514b06d41f0 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -177,4 +177,5 @@ void perf_session__print_ip(union perf_event *event, int perf_session__cpu_bitmap(struct perf_session *session, const char *cpu_list, unsigned long *cpu_bitmap); +void perf_session__fprintf_info(struct perf_session *s, FILE *fp, bool full); #endif /* __PERF_SESSION_H */ diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 1ee8f1e40f1..16da30d8d76 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -177,7 +177,9 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, BITS_PER_LONG / 4, self->ip, o); } - ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); + if (!sort_dso.elide) + ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); + if (self->ms.sym) ret += repsep_snprintf(bf + ret, size - ret, "%s", self->ms.sym->name); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 77d0388ad41..3f67ae39575 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -45,6 +45,7 @@ extern enum sort_type sort__first_dimension; * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding */ struct hist_entry { + struct rb_node rb_node_in; struct rb_node rb_node; u64 period; u64 period_sys; @@ -63,6 +64,7 @@ struct hist_entry { bool init_have_children; char level; + bool used; u8 filtered; struct symbol *parent; union { diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 40eeaf07725..632b50c7bc2 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -24,7 +24,7 @@ #include <sys/utsname.h> #ifndef KSYM_NAME_LEN -#define KSYM_NAME_LEN 128 +#define KSYM_NAME_LEN 256 #endif #ifndef NT_GNU_BUILD_ID @@ -46,6 +46,7 @@ struct symbol_conf symbol_conf = { .exclude_other = true, .use_modules = true, .try_vmlinux_path = true, + .annotate_src = true, .symfs = "", }; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 4f377d92e75..29f8d742e92 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -72,11 +72,14 @@ struct symbol_conf { use_modules, sort_by_name, show_nr_samples, + show_total_period, use_callchain, exclude_other, show_cpu_utilization, initialized, - kptr_restrict; + kptr_restrict, + annotate_asm_raw, + annotate_src; const char *vmlinux_name, *kallsyms_name, *source_prefix, diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index a11f60735a1..500471dffa4 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c @@ -15,52 +15,6 @@ #include "top.h" #include <inttypes.h> -/* - * Ordering weight: count-1 * count-2 * ... / count-n - */ -static double sym_weight(const struct sym_entry *sym, struct perf_top *top) -{ - double weight = sym->snap_count; - int counter; - - if (!top->display_weighted) - return weight; - - for (counter = 1; counter < top->evlist->nr_entries - 1; counter++) - weight *= sym->count[counter]; - - weight /= (sym->count[counter] + 1); - - return weight; -} - -static void perf_top__remove_active_sym(struct perf_top *top, struct sym_entry *syme) -{ - pthread_mutex_lock(&top->active_symbols_lock); - list_del_init(&syme->node); - pthread_mutex_unlock(&top->active_symbols_lock); -} - -static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) -{ - struct rb_node **p = &tree->rb_node; - struct rb_node *parent = NULL; - struct sym_entry *iter; - - while (*p != NULL) { - parent = *p; - iter = rb_entry(parent, struct sym_entry, rb_node); - - if (se->weight > iter->weight) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; - } - - rb_link_node(&se->rb_node, parent, p); - rb_insert_color(&se->rb_node, tree); -} - #define SNPRINTF(buf, size, fmt, args...) \ ({ \ size_t r = snprintf(buf, size, fmt, ## args); \ @@ -69,7 +23,6 @@ static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) { - struct perf_evsel *counter; float samples_per_sec = top->samples / top->delay_secs; float ksamples_per_sec = top->kernel_samples / top->delay_secs; float esamples_percent = (100.0 * top->exact_samples) / top->samples; @@ -104,7 +57,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) esamples_percent); } - if (top->evlist->nr_entries == 1 || !top->display_weighted) { + if (top->evlist->nr_entries == 1) { struct perf_evsel *first; first = list_entry(top->evlist->entries.next, struct perf_evsel, node); ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", @@ -112,27 +65,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) top->freq ? "Hz" : ""); } - if (!top->display_weighted) { - ret += SNPRINTF(bf + ret, size - ret, "%s", - event_name(top->sym_evsel)); - } else { - /* - * Don't let events eat all the space. Leaving 30 bytes - * for the rest should be enough. - */ - size_t last_pos = size - 30; - - list_for_each_entry(counter, &top->evlist->entries, node) { - ret += SNPRINTF(bf + ret, size - ret, "%s%s", - counter->idx ? "/" : "", - event_name(counter)); - if (ret > last_pos) { - sprintf(bf + last_pos - 3, ".."); - ret = last_pos - 1; - break; - } - } - } + ret += SNPRINTF(bf + ret, size - ret, "%s", event_name(top->sym_evsel)); ret += SNPRINTF(bf + ret, size - ret, "], "); @@ -166,73 +99,3 @@ void perf_top__reset_sample_counters(struct perf_top *top) top->exact_samples = top->guest_kernel_samples = top->guest_us_samples = 0; } - -float perf_top__decay_samples(struct perf_top *top, struct rb_root *root) -{ - struct sym_entry *syme, *n; - float sum_ksamples = 0.0; - int snap = !top->display_weighted ? top->sym_evsel->idx : 0, j; - - /* Sort the active symbols */ - pthread_mutex_lock(&top->active_symbols_lock); - syme = list_entry(top->active_symbols.next, struct sym_entry, node); - pthread_mutex_unlock(&top->active_symbols_lock); - - top->rb_entries = 0; - list_for_each_entry_safe_from(syme, n, &top->active_symbols, node) { - syme->snap_count = syme->count[snap]; - if (syme->snap_count != 0) { - - if ((top->hide_user_symbols && - syme->map->dso->kernel == DSO_TYPE_USER) || - (top->hide_kernel_symbols && - syme->map->dso->kernel == DSO_TYPE_KERNEL)) { - perf_top__remove_active_sym(top, syme); - continue; - } - syme->weight = sym_weight(syme, top); - - if ((int)syme->snap_count >= top->count_filter) { - rb_insert_active_sym(root, syme); - ++top->rb_entries; - } - sum_ksamples += syme->snap_count; - - for (j = 0; j < top->evlist->nr_entries; j++) - syme->count[j] = top->zero ? 0 : syme->count[j] * 7 / 8; - } else - perf_top__remove_active_sym(top, syme); - } - - return sum_ksamples; -} - -/* - * Find the longest symbol name that will be displayed - */ -void perf_top__find_widths(struct perf_top *top, struct rb_root *root, - int *dso_width, int *dso_short_width, int *sym_width) -{ - struct rb_node *nd; - int printed = 0; - - *sym_width = *dso_width = *dso_short_width = 0; - - for (nd = rb_first(root); nd; nd = rb_next(nd)) { - struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node); - struct symbol *sym = sym_entry__symbol(syme); - - if (++printed > top->print_entries || - (int)syme->snap_count < top->count_filter) - continue; - - if (syme->map->dso->long_name_len > *dso_width) - *dso_width = syme->map->dso->long_name_len; - - if (syme->map->dso->short_name_len > *dso_short_width) - *dso_short_width = syme->map->dso->short_name_len; - - if (sym->namelen > *sym_width) - *sym_width = sym->namelen; - } -} diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index bfbf95bcc60..01d1057f307 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -4,26 +4,10 @@ #include "types.h" #include "../perf.h" #include <stddef.h> -#include <pthread.h> -#include <linux/list.h> -#include <linux/rbtree.h> struct perf_evlist; struct perf_evsel; - -struct sym_entry { - struct rb_node rb_node; - struct list_head node; - unsigned long snap_count; - double weight; - struct map *map; - unsigned long count[0]; -}; - -static inline struct symbol *sym_entry__symbol(struct sym_entry *self) -{ - return ((void *)self) + symbol_conf.priv_size; -} +struct perf_session; struct perf_top { struct perf_evlist *evlist; @@ -31,34 +15,21 @@ struct perf_top { * Symbols will be added here in perf_event__process_sample and will * get out after decayed. */ - struct list_head active_symbols; - pthread_mutex_t active_symbols_lock; - pthread_cond_t active_symbols_cond; u64 samples; u64 kernel_samples, us_samples; u64 exact_samples; u64 guest_us_samples, guest_kernel_samples; + u64 total_lost_warned; int print_entries, count_filter, delay_secs; - int display_weighted, freq, rb_entries; + int freq; pid_t target_pid, target_tid; bool hide_kernel_symbols, hide_user_symbols, zero; const char *cpu_list; - struct sym_entry *sym_filter_entry; + struct hist_entry *sym_filter_entry; struct perf_evsel *sym_evsel; + struct perf_session *session; }; size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); void perf_top__reset_sample_counters(struct perf_top *top); -float perf_top__decay_samples(struct perf_top *top, struct rb_root *root); -void perf_top__find_widths(struct perf_top *top, struct rb_root *root, - int *dso_width, int *dso_short_width, int *sym_width); - -#ifdef NO_NEWT_SUPPORT -static inline int perf_top__tui_browser(struct perf_top *top __used) -{ - return 0; -} -#else -int perf_top__tui_browser(struct perf_top *top); -#endif #endif /* __PERF_TOP_H */ diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 3403f814ad7..2d530cf74f4 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -196,7 +196,8 @@ static void record_file(const char *file, size_t hdr_sz) die("Can't read '%s'", file); /* put in zeros for file size, then fill true size later */ - write_or_die(&size, hdr_sz); + if (hdr_sz) + write_or_die(&size, hdr_sz); do { r = read(fd, buf, BUFSIZ); @@ -212,7 +213,7 @@ static void record_file(const char *file, size_t hdr_sz) if (bigendian()) sizep += sizeof(u64) - hdr_sz; - if (pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) + if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) die("writing to %s", output_file); } @@ -428,6 +429,19 @@ get_tracepoints_path(struct list_head *pattrs) return nr_tracepoints > 0 ? path.next : NULL; } +static void +put_tracepoints_path(struct tracepoint_path *tps) +{ + while (tps) { + struct tracepoint_path *t = tps; + + tps = tps->next; + free(t->name); + free(t->system); + free(t); + } +} + bool have_tracepoints(struct list_head *pattrs) { struct perf_evsel *pos; @@ -439,19 +453,11 @@ bool have_tracepoints(struct list_head *pattrs) return false; } -int read_tracing_data(int fd, struct list_head *pattrs) +static void tracing_data_header(void) { - char buf[BUFSIZ]; - struct tracepoint_path *tps = get_tracepoints_path(pattrs); - - /* - * What? No tracepoints? No sense writing anything here, bail out. - */ - if (tps == NULL) - return -1; - - output_fd = fd; + char buf[20]; + /* just guessing this is someone's birthday.. ;) */ buf[0] = 23; buf[1] = 8; buf[2] = 68; @@ -476,28 +482,86 @@ int read_tracing_data(int fd, struct list_head *pattrs) /* save page_size */ page_size = sysconf(_SC_PAGESIZE); write_or_die(&page_size, 4); +} + +struct tracing_data *tracing_data_get(struct list_head *pattrs, + int fd, bool temp) +{ + struct tracepoint_path *tps; + struct tracing_data *tdata; + + output_fd = fd; + + tps = get_tracepoints_path(pattrs); + if (!tps) + return NULL; + tdata = malloc_or_die(sizeof(*tdata)); + tdata->temp = temp; + tdata->size = 0; + + if (temp) { + int temp_fd; + + snprintf(tdata->temp_file, sizeof(tdata->temp_file), + "/tmp/perf-XXXXXX"); + if (!mkstemp(tdata->temp_file)) + die("Can't make temp file"); + + temp_fd = open(tdata->temp_file, O_RDWR); + if (temp_fd < 0) + die("Can't read '%s'", tdata->temp_file); + + /* + * Set the temp file the default output, so all the + * tracing data are stored into it. + */ + output_fd = temp_fd; + } + + tracing_data_header(); read_header_files(); read_ftrace_files(tps); read_event_files(tps); read_proc_kallsyms(); read_ftrace_printk(); - return 0; + /* + * All tracing data are stored by now, we can restore + * the default output file in case we used temp file. + */ + if (temp) { + tdata->size = lseek(output_fd, 0, SEEK_CUR); + close(output_fd); + output_fd = fd; + } + + put_tracepoints_path(tps); + return tdata; } -ssize_t read_tracing_data_size(int fd, struct list_head *pattrs) +void tracing_data_put(struct tracing_data *tdata) { - ssize_t size; - int err = 0; + if (tdata->temp) { + record_file(tdata->temp_file, 0); + unlink(tdata->temp_file); + } - calc_data_size = 1; - err = read_tracing_data(fd, pattrs); - size = calc_data_size - 1; - calc_data_size = 0; + free(tdata); +} - if (err < 0) - return err; +int read_tracing_data(int fd, struct list_head *pattrs) +{ + struct tracing_data *tdata; - return size; + /* + * We work over the real file, so we can write data + * directly, no temp file is needed. + */ + tdata = tracing_data_get(pattrs, fd, false); + if (!tdata) + return -ENOMEM; + + tracing_data_put(tdata); + return 0; } diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index f674dda3363..a8410081764 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -263,7 +263,18 @@ void *raw_field_ptr(struct event *event, const char *name, void *data); unsigned long long eval_flag(const char *flag); int read_tracing_data(int fd, struct list_head *pattrs); -ssize_t read_tracing_data_size(int fd, struct list_head *pattrs); + +struct tracing_data { + /* size is only valid if temp is 'true' */ + ssize_t size; + bool temp; + char temp_file[50]; +}; + +struct tracing_data *tracing_data_get(struct list_head *pattrs, + int fd, bool temp); +void tracing_data_put(struct tracing_data *tdata); + /* taken from kernel/trace/trace.h */ enum trace_flag_type { diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index 611219f8068..5359f371d30 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c @@ -1,4 +1,8 @@ +#include "../util.h" +#include "../cache.h" +#include "../../perf.h" #include "libslang.h" +#include <newt.h> #include "ui.h" #include <linux/compiler.h> #include <linux/list.h> @@ -7,13 +11,13 @@ #include <sys/ttydefaults.h> #include "browser.h" #include "helpline.h" +#include "keysyms.h" #include "../color.h" -#include "../util.h" -#include <stdio.h> -static int ui_browser__percent_color(double percent, bool current) +static int ui_browser__percent_color(struct ui_browser *browser, + double percent, bool current) { - if (current) + if (current && (!browser->use_navkeypressed || browser->navkeypressed)) return HE_COLORSET_SELECTED; if (percent >= MIN_RED) return HE_COLORSET_TOP; @@ -30,7 +34,7 @@ void ui_browser__set_color(struct ui_browser *self __used, int color) void ui_browser__set_percent_color(struct ui_browser *self, double percent, bool current) { - int color = ui_browser__percent_color(percent, current); + int color = ui_browser__percent_color(self, percent, current); ui_browser__set_color(self, color); } @@ -39,31 +43,62 @@ void ui_browser__gotorc(struct ui_browser *self, int y, int x) SLsmg_gotorc(self->y + y, self->x + x); } +static struct list_head * +ui_browser__list_head_filter_entries(struct ui_browser *browser, + struct list_head *pos) +{ + do { + if (!browser->filter || !browser->filter(browser, pos)) + return pos; + pos = pos->next; + } while (pos != browser->entries); + + return NULL; +} + +static struct list_head * +ui_browser__list_head_filter_prev_entries(struct ui_browser *browser, + struct list_head *pos) +{ + do { + if (!browser->filter || !browser->filter(browser, pos)) + return pos; + pos = pos->prev; + } while (pos != browser->entries); + + return NULL; +} + void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence) { struct list_head *head = self->entries; struct list_head *pos; + if (self->nr_entries == 0) + return; + switch (whence) { case SEEK_SET: - pos = head->next; + pos = ui_browser__list_head_filter_entries(self, head->next); break; case SEEK_CUR: pos = self->top; break; case SEEK_END: - pos = head->prev; + pos = ui_browser__list_head_filter_prev_entries(self, head->prev); break; default: return; } + assert(pos != NULL); + if (offset > 0) { while (offset-- != 0) - pos = pos->next; + pos = ui_browser__list_head_filter_entries(self, pos->next); } else { while (offset++ != 0) - pos = pos->prev; + pos = ui_browser__list_head_filter_prev_entries(self, pos->prev); } self->top = pos; @@ -127,11 +162,8 @@ bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) void ui_browser__refresh_dimensions(struct ui_browser *self) { - int cols, rows; - newtGetScreenSize(&cols, &rows); - - self->width = cols - 1; - self->height = rows - 2; + self->width = SLtt_Screen_Cols - 1; + self->height = SLtt_Screen_Rows - 2; self->y = 1; self->x = 0; } @@ -142,26 +174,11 @@ void ui_browser__reset_index(struct ui_browser *self) self->seek(self, 0, SEEK_SET); } -void ui_browser__add_exit_key(struct ui_browser *self, int key) -{ - newtFormAddHotKey(self->form, key); -} - -void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]) -{ - int i = 0; - - while (keys[i] && i < 64) { - ui_browser__add_exit_key(self, keys[i]); - ++i; - } -} - void __ui_browser__show_title(struct ui_browser *browser, const char *title) { SLsmg_gotorc(0, 0); ui_browser__set_color(browser, NEWT_COLORSET_ROOT); - slsmg_write_nstring(title, browser->width); + slsmg_write_nstring(title, browser->width + 1); } void ui_browser__show_title(struct ui_browser *browser, const char *title) @@ -174,78 +191,189 @@ void ui_browser__show_title(struct ui_browser *browser, const char *title) int ui_browser__show(struct ui_browser *self, const char *title, const char *helpline, ...) { + int err; va_list ap; - int keys[] = { NEWT_KEY_UP, NEWT_KEY_DOWN, NEWT_KEY_PGUP, - NEWT_KEY_PGDN, NEWT_KEY_HOME, NEWT_KEY_END, ' ', - NEWT_KEY_LEFT, NEWT_KEY_ESCAPE, 'q', CTRL('c'), 0 }; - - if (self->form != NULL) - newtFormDestroy(self->form); ui_browser__refresh_dimensions(self); - self->form = newtForm(NULL, NULL, 0); - if (self->form == NULL) - return -1; - - self->sb = newtVerticalScrollbar(self->width, 1, self->height, - HE_COLORSET_NORMAL, - HE_COLORSET_SELECTED); - if (self->sb == NULL) - return -1; pthread_mutex_lock(&ui__lock); __ui_browser__show_title(self, title); - ui_browser__add_exit_keys(self, keys); - newtFormAddComponent(self->form, self->sb); + self->title = title; + free(self->helpline); + self->helpline = NULL; va_start(ap, helpline); - ui_helpline__vpush(helpline, ap); + err = vasprintf(&self->helpline, helpline, ap); va_end(ap); + if (err > 0) + ui_helpline__push(self->helpline); pthread_mutex_unlock(&ui__lock); - return 0; + return err ? 0 : -1; } -void ui_browser__hide(struct ui_browser *self) +void ui_browser__hide(struct ui_browser *browser __used) { pthread_mutex_lock(&ui__lock); - newtFormDestroy(self->form); - self->form = NULL; ui_helpline__pop(); pthread_mutex_unlock(&ui__lock); } -int ui_browser__refresh(struct ui_browser *self) +static void ui_browser__scrollbar_set(struct ui_browser *browser) +{ + int height = browser->height, h = 0, pct = 0, + col = browser->width, + row = browser->y - 1; + + if (browser->nr_entries > 1) { + pct = ((browser->index * (browser->height - 1)) / + (browser->nr_entries - 1)); + } + + while (h < height) { + ui_browser__gotorc(browser, row++, col); + SLsmg_set_char_set(1); + SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_BOARD_CHAR); + SLsmg_set_char_set(0); + ++h; + } +} + +static int __ui_browser__refresh(struct ui_browser *browser) { int row; + int width = browser->width; + + row = browser->refresh(browser); + ui_browser__set_color(browser, HE_COLORSET_NORMAL); + + if (!browser->use_navkeypressed || browser->navkeypressed) + ui_browser__scrollbar_set(browser); + else + width += 1; + SLsmg_fill_region(browser->y + row, browser->x, + browser->height - row, width, ' '); + + return 0; +} + +int ui_browser__refresh(struct ui_browser *browser) +{ pthread_mutex_lock(&ui__lock); - newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); - row = self->refresh(self); - ui_browser__set_color(self, HE_COLORSET_NORMAL); - SLsmg_fill_region(self->y + row, self->x, - self->height - row, self->width, ' '); + __ui_browser__refresh(browser); pthread_mutex_unlock(&ui__lock); return 0; } -int ui_browser__run(struct ui_browser *self) +/* + * Here we're updating nr_entries _after_ we started browsing, i.e. we have to + * forget about any reference to any entry in the underlying data structure, + * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser + * after an output_resort and hist decay. + */ +void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries) { - struct newtExitStruct es; + off_t offset = nr_entries - browser->nr_entries; + + browser->nr_entries = nr_entries; - if (ui_browser__refresh(self) < 0) - return -1; + if (offset < 0) { + if (browser->top_idx < (u64)-offset) + offset = -browser->top_idx; + + browser->index += offset; + browser->top_idx += offset; + } + + browser->top = NULL; + browser->seek(browser, browser->top_idx, SEEK_SET); +} + +static int ui__getch(int delay_secs) +{ + struct timeval timeout, *ptimeout = delay_secs ? &timeout : NULL; + fd_set read_set; + int err, key; + + FD_ZERO(&read_set); + FD_SET(0, &read_set); + + if (delay_secs) { + timeout.tv_sec = delay_secs; + timeout.tv_usec = 0; + } + + err = select(1, &read_set, NULL, NULL, ptimeout); + + if (err == 0) + return K_TIMER; + + if (err == -1) { + if (errno == EINTR) + return K_RESIZE; + return K_ERROR; + } + + key = SLang_getkey(); + if (key != K_ESC) + return key; + + FD_ZERO(&read_set); + FD_SET(0, &read_set); + timeout.tv_sec = 0; + timeout.tv_usec = 20; + err = select(1, &read_set, NULL, NULL, &timeout); + if (err == 0) + return K_ESC; + + SLang_ungetkey(key); + return SLkp_getkey(); +} + +int ui_browser__run(struct ui_browser *self, int delay_secs) +{ + int err, key; + + pthread__unblock_sigwinch(); while (1) { off_t offset; - newtFormRun(self->form, &es); - - if (es.reason != NEWT_EXIT_HOTKEY) + pthread_mutex_lock(&ui__lock); + err = __ui_browser__refresh(self); + SLsmg_refresh(); + pthread_mutex_unlock(&ui__lock); + if (err < 0) break; - switch (es.u.key) { - case NEWT_KEY_DOWN: + + key = ui__getch(delay_secs); + + if (key == K_RESIZE) { + pthread_mutex_lock(&ui__lock); + SLtt_get_screen_size(); + SLsmg_reinit_smg(); + pthread_mutex_unlock(&ui__lock); + ui_browser__refresh_dimensions(self); + __ui_browser__show_title(self, self->title); + ui_helpline__puts(self->helpline); + continue; + } + + if (self->use_navkeypressed && !self->navkeypressed) { + if (key == K_DOWN || key == K_UP || + key == K_PGDN || key == K_PGUP || + key == K_HOME || key == K_END || + key == ' ') { + self->navkeypressed = true; + continue; + } else + return key; + } + + switch (key) { + case K_DOWN: if (self->index == self->nr_entries - 1) break; ++self->index; @@ -254,7 +382,7 @@ int ui_browser__run(struct ui_browser *self) self->seek(self, +1, SEEK_CUR); } break; - case NEWT_KEY_UP: + case K_UP: if (self->index == 0) break; --self->index; @@ -263,7 +391,7 @@ int ui_browser__run(struct ui_browser *self) self->seek(self, -1, SEEK_CUR); } break; - case NEWT_KEY_PGDN: + case K_PGDN: case ' ': if (self->top_idx + self->height > self->nr_entries - 1) break; @@ -275,7 +403,7 @@ int ui_browser__run(struct ui_browser *self) self->top_idx += offset; self->seek(self, +offset, SEEK_CUR); break; - case NEWT_KEY_PGUP: + case K_PGUP: if (self->top_idx == 0) break; @@ -288,10 +416,10 @@ int ui_browser__run(struct ui_browser *self) self->top_idx -= offset; self->seek(self, -offset, SEEK_CUR); break; - case NEWT_KEY_HOME: + case K_HOME: ui_browser__reset_index(self); break; - case NEWT_KEY_END: + case K_END: offset = self->height - 1; if (offset >= self->nr_entries) offset = self->nr_entries - 1; @@ -301,10 +429,8 @@ int ui_browser__run(struct ui_browser *self) self->seek(self, -offset, SEEK_END); break; default: - return es.u.key; + return key; } - if (ui_browser__refresh(self) < 0) - return -1; } return -1; } @@ -316,41 +442,105 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *self) int row = 0; if (self->top == NULL || self->top == self->entries) - self->top = head->next; + self->top = ui_browser__list_head_filter_entries(self, head->next); pos = self->top; list_for_each_from(pos, head) { - ui_browser__gotorc(self, row, 0); - self->write(self, pos, row); - if (++row == self->height) - break; + if (!self->filter || !self->filter(self, pos)) { + ui_browser__gotorc(self, row, 0); + self->write(self, pos, row); + if (++row == self->height) + break; + } } return row; } -static struct newtPercentTreeColors { - const char *topColorFg, *topColorBg; - const char *mediumColorFg, *mediumColorBg; - const char *normalColorFg, *normalColorBg; - const char *selColorFg, *selColorBg; - const char *codeColorFg, *codeColorBg; -} defaultPercentTreeColors = { - "red", "lightgray", - "green", "lightgray", - "black", "lightgray", - "lightgray", "magenta", - "blue", "lightgray", +static struct ui_browser__colorset { + const char *name, *fg, *bg; + int colorset; +} ui_browser__colorsets[] = { + { + .colorset = HE_COLORSET_TOP, + .name = "top", + .fg = "red", + .bg = "default", + }, + { + .colorset = HE_COLORSET_MEDIUM, + .name = "medium", + .fg = "green", + .bg = "default", + }, + { + .colorset = HE_COLORSET_NORMAL, + .name = "normal", + .fg = "default", + .bg = "default", + }, + { + .colorset = HE_COLORSET_SELECTED, + .name = "selected", + .fg = "black", + .bg = "lightgray", + }, + { + .colorset = HE_COLORSET_CODE, + .name = "code", + .fg = "blue", + .bg = "default", + }, + { + .name = NULL, + } }; + +static int ui_browser__color_config(const char *var, const char *value, + void *data __used) +{ + char *fg = NULL, *bg; + int i; + + /* same dir for all commands */ + if (prefixcmp(var, "colors.") != 0) + return 0; + + for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) { + const char *name = var + 7; + + if (strcmp(ui_browser__colorsets[i].name, name) != 0) + continue; + + fg = strdup(value); + if (fg == NULL) + break; + + bg = strchr(fg, ','); + if (bg == NULL) + break; + + *bg = '\0'; + while (isspace(*++bg)); + ui_browser__colorsets[i].bg = bg; + ui_browser__colorsets[i].fg = fg; + return 0; + } + + free(fg); + return -1; +} + void ui_browser__init(void) { - struct newtPercentTreeColors *c = &defaultPercentTreeColors; + int i = 0; - 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); + perf_config(ui_browser__color_config, NULL); + + while (ui_browser__colorsets[i].name) { + struct ui_browser__colorset *c = &ui_browser__colorsets[i++]; + sltt_set_color(c->colorset, c->name, c->fg, c->bg); + } } diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index fc63dda1091..a2c707d33c5 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h @@ -2,7 +2,6 @@ #define _PERF_UI_BROWSER_H_ 1 #include <stdbool.h> -#include <newt.h> #include <sys/types.h> #include "../types.h" @@ -13,15 +12,19 @@ #define HE_COLORSET_CODE 54 struct ui_browser { - newtComponent form, sb; u64 index, top_idx; void *top, *entries; u16 y, x, width, height; void *priv; + const char *title; + char *helpline; unsigned int (*refresh)(struct ui_browser *self); void (*write)(struct ui_browser *self, void *entry, int row); void (*seek)(struct ui_browser *self, off_t offset, int whence); + bool (*filter)(struct ui_browser *self, void *entry); u32 nr_entries; + bool navkeypressed; + bool use_navkeypressed; }; void ui_browser__set_color(struct ui_browser *self, int color); @@ -32,15 +35,14 @@ void ui_browser__refresh_dimensions(struct ui_browser *self); void ui_browser__reset_index(struct ui_browser *self); void ui_browser__gotorc(struct ui_browser *self, int y, int x); -void ui_browser__add_exit_key(struct ui_browser *self, int key); -void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]); void __ui_browser__show_title(struct ui_browser *browser, const char *title); void ui_browser__show_title(struct ui_browser *browser, const char *title); int ui_browser__show(struct ui_browser *self, const char *title, const char *helpline, ...); void ui_browser__hide(struct ui_browser *self); int ui_browser__refresh(struct ui_browser *self); -int ui_browser__run(struct ui_browser *self); +int ui_browser__run(struct ui_browser *browser, int delay_secs); +void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries); void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence); unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self); diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 0229723aceb..4e0cb7fea7d 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c @@ -6,6 +6,7 @@ #include "../../sort.h" #include "../../symbol.h" #include <pthread.h> +#include <newt.h> static void ui__error_window(const char *fmt, ...) { @@ -20,12 +21,17 @@ struct annotate_browser { struct ui_browser b; struct rb_root entries; struct rb_node *curr_hot; + struct objdump_line *selection; + int nr_asm_entries; + int nr_entries; + bool hide_src_code; }; struct objdump_line_rb_node { struct rb_node rb_node; double percent; u32 idx; + int idx_asm; }; static inline @@ -34,9 +40,22 @@ struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self) return (struct objdump_line_rb_node *)(self + 1); } +static bool objdump_line__filter(struct ui_browser *browser, void *entry) +{ + struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); + + if (ab->hide_src_code) { + struct objdump_line *ol = list_entry(entry, struct objdump_line, node); + return ol->offset == -1; + } + + return false; +} + static void annotate_browser__write(struct ui_browser *self, void *entry, int row) { - struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); + struct annotate_browser *ab = container_of(self, struct annotate_browser, b); + struct objdump_line *ol = list_entry(entry, struct objdump_line, node); bool current_entry = ui_browser__is_current_entry(self, row); int width = self->width; @@ -51,6 +70,11 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro SLsmg_write_char(':'); slsmg_write_nstring(" ", 8); + + /* The scroll bar isn't being used */ + if (!self->navkeypressed) + width += 1; + if (!*ol->line) slsmg_write_nstring(" ", width - 18); else @@ -58,6 +82,8 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro if (!current_entry) ui_browser__set_color(self, HE_COLORSET_CODE); + else + ab->selection = ol; } static double objdump_line__calc_percent(struct objdump_line *self, @@ -141,7 +167,8 @@ static void annotate_browser__set_top(struct annotate_browser *self, static void annotate_browser__calc_percent(struct annotate_browser *browser, int evidx) { - struct symbol *sym = browser->b.priv; + struct map_symbol *ms = browser->b.priv; + struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); struct objdump_line *pos; @@ -163,25 +190,60 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, browser->curr_hot = rb_last(&browser->entries); } +static bool annotate_browser__toggle_source(struct annotate_browser *browser) +{ + struct objdump_line *ol; + struct objdump_line_rb_node *olrb; + off_t offset = browser->b.index - browser->b.top_idx; + + browser->b.seek(&browser->b, offset, SEEK_CUR); + ol = list_entry(browser->b.top, struct objdump_line, node); + olrb = objdump_line__rb(ol); + + if (browser->hide_src_code) { + if (olrb->idx_asm < offset) + offset = olrb->idx; + + browser->b.nr_entries = browser->nr_entries; + browser->hide_src_code = false; + browser->b.seek(&browser->b, -offset, SEEK_CUR); + browser->b.top_idx = olrb->idx - offset; + browser->b.index = olrb->idx; + } else { + if (olrb->idx_asm < 0) { + ui_helpline__puts("Only available for assembly lines."); + browser->b.seek(&browser->b, -offset, SEEK_CUR); + return false; + } + + if (olrb->idx_asm < offset) + offset = olrb->idx_asm; + + browser->b.nr_entries = browser->nr_asm_entries; + browser->hide_src_code = true; + browser->b.seek(&browser->b, -offset, SEEK_CUR); + browser->b.top_idx = olrb->idx_asm - offset; + browser->b.index = olrb->idx_asm; + } + + return true; +} + static int annotate_browser__run(struct annotate_browser *self, int evidx, - int refresh) + int nr_events, void(*timer)(void *arg), + void *arg, int delay_secs) { struct rb_node *nd = NULL; - struct symbol *sym = self->b.priv; - /* - * RIGHT To allow builtin-annotate to cycle thru multiple symbols by - * examining the exit key for this function. - */ - int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB, - NEWT_KEY_RIGHT, 0 }; + struct map_symbol *ms = self->b.priv; + struct symbol *sym = ms->sym; + const char *help = "<-, ESC: exit, TAB/shift+TAB: cycle hottest lines, " + "H: Hottest, -> Line action, S -> Toggle source " + "code view"; int key; - if (ui_browser__show(&self->b, sym->name, - "<-, -> or ESC: exit, TAB/shift+TAB: " - "cycle hottest lines, H: Hottest") < 0) + if (ui_browser__show(&self->b, sym->name, help) < 0) return -1; - ui_browser__add_exit_keys(&self->b, exit_keys); annotate_browser__calc_percent(self, evidx); if (self->curr_hot) @@ -189,13 +251,10 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, nd = self->curr_hot; - if (refresh != 0) - newtFormSetTimer(self->b.form, refresh); - while (1) { - key = ui_browser__run(&self->b); + key = ui_browser__run(&self->b, delay_secs); - if (refresh != 0) { + if (delay_secs != 0) { annotate_browser__calc_percent(self, evidx); /* * Current line focus got out of the list of most active @@ -207,15 +266,14 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, } switch (key) { - case -1: - /* - * FIXME we need to check if it was - * es.reason == NEWT_EXIT_TIMER - */ - if (refresh != 0) + case K_TIMER: + if (timer != NULL) + timer(arg); + + if (delay_secs != 0) symbol__annotate_decay_histogram(sym, evidx); continue; - case NEWT_KEY_TAB: + case K_TAB: if (nd != NULL) { nd = rb_prev(nd); if (nd == NULL) @@ -223,7 +281,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, } else nd = self->curr_hot; break; - case NEWT_KEY_UNTAB: + case K_UNTAB: if (nd != NULL) nd = rb_next(nd); if (nd == NULL) @@ -234,8 +292,68 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, case 'H': nd = self->curr_hot; break; - default: + case 'S': + if (annotate_browser__toggle_source(self)) + ui_helpline__puts(help); + continue; + case K_ENTER: + case K_RIGHT: + if (self->selection == NULL) { + ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); + continue; + } + + if (self->selection->offset == -1) { + ui_helpline__puts("Actions are only available for assembly lines."); + continue; + } else { + char *s = strstr(self->selection->line, "callq "); + struct annotation *notes; + struct symbol *target; + u64 ip; + + if (s == NULL) { + ui_helpline__puts("Actions are only available for the 'callq' instruction."); + continue; + } + + s = strchr(s, ' '); + if (s++ == NULL) { + ui_helpline__puts("Invallid callq instruction."); + continue; + } + + ip = strtoull(s, NULL, 16); + ip = ms->map->map_ip(ms->map, ip); + target = map__find_symbol(ms->map, ip, NULL); + if (target == NULL) { + ui_helpline__puts("The called function was not found."); + continue; + } + + notes = symbol__annotation(target); + pthread_mutex_lock(¬es->lock); + + if (notes->src == NULL && + symbol__alloc_hist(target, nr_events) < 0) { + pthread_mutex_unlock(¬es->lock); + ui__warning("Not enough memory for annotating '%s' symbol!\n", + target->name); + continue; + } + + pthread_mutex_unlock(¬es->lock); + symbol__tui_annotate(target, ms->map, evidx, nr_events, + timer, arg, delay_secs); + } + continue; + case K_LEFT: + case K_ESC: + case 'q': + case CTRL('c'): goto out; + default: + continue; } if (nd != NULL) @@ -246,22 +364,31 @@ out: return key; } -int hist_entry__tui_annotate(struct hist_entry *he, int evidx) +int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events, + void(*timer)(void *arg), void *arg, int delay_secs) { - return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, 0); + return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, nr_events, + timer, arg, delay_secs); } int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, - int refresh) + int nr_events, void(*timer)(void *arg), void *arg, + int delay_secs) { struct objdump_line *pos, *n; struct annotation *notes; + struct map_symbol ms = { + .map = map, + .sym = sym, + }; struct annotate_browser browser = { .b = { .refresh = ui_browser__list_head_refresh, .seek = ui_browser__list_head_seek, .write = annotate_browser__write, - .priv = sym, + .filter = objdump_line__filter, + .priv = &ms, + .use_navkeypressed = true, }, }; int ret; @@ -288,12 +415,18 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, if (browser.b.width < line_len) browser.b.width = line_len; rbpos = objdump_line__rb(pos); - rbpos->idx = browser.b.nr_entries++; + rbpos->idx = browser.nr_entries++; + if (pos->offset != -1) + rbpos->idx_asm = browser.nr_asm_entries++; + else + rbpos->idx_asm = -1; } + browser.b.nr_entries = browser.nr_entries; browser.b.entries = ¬es->src->source, browser.b.width += 18; /* Percentage */ - ret = annotate_browser__run(&browser, evidx, refresh); + ret = annotate_browser__run(&browser, evidx, nr_events, + timer, arg, delay_secs); list_for_each_entry_safe(pos, n, ¬es->src->source, node) { list_del(&pos->node); objdump_line__free(pos); diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 5d767c622df..4663dcb2a19 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c @@ -24,8 +24,12 @@ struct hist_browser { struct hists *hists; struct hist_entry *he_selection; struct map_symbol *selection; + bool has_symbols; }; +static int hists__browser_title(struct hists *self, char *bf, size_t size, + const char *ev_name); + static void hist_browser__refresh_dimensions(struct hist_browser *self) { /* 3 == +/- toggle symbol before actual hist_entry rendering */ @@ -290,28 +294,34 @@ static void hist_browser__set_folding(struct hist_browser *self, bool unfold) ui_browser__reset_index(&self->b); } -static int hist_browser__run(struct hist_browser *self, const char *title) +static int hist_browser__run(struct hist_browser *self, const char *ev_name, + void(*timer)(void *arg), void *arg, int delay_secs) { int key; - int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't', - NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, - NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0, }; + char title[160]; self->b.entries = &self->hists->entries; self->b.nr_entries = self->hists->nr_entries; hist_browser__refresh_dimensions(self); + hists__browser_title(self->hists, title, sizeof(title), ev_name); if (ui_browser__show(&self->b, title, "Press '?' for help on key bindings") < 0) return -1; - ui_browser__add_exit_keys(&self->b, exit_keys); - while (1) { - key = ui_browser__run(&self->b); + key = ui_browser__run(&self->b, delay_secs); switch (key) { + case -1: + /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */ + timer(arg); + ui_browser__update_nr_entries(&self->b, self->hists->nr_entries); + hists__browser_title(self->hists, title, sizeof(title), + ev_name); + ui_browser__show_title(&self->b, title); + continue; case 'D': { /* Debug */ static int seq; struct hist_entry *h = rb_entry(self->b.top, @@ -334,7 +344,7 @@ static int hist_browser__run(struct hist_browser *self, const char *title) /* Expand the whole world. */ hist_browser__set_folding(self, true); break; - case NEWT_KEY_ENTER: + case K_ENTER: if (hist_browser__toggle_fold(self)) break; /* fall thru */ @@ -532,7 +542,7 @@ static int hist_browser__show_entry(struct hist_browser *self, char s[256]; double percent; int printed = 0; - int color, width = self->b.width; + int width = self->b.width - 6; /* The percentage */ char folded_sign = ' '; bool current_entry = ui_browser__is_current_entry(&self->b, row); off_t row_offset = entry->row_offset; @@ -548,26 +558,35 @@ static int hist_browser__show_entry(struct hist_browser *self, } if (row_offset == 0) { - hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false, - 0, false, self->hists->stats.total_period); + hist_entry__snprintf(entry, s, sizeof(s), self->hists); percent = (entry->period * 100.0) / self->hists->stats.total_period; - color = HE_COLORSET_SELECTED; - if (!current_entry) { - if (percent >= MIN_RED) - color = HE_COLORSET_TOP; - else if (percent >= MIN_GREEN) - color = HE_COLORSET_MEDIUM; - else - color = HE_COLORSET_NORMAL; - } - - ui_browser__set_color(&self->b, color); + ui_browser__set_percent_color(&self->b, percent, current_entry); ui_browser__gotorc(&self->b, row, 0); if (symbol_conf.use_callchain) { slsmg_printf("%c ", folded_sign); width -= 2; } + + slsmg_printf(" %5.2f%%", percent); + + /* The scroll bar isn't being used */ + if (!self->b.navkeypressed) + width += 1; + + if (!current_entry || !self->b.navkeypressed) + ui_browser__set_color(&self->b, HE_COLORSET_NORMAL); + + if (symbol_conf.show_nr_samples) { + slsmg_printf(" %11u", entry->nr_events); + width -= 12; + } + + if (symbol_conf.show_total_period) { + slsmg_printf(" %12" PRIu64, entry->period); + width -= 13; + } + slsmg_write_nstring(s, width); ++row; ++printed; @@ -585,14 +604,23 @@ static int hist_browser__show_entry(struct hist_browser *self, return printed; } +static void ui_browser__hists_init_top(struct ui_browser *browser) +{ + if (browser->top == NULL) { + struct hist_browser *hb; + + hb = container_of(browser, struct hist_browser, b); + browser->top = rb_first(&hb->hists->entries); + } +} + static unsigned int hist_browser__refresh(struct ui_browser *self) { unsigned row = 0; struct rb_node *nd; struct hist_browser *hb = container_of(self, struct hist_browser, b); - if (self->top == NULL) - self->top = rb_first(&hb->hists->entries); + ui_browser__hists_init_top(self); for (nd = self->top; nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); @@ -644,6 +672,8 @@ static void ui_browser__hists_seek(struct ui_browser *self, if (self->nr_entries == 0) return; + ui_browser__hists_init_top(self); + switch (whence) { case SEEK_SET: nd = hists__filter_entries(rb_first(self->entries)); @@ -761,6 +791,8 @@ static struct hist_browser *hist_browser__new(struct hists *hists) self->hists = hists; self->b.refresh = hist_browser__refresh; self->b.seek = ui_browser__hists_seek; + self->b.use_navkeypressed = true, + self->has_symbols = sort_sym.list.next != NULL; } return self; @@ -782,11 +814,12 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self) } static int hists__browser_title(struct hists *self, char *bf, size_t size, - const char *ev_name, const struct dso *dso, - const struct thread *thread) + const char *ev_name) { char unit; int printed; + const struct dso *dso = self->dso_filter; + const struct thread *thread = self->thread_filter; unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; nr_events = convert_unit(nr_events, &unit); @@ -803,16 +836,15 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, return printed; } -static int perf_evsel__hists_browse(struct perf_evsel *evsel, +static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, const char *helpline, const char *ev_name, - bool left_exits) + bool left_exits, + void(*timer)(void *arg), void *arg, + int delay_secs) { struct hists *self = &evsel->hists; struct hist_browser *browser = hist_browser__new(self); struct pstack *fstack; - const struct thread *thread_filter = NULL; - const struct dso *dso_filter = NULL; - char msg[160]; int key = -1; if (browser == NULL) @@ -824,8 +856,6 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, ui_helpline__push(helpline); - hists__browser_title(self, msg, sizeof(msg), ev_name, - dso_filter, thread_filter); while (1) { const struct thread *thread = NULL; const struct dso *dso = NULL; @@ -834,7 +864,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, annotate = -2, zoom_dso = -2, zoom_thread = -2, browse_map = -2; - key = hist_browser__run(browser, msg); + key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); if (browser->he_selection != NULL) { thread = hist_browser__selected_thread(browser); @@ -842,14 +872,23 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, } switch (key) { - case NEWT_KEY_TAB: - case NEWT_KEY_UNTAB: + case K_TAB: + case K_UNTAB: + if (nr_events == 1) + continue; /* * Exit the browser, let hists__browser_tree * go to the next or previous */ goto out_free_stack; case 'a': + if (!browser->has_symbols) { + ui__warning( + "Annotation is only available for symbolic views, " + "include \"sym\" in --sort to use it."); + continue; + } + if (browser->selection == NULL || browser->selection->sym == NULL || browser->selection->map->dso->annotate_warned) @@ -859,25 +898,29 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, goto zoom_dso; case 't': goto zoom_thread; - case NEWT_KEY_F1: + case K_F1: case 'h': case '?': - ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" - "<- Zoom out\n" - "a Annotate current symbol\n" - "h/?/F1 Show this window\n" - "C Collapse all callchains\n" - "E Expand all callchains\n" - "d Zoom into current DSO\n" - "t Zoom into current Thread\n" - "TAB/UNTAB Switch events\n" - "q/CTRL+C Exit browser"); + ui__help_window("h/?/F1 Show this window\n" + "UP/DOWN/PGUP\n" + "PGDN/SPACE Navigate\n" + "q/ESC/CTRL+C Exit browser\n\n" + "For multiple event sessions:\n\n" + "TAB/UNTAB Switch events\n\n" + "For symbolic views (--sort has sym):\n\n" + "-> Zoom into DSO/Threads & Annotate current symbol\n" + "<- Zoom out\n" + "a Annotate current symbol\n" + "C Collapse all callchains\n" + "E Expand all callchains\n" + "d Zoom into current DSO\n" + "t Zoom into current Thread\n"); continue; - case NEWT_KEY_ENTER: - case NEWT_KEY_RIGHT: + case K_ENTER: + case K_RIGHT: /* menu */ break; - case NEWT_KEY_LEFT: { + case K_LEFT: { const void *top; if (pstack__empty(fstack)) { @@ -889,21 +932,27 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, continue; } top = pstack__pop(fstack); - if (top == &dso_filter) + if (top == &browser->hists->dso_filter) goto zoom_out_dso; - if (top == &thread_filter) + if (top == &browser->hists->thread_filter) goto zoom_out_thread; continue; } - case NEWT_KEY_ESCAPE: + case K_ESC: if (!left_exits && !ui__dialog_yesno("Do you really want to exit?")) continue; /* Fall thru */ - default: + case 'q': + case CTRL('c'): goto out_free_stack; + default: + continue; } + if (!browser->has_symbols) + goto add_exit_option; + if (browser->selection != NULL && browser->selection->sym != NULL && !browser->selection->map->dso->annotate_warned && @@ -913,14 +962,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, if (thread != NULL && asprintf(&options[nr_options], "Zoom %s %s(%d) thread", - (thread_filter ? "out of" : "into"), + (browser->hists->thread_filter ? "out of" : "into"), (thread->comm_set ? thread->comm : ""), thread->pid) > 0) zoom_thread = nr_options++; if (dso != NULL && asprintf(&options[nr_options], "Zoom %s %s DSO", - (dso_filter ? "out of" : "into"), + (browser->hists->dso_filter ? "out of" : "into"), (dso->kernel ? "the Kernel" : dso->short_name)) > 0) zoom_dso = nr_options++; @@ -928,7 +977,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, browser->selection->map != NULL && asprintf(&options[nr_options], "Browse map details") > 0) browse_map = nr_options++; - +add_exit_option: options[nr_options++] = (char *)"Exit"; choice = ui__popup_menu(nr_options, options); @@ -948,46 +997,52 @@ do_annotate: he = hist_browser__selected_entry(browser); if (he == NULL) continue; - - hist_entry__tui_annotate(he, evsel->idx); + /* + * Don't let this be freed, say, by hists__decay_entry. + */ + he->used = true; + hist_entry__tui_annotate(he, evsel->idx, nr_events, + timer, arg, delay_secs); + he->used = false; + ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); } else if (choice == browse_map) map__browse(browser->selection->map); else if (choice == zoom_dso) { zoom_dso: - if (dso_filter) { - pstack__remove(fstack, &dso_filter); + if (browser->hists->dso_filter) { + pstack__remove(fstack, &browser->hists->dso_filter); zoom_out_dso: ui_helpline__pop(); - dso_filter = NULL; + browser->hists->dso_filter = NULL; + sort_dso.elide = false; } else { if (dso == NULL) continue; ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", dso->kernel ? "the Kernel" : dso->short_name); - dso_filter = dso; - pstack__push(fstack, &dso_filter); + browser->hists->dso_filter = dso; + sort_dso.elide = true; + pstack__push(fstack, &browser->hists->dso_filter); } - hists__filter_by_dso(self, dso_filter); - hists__browser_title(self, msg, sizeof(msg), ev_name, - dso_filter, thread_filter); + hists__filter_by_dso(self); hist_browser__reset(browser); } else if (choice == zoom_thread) { zoom_thread: - if (thread_filter) { - pstack__remove(fstack, &thread_filter); + if (browser->hists->thread_filter) { + pstack__remove(fstack, &browser->hists->thread_filter); zoom_out_thread: ui_helpline__pop(); - thread_filter = NULL; + browser->hists->thread_filter = NULL; + sort_thread.elide = false; } else { ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", thread->comm_set ? thread->comm : "", thread->pid); - thread_filter = thread; - pstack__push(fstack, &thread_filter); + browser->hists->thread_filter = thread; + sort_thread.elide = true; + pstack__push(fstack, &browser->hists->thread_filter); } - hists__filter_by_thread(self, thread_filter); - hists__browser_title(self, msg, sizeof(msg), ev_name, - dso_filter, thread_filter); + hists__filter_by_thread(self); hist_browser__reset(browser); } } @@ -1026,9 +1081,10 @@ static void perf_evsel_menu__write(struct ui_browser *browser, menu->selection = evsel; } -static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) +static int perf_evsel_menu__run(struct perf_evsel_menu *menu, + int nr_events, const char *help, + void(*timer)(void *arg), void *arg, int delay_secs) { - int exit_keys[] = { NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; struct perf_evlist *evlist = menu->b.priv; struct perf_evsel *pos; const char *ev_name, *title = "Available samples"; @@ -1038,50 +1094,65 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) "ESC: exit, ENTER|->: Browse histograms") < 0) return -1; - ui_browser__add_exit_keys(&menu->b, exit_keys); - while (1) { - key = ui_browser__run(&menu->b); + key = ui_browser__run(&menu->b, delay_secs); switch (key) { - case NEWT_KEY_RIGHT: - case NEWT_KEY_ENTER: + case K_TIMER: + timer(arg); + continue; + case K_RIGHT: + case K_ENTER: if (!menu->selection) continue; pos = menu->selection; browse_hists: + perf_evlist__set_selected(evlist, pos); + /* + * Give the calling tool a chance to populate the non + * default evsel resorted hists tree. + */ + if (timer) + timer(arg); ev_name = event_name(pos); - key = perf_evsel__hists_browse(pos, help, ev_name, true); + key = perf_evsel__hists_browse(pos, nr_events, help, + ev_name, true, timer, + arg, delay_secs); ui_browser__show_title(&menu->b, title); - break; - case NEWT_KEY_LEFT: + switch (key) { + case K_TAB: + if (pos->node.next == &evlist->entries) + pos = list_entry(evlist->entries.next, struct perf_evsel, node); + else + pos = list_entry(pos->node.next, struct perf_evsel, node); + goto browse_hists; + case K_UNTAB: + if (pos->node.prev == &evlist->entries) + pos = list_entry(evlist->entries.prev, struct perf_evsel, node); + else + pos = list_entry(pos->node.prev, struct perf_evsel, node); + goto browse_hists; + case K_ESC: + if (!ui__dialog_yesno("Do you really want to exit?")) + continue; + /* Fall thru */ + case 'q': + case CTRL('c'): + goto out; + default: + continue; + } + case K_LEFT: continue; - case NEWT_KEY_ESCAPE: + case K_ESC: if (!ui__dialog_yesno("Do you really want to exit?")) continue; /* Fall thru */ - default: - goto out; - } - - switch (key) { - case NEWT_KEY_TAB: - if (pos->node.next == &evlist->entries) - pos = list_entry(evlist->entries.next, struct perf_evsel, node); - else - pos = list_entry(pos->node.next, struct perf_evsel, node); - goto browse_hists; - case NEWT_KEY_UNTAB: - if (pos->node.prev == &evlist->entries) - pos = list_entry(evlist->entries.prev, struct perf_evsel, node); - else - pos = list_entry(pos->node.prev, struct perf_evsel, node); - goto browse_hists; case 'q': case CTRL('c'): goto out; default: - break; + continue; } } @@ -1091,7 +1162,9 @@ out: } static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, - const char *help) + const char *help, + void(*timer)(void *arg), void *arg, + int delay_secs) { struct perf_evsel *pos; struct perf_evsel_menu menu = { @@ -1121,18 +1194,24 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, pos->name = strdup(ev_name); } - return perf_evsel_menu__run(&menu, help); + return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer, + arg, delay_secs); } -int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help) +int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, + void(*timer)(void *arg), void *arg, + int delay_secs) { if (evlist->nr_entries == 1) { struct perf_evsel *first = list_entry(evlist->entries.next, struct perf_evsel, node); const char *ev_name = event_name(first); - return perf_evsel__hists_browse(first, help, ev_name, false); + return perf_evsel__hists_browse(first, evlist->nr_entries, help, + ev_name, false, timer, arg, + delay_secs); } - return __perf_evlist__tui_browse_hists(evlist, help); + return __perf_evlist__tui_browse_hists(evlist, help, + timer, arg, delay_secs); } diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c index 8462bffe20b..6905bcc8be2 100644 --- a/tools/perf/util/ui/browsers/map.c +++ b/tools/perf/util/ui/browsers/map.c @@ -1,5 +1,6 @@ #include "../libslang.h" #include <elf.h> +#include <newt.h> #include <inttypes.h> #include <sys/ttydefaults.h> #include <ctype.h> @@ -108,11 +109,8 @@ static int map_browser__run(struct map_browser *self) verbose ? "" : "restart with -v to use") < 0) return -1; - if (verbose) - ui_browser__add_exit_key(&self->b, '/'); - while (1) { - key = ui_browser__run(&self->b); + key = ui_browser__run(&self->b, 0); if (verbose && key == '/') map_browser__search(self); diff --git a/tools/perf/util/ui/browsers/top.c b/tools/perf/util/ui/browsers/top.c deleted file mode 100644 index 88403cf8396..00000000000 --- a/tools/perf/util/ui/browsers/top.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> - * - * Parts came from builtin-{top,stat,record}.c, see those files for further - * copyright notes. - * - * Released under the GPL v2. (and only v2, not any later version) - */ -#include "../browser.h" -#include "../../annotate.h" -#include "../helpline.h" -#include "../libslang.h" -#include "../util.h" -#include "../../evlist.h" -#include "../../hist.h" -#include "../../sort.h" -#include "../../symbol.h" -#include "../../top.h" - -struct perf_top_browser { - struct ui_browser b; - struct rb_root root; - struct sym_entry *selection; - float sum_ksamples; - int dso_width; - int dso_short_width; - int sym_width; -}; - -static void perf_top_browser__write(struct ui_browser *browser, void *entry, int row) -{ - struct perf_top_browser *top_browser = container_of(browser, struct perf_top_browser, b); - struct sym_entry *syme = rb_entry(entry, struct sym_entry, rb_node); - bool current_entry = ui_browser__is_current_entry(browser, row); - struct symbol *symbol = sym_entry__symbol(syme); - struct perf_top *top = browser->priv; - int width = browser->width; - double pcnt; - - pcnt = 100.0 - (100.0 * ((top_browser->sum_ksamples - syme->snap_count) / - top_browser->sum_ksamples)); - ui_browser__set_percent_color(browser, pcnt, current_entry); - - if (top->evlist->nr_entries == 1 || !top->display_weighted) { - slsmg_printf("%20.2f ", syme->weight); - width -= 24; - } else { - slsmg_printf("%9.1f %10ld ", syme->weight, syme->snap_count); - width -= 23; - } - - slsmg_printf("%4.1f%%", pcnt); - width -= 7; - - if (verbose) { - slsmg_printf(" %016" PRIx64, symbol->start); - width -= 17; - } - - slsmg_printf(" %-*.*s ", top_browser->sym_width, top_browser->sym_width, - symbol->name); - width -= top_browser->sym_width; - slsmg_write_nstring(width >= syme->map->dso->long_name_len ? - syme->map->dso->long_name : - syme->map->dso->short_name, width); - - if (current_entry) - top_browser->selection = syme; -} - -static void perf_top_browser__update_rb_tree(struct perf_top_browser *browser) -{ - struct perf_top *top = browser->b.priv; - u64 top_idx = browser->b.top_idx; - - browser->root = RB_ROOT; - browser->b.top = NULL; - browser->sum_ksamples = perf_top__decay_samples(top, &browser->root); - /* - * No active symbols - */ - if (top->rb_entries == 0) - return; - - perf_top__find_widths(top, &browser->root, &browser->dso_width, - &browser->dso_short_width, - &browser->sym_width); - if (browser->sym_width + browser->dso_width > browser->b.width - 29) { - browser->dso_width = browser->dso_short_width; - if (browser->sym_width + browser->dso_width > browser->b.width - 29) - browser->sym_width = browser->b.width - browser->dso_width - 29; - } - - /* - * Adjust the ui_browser indexes since the entries in the browser->root - * rb_tree may have changed, then seek it from start, so that we get a - * possible new top of the screen. - */ - browser->b.nr_entries = top->rb_entries; - - if (top_idx >= browser->b.nr_entries) { - if (browser->b.height >= browser->b.nr_entries) - top_idx = browser->b.nr_entries - browser->b.height; - else - top_idx = 0; - } - - if (browser->b.index >= top_idx + browser->b.height) - browser->b.index = top_idx + browser->b.index - browser->b.top_idx; - - if (browser->b.index >= browser->b.nr_entries) - browser->b.index = browser->b.nr_entries - 1; - - browser->b.top_idx = top_idx; - browser->b.seek(&browser->b, top_idx, SEEK_SET); -} - -static void perf_top_browser__annotate(struct perf_top_browser *browser) -{ - struct sym_entry *syme = browser->selection; - struct symbol *sym = sym_entry__symbol(syme); - struct annotation *notes = symbol__annotation(sym); - struct perf_top *top = browser->b.priv; - - if (notes->src != NULL) - goto do_annotation; - - pthread_mutex_lock(¬es->lock); - - top->sym_filter_entry = NULL; - - if (symbol__alloc_hist(sym, top->evlist->nr_entries) < 0) { - pr_err("Not enough memory for annotating '%s' symbol!\n", - sym->name); - pthread_mutex_unlock(¬es->lock); - return; - } - - top->sym_filter_entry = syme; - - pthread_mutex_unlock(¬es->lock); -do_annotation: - symbol__tui_annotate(sym, syme->map, 0, top->delay_secs * 1000); -} - -static int perf_top_browser__run(struct perf_top_browser *browser) -{ - int key; - char title[160]; - struct perf_top *top = browser->b.priv; - int delay_msecs = top->delay_secs * 1000; - int exit_keys[] = { 'a', NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; - - perf_top_browser__update_rb_tree(browser); - perf_top__header_snprintf(top, title, sizeof(title)); - perf_top__reset_sample_counters(top); - - if (ui_browser__show(&browser->b, title, - "ESC: exit, ENTER|->|a: Live Annotate") < 0) - return -1; - - newtFormSetTimer(browser->b.form, delay_msecs); - ui_browser__add_exit_keys(&browser->b, exit_keys); - - while (1) { - key = ui_browser__run(&browser->b); - - switch (key) { - case -1: - /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */ - perf_top_browser__update_rb_tree(browser); - perf_top__header_snprintf(top, title, sizeof(title)); - perf_top__reset_sample_counters(top); - ui_browser__set_color(&browser->b, NEWT_COLORSET_ROOT); - SLsmg_gotorc(0, 0); - slsmg_write_nstring(title, browser->b.width); - break; - case 'a': - case NEWT_KEY_RIGHT: - case NEWT_KEY_ENTER: - if (browser->selection) - perf_top_browser__annotate(browser); - break; - case NEWT_KEY_LEFT: - continue; - case NEWT_KEY_ESCAPE: - if (!ui__dialog_yesno("Do you really want to exit?")) - continue; - /* Fall thru */ - default: - goto out; - } - } -out: - ui_browser__hide(&browser->b); - return key; -} - -int perf_top__tui_browser(struct perf_top *top) -{ - struct perf_top_browser browser = { - .b = { - .entries = &browser.root, - .refresh = ui_browser__rb_tree_refresh, - .seek = ui_browser__rb_tree_seek, - .write = perf_top_browser__write, - .priv = top, - }, - }; - - return perf_top_browser__run(&browser); -} diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h index ab6028d0c40..fdcbc0270ac 100644 --- a/tools/perf/util/ui/helpline.h +++ b/tools/perf/util/ui/helpline.h @@ -1,6 +1,9 @@ #ifndef _PERF_UI_HELPLINE_H_ #define _PERF_UI_HELPLINE_H_ 1 +#include <stdio.h> +#include <stdarg.h> + void ui_helpline__init(void); void ui_helpline__pop(void); void ui_helpline__push(const char *msg); diff --git a/tools/perf/util/ui/keysyms.h b/tools/perf/util/ui/keysyms.h new file mode 100644 index 00000000000..3458b198576 --- /dev/null +++ b/tools/perf/util/ui/keysyms.h @@ -0,0 +1,25 @@ +#ifndef _PERF_KEYSYMS_H_ +#define _PERF_KEYSYMS_H_ 1 + +#include "libslang.h" + +#define K_DOWN SL_KEY_DOWN +#define K_END SL_KEY_END +#define K_ENTER '\r' +#define K_ESC 033 +#define K_F1 SL_KEY_F(1) +#define K_HOME SL_KEY_HOME +#define K_LEFT SL_KEY_LEFT +#define K_PGDN SL_KEY_NPAGE +#define K_PGUP SL_KEY_PPAGE +#define K_RIGHT SL_KEY_RIGHT +#define K_TAB '\t' +#define K_UNTAB SL_KEY_UNTAB +#define K_UP SL_KEY_UP + +/* Not really keys */ +#define K_TIMER -1 +#define K_ERROR -2 +#define K_RESIZE -3 + +#endif /* _PERF_KEYSYMS_H_ */ diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h index 2b63e1c9b18..4d54b6450f5 100644 --- a/tools/perf/util/ui/libslang.h +++ b/tools/perf/util/ui/libslang.h @@ -24,4 +24,6 @@ #define sltt_set_color SLtt_set_color #endif +#define SL_KEY_UNTAB 0x1000 + #endif /* _PERF_UI_SLANG_H_ */ diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c index ee46d671db5..1e6ba06980c 100644 --- a/tools/perf/util/ui/setup.c +++ b/tools/perf/util/ui/setup.c @@ -7,6 +7,7 @@ #include "browser.h" #include "helpline.h" #include "ui.h" +#include "libslang.h" pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; @@ -17,6 +18,33 @@ static void newt_suspend(void *d __used) newtResume(); } +static int ui__init(void) +{ + int err = SLkp_init(); + + if (err < 0) + goto out; + + SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB); +out: + return err; +} + +static void ui__exit(void) +{ + SLtt_set_cursor_visibility(1); + SLsmg_refresh(); + SLsmg_reset_smg(); + SLang_reset_tty(); +} + +static void ui__signal(int sig) +{ + ui__exit(); + psignal(sig, "perf"); + exit(0); +} + void setup_browser(bool fallback_to_pager) { if (!isatty(1) || !use_browser || dump_trace) { @@ -28,10 +56,16 @@ void setup_browser(bool fallback_to_pager) use_browser = 1; newtInit(); - newtCls(); + ui__init(); newtSetSuspendCallback(newt_suspend, NULL); ui_helpline__init(); ui_browser__init(); + + signal(SIGSEGV, ui__signal); + signal(SIGFPE, ui__signal); + signal(SIGINT, ui__signal); + signal(SIGQUIT, ui__signal); + signal(SIGTERM, ui__signal); } void exit_browser(bool wait_for_ok) @@ -41,6 +75,6 @@ void exit_browser(bool wait_for_ok) char title[] = "Fatal Error", ok[] = "Ok"; newtWinMessage(title, ok, ui_helpline__last_msg); } - newtFinished(); + ui__exit(); } } |