diff options
Diffstat (limited to 'tools/perf/util')
43 files changed, 2144 insertions, 1016 deletions
diff --git a/tools/perf/util/bitmap.c b/tools/perf/util/bitmap.c new file mode 100644 index 00000000000..5e230acae1e --- /dev/null +++ b/tools/perf/util/bitmap.c @@ -0,0 +1,21 @@ +/* + * From lib/bitmap.c + * Helper functions for bitmap.h. + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ +#include <linux/bitmap.h> + +int __bitmap_weight(const unsigned long *bitmap, int bits) +{ + int k, w = 0, lim = bits/BITS_PER_LONG; + + for (k = 0; k < lim; k++) + w += hweight_long(bitmap[k]); + + if (bits % BITS_PER_LONG) + w += hweight_long(bitmap[k] & BITMAP_LAST_WORD_MASK(bits)); + + return w; +} diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index db628af6d20..21a52e0a443 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -17,6 +17,13 @@ #include "callchain.h" +bool ip_callchain__valid(struct ip_callchain *chain, event_t *event) +{ + unsigned int chain_size = event->header.size; + chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; + return chain->nr * sizeof(u64) <= chain_size; +} + #define chain_for_each_child(child, parent) \ list_for_each_entry(child, &parent->children, brothers) @@ -160,7 +167,7 @@ create_child(struct callchain_node *parent, bool inherit_children) { struct callchain_node *new; - new = malloc(sizeof(*new)); + new = zalloc(sizeof(*new)); if (!new) { perror("not enough memory to create child for code path tree"); return NULL; @@ -206,7 +213,7 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) for (i = start; i < chain->nr; i++) { struct callchain_list *call; - call = malloc(sizeof(*call)); + call = zalloc(sizeof(*call)); if (!call) { perror("not enough memory for the code path tree"); return; @@ -379,7 +386,7 @@ int append_chain(struct callchain_node *root, struct ip_callchain *chain, if (!chain->nr) return 0; - filtered = malloc(sizeof(*filtered) + + filtered = zalloc(sizeof(*filtered) + chain->nr * sizeof(struct resolved_ip)); if (!filtered) return -ENOMEM; diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 8a7e8bbd0fd..1cba1f5504e 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -4,6 +4,7 @@ #include "../perf.h" #include <linux/list.h> #include <linux/rbtree.h> +#include "event.h" #include "util.h" #include "symbol.h" @@ -33,6 +34,7 @@ typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_node *, struct callchain_param { enum chain_mode mode; + u32 print_limit; double min_percent; sort_chain_func_t sort; }; @@ -58,4 +60,6 @@ static inline u64 cumul_hits(struct callchain_node *node) int register_callchain_param(struct callchain_param *param); int append_chain(struct callchain_node *root, struct ip_callchain *chain, struct map_symbol *syms); + +bool ip_callchain__valid(struct ip_callchain *chain, event_t *event); #endif /* __PERF_CALLCHAIN_H */ diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index e3fa8d3d11b..50771b5813e 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -7,6 +7,23 @@ #include "strlist.h" #include "thread.h" +const char *event__name[] = { + [0] = "TOTAL", + [PERF_RECORD_MMAP] = "MMAP", + [PERF_RECORD_LOST] = "LOST", + [PERF_RECORD_COMM] = "COMM", + [PERF_RECORD_EXIT] = "EXIT", + [PERF_RECORD_THROTTLE] = "THROTTLE", + [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", + [PERF_RECORD_FORK] = "FORK", + [PERF_RECORD_READ] = "READ", + [PERF_RECORD_SAMPLE] = "SAMPLE", + [PERF_RECORD_HEADER_ATTR] = "ATTR", + [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", + [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", + [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", +}; + static pid_t event__synthesize_comm(pid_t pid, int full, event__handler_t process, struct perf_session *session) @@ -172,17 +189,17 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, int event__synthesize_modules(event__handler_t process, struct perf_session *session, - struct kernel_info *kerninfo) + struct machine *machine) { struct rb_node *nd; - struct map_groups *kmaps = &kerninfo->kmaps; + struct map_groups *kmaps = &machine->kmaps; u16 misc; /* * kernel uses 0 for user space maps, see kernel/perf_event.c * __perf_event_mmap */ - if (is_host_kernel(kerninfo)) + if (machine__is_host(machine)) misc = PERF_RECORD_MISC_KERNEL; else misc = PERF_RECORD_MISC_GUEST_KERNEL; @@ -204,7 +221,7 @@ int event__synthesize_modules(event__handler_t process, (sizeof(ev.mmap.filename) - size)); ev.mmap.start = pos->start; ev.mmap.len = pos->end - pos->start; - ev.mmap.pid = kerninfo->pid; + ev.mmap.pid = machine->pid; memcpy(ev.mmap.filename, pos->dso->long_name, pos->dso->long_name_len + 1); @@ -267,7 +284,7 @@ static int find_symbol_cb(void *arg, const char *name, char type, u64 start) int event__synthesize_kernel_mmap(event__handler_t process, struct perf_session *session, - struct kernel_info *kerninfo, + struct machine *machine, const char *symbol_name) { size_t size; @@ -288,8 +305,8 @@ int event__synthesize_kernel_mmap(event__handler_t process, */ struct process_symbol_args args = { .name = symbol_name, }; - mmap_name = kern_mmap_name(kerninfo, name_buff); - if (is_host_kernel(kerninfo)) { + mmap_name = machine__mmap_name(machine, name_buff, sizeof(name_buff)); + if (machine__is_host(machine)) { /* * kernel uses PERF_RECORD_MISC_USER for user space maps, * see kernel/perf_event.c __perf_event_mmap @@ -298,10 +315,10 @@ int event__synthesize_kernel_mmap(event__handler_t process, filename = "/proc/kallsyms"; } else { ev.header.misc = PERF_RECORD_MISC_GUEST_KERNEL; - if (is_default_guest(kerninfo)) + if (machine__is_default_guest(machine)) filename = (char *) symbol_conf.default_guest_kallsyms; else { - sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir); + sprintf(path, "%s/proc/kallsyms", machine->root_dir); filename = path; } } @@ -309,7 +326,7 @@ int event__synthesize_kernel_mmap(event__handler_t process, if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0) return -ENOENT; - map = kerninfo->vmlinux_maps[MAP__FUNCTION]; + map = machine->vmlinux_maps[MAP__FUNCTION]; size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), "%s%s", mmap_name, symbol_name) + 1; size = ALIGN(size, sizeof(u64)); @@ -318,7 +335,7 @@ int event__synthesize_kernel_mmap(event__handler_t process, ev.mmap.pgoff = args.start; ev.mmap.start = map->start; ev.mmap.len = map->end - ev.mmap.start; - ev.mmap.pid = kerninfo->pid; + ev.mmap.pid = machine->pid; return process(&ev, session); } @@ -368,7 +385,7 @@ int event__process_comm(event_t *self, struct perf_session *session) int event__process_lost(event_t *self, struct perf_session *session) { dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); - session->events_stats.lost += self->lost.lost; + session->hists.stats.total_lost += self->lost.lost; return 0; } @@ -389,18 +406,18 @@ static int event__process_kernel_mmap(event_t *self, { struct map *map; char kmmap_prefix[PATH_MAX]; - struct kernel_info *kerninfo; + struct machine *machine; enum dso_kernel_type kernel_type; bool is_kernel_mmap; - kerninfo = kerninfo__findnew(&session->kerninfo_root, self->mmap.pid); - if (!kerninfo) { - pr_err("Can't find id %d's kerninfo\n", self->mmap.pid); + machine = perf_session__findnew_machine(session, self->mmap.pid); + if (!machine) { + pr_err("Can't find id %d's machine\n", self->mmap.pid); goto out_problem; } - kern_mmap_name(kerninfo, kmmap_prefix); - if (is_host_kernel(kerninfo)) + machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix)); + if (machine__is_host(machine)) kernel_type = DSO_TYPE_KERNEL; else kernel_type = DSO_TYPE_GUEST_KERNEL; @@ -429,10 +446,8 @@ static int event__process_kernel_mmap(event_t *self, } else strcpy(short_module_name, self->mmap.filename); - map = map_groups__new_module(&kerninfo->kmaps, - self->mmap.start, - self->mmap.filename, - kerninfo); + map = machine__new_module(machine, self->mmap.start, + self->mmap.filename); if (map == NULL) goto out_problem; @@ -449,27 +464,25 @@ static int event__process_kernel_mmap(event_t *self, * Should be there already, from the build-id table in * the header. */ - struct dso *kernel = __dsos__findnew(&kerninfo->dsos__kernel, - kmmap_prefix); + struct dso *kernel = __dsos__findnew(&machine->kernel_dsos, + kmmap_prefix); if (kernel == NULL) goto out_problem; kernel->kernel = kernel_type; - if (__map_groups__create_kernel_maps(&kerninfo->kmaps, - kerninfo->vmlinux_maps, kernel) < 0) + if (__machine__create_kernel_maps(machine, kernel) < 0) goto out_problem; - event_set_kernel_mmap_len(kerninfo->vmlinux_maps, self); - perf_session__set_kallsyms_ref_reloc_sym(kerninfo->vmlinux_maps, - symbol_name, - self->mmap.pgoff); - if (is_default_guest(kerninfo)) { + event_set_kernel_mmap_len(machine->vmlinux_maps, self); + perf_session__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, + symbol_name, + self->mmap.pgoff); + if (machine__is_default_guest(machine)) { /* * preload dso of guest kernel and modules */ - dso__load(kernel, - kerninfo->vmlinux_maps[MAP__FUNCTION], - NULL); + dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION], + NULL); } } return 0; @@ -479,7 +492,7 @@ out_problem: int event__process_mmap(event_t *self, struct perf_session *session) { - struct kernel_info *kerninfo; + struct machine *machine; struct thread *thread; struct map *map; u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; @@ -497,9 +510,11 @@ int event__process_mmap(event_t *self, struct perf_session *session) return 0; } + machine = perf_session__find_host_machine(session); + if (machine == NULL) + goto out_problem; thread = perf_session__findnew(session, self->mmap.pid); - kerninfo = kerninfo__findhost(&session->kerninfo_root); - map = map__new(&kerninfo->dsos__user, self->mmap.start, + map = map__new(&machine->user_dsos, self->mmap.start, self->mmap.len, self->mmap.pgoff, self->mmap.pid, self->mmap.filename, MAP__FUNCTION, session->cwd, session->cwdlen); @@ -546,7 +561,7 @@ void thread__find_addr_map(struct thread *self, struct addr_location *al) { struct map_groups *mg = &self->mg; - struct kernel_info *kerninfo = NULL; + struct machine *machine = NULL; al->thread = self; al->addr = addr; @@ -555,19 +570,23 @@ void thread__find_addr_map(struct thread *self, if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) { al->level = 'k'; - kerninfo = kerninfo__findhost(&session->kerninfo_root); - mg = &kerninfo->kmaps; + machine = perf_session__find_host_machine(session); + if (machine == NULL) { + al->map = NULL; + return; + } + mg = &machine->kmaps; } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) { al->level = '.'; - kerninfo = kerninfo__findhost(&session->kerninfo_root); + machine = perf_session__find_host_machine(session); } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) { al->level = 'g'; - kerninfo = kerninfo__find(&session->kerninfo_root, pid); - if (!kerninfo) { + machine = perf_session__find_machine(session, pid); + if (machine == NULL) { al->map = NULL; return; } - mg = &kerninfo->kmaps; + mg = &machine->kmaps; } else { /* * 'u' means guest os user space. @@ -603,10 +622,9 @@ try_again: * in the whole kernel symbol list. */ if ((long long)al->addr < 0 && - cpumode == PERF_RECORD_MISC_KERNEL && - kerninfo && - mg != &kerninfo->kmaps) { - mg = &kerninfo->kmaps; + cpumode == PERF_RECORD_MISC_KERNEL && + machine && mg != &machine->kmaps) { + mg = &machine->kmaps; goto try_again; } } else @@ -631,8 +649,10 @@ static void dso__calc_col_width(struct dso *self) if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && (!symbol_conf.dso_list || strlist__has_entry(symbol_conf.dso_list, self->name))) { - unsigned int slen = strlen(self->name); - if (slen > dsos__col_width) + u16 slen = self->short_name_len; + if (verbose) + slen = self->long_name_len; + if (dsos__col_width < slen) dsos__col_width = slen; } @@ -653,6 +673,16 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, goto out_filtered; dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); + /* + * Have we already created the kernel maps for the host machine? + * + * This should have happened earlier, when we processed the kernel MMAP + * events, but for older perf.data files there was no such thing, so do + * it now. + */ + if (cpumode == PERF_RECORD_MISC_KERNEL && + session->host_machine.vmlinux_maps[MAP__FUNCTION] == NULL) + machine__create_kernel_maps(&session->host_machine); thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, self->ip.pid, self->ip.ip, al); @@ -679,6 +709,13 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, dso__calc_col_width(al->map->dso); al->sym = map__find_symbol(al->map, al->addr, filter); + } else { + const unsigned int unresolved_col_width = BITS_PER_LONG / 4; + + if (dsos__col_width < unresolved_col_width && + !symbol_conf.col_width_list_str && !symbol_conf.field_sep && + !symbol_conf.dso_list) + dsos__col_width = unresolved_col_width; } if (symbol_conf.sym_list && al->sym && @@ -718,6 +755,7 @@ int event__parse_sample(event_t *event, u64 type, struct sample_data *data) array++; } + data->id = -1ULL; if (type & PERF_SAMPLE_ID) { data->id = *array; array++; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 4af2ed5d48a..8577085db06 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -84,11 +84,12 @@ struct build_id_event { char filename[]; }; -enum perf_header_event_type { /* above any possible kernel type */ +enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_HEADER_ATTR = 64, PERF_RECORD_HEADER_EVENT_TYPE = 65, PERF_RECORD_HEADER_TRACING_DATA = 66, PERF_RECORD_HEADER_BUILD_ID = 67, + PERF_RECORD_FINISHED_ROUND = 68, PERF_RECORD_HEADER_MAX }; @@ -130,20 +131,6 @@ typedef union event_union { struct build_id_event build_id; } event_t; -struct events_stats { - u64 total; - u64 lost; -}; - -struct event_stat_id { - struct rb_node rb_node; - struct rb_root hists; - struct events_stats stats; - u64 config; - u64 event_stream; - u32 type; -}; - void event__print_totals(void); struct perf_session; @@ -156,12 +143,12 @@ void event__synthesize_threads(event__handler_t process, struct perf_session *session); int event__synthesize_kernel_mmap(event__handler_t process, struct perf_session *session, - struct kernel_info *kerninfo, + struct machine *machine, const char *symbol_name); int event__synthesize_modules(event__handler_t process, struct perf_session *session, - struct kernel_info *kerninfo); + struct machine *machine); int event__process_comm(event_t *self, struct perf_session *session); int event__process_lost(event_t *self, struct perf_session *session); @@ -173,4 +160,6 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, struct addr_location *al, symbol_filter_t filter); int event__parse_sample(event_t *event, u64 type, struct sample_data *data); +extern const char *event__name[]; + #endif /* __PERF_RECORD_H */ diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 75d01676802..8847bec64c5 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -229,10 +229,9 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd) int err = 0; u16 kmisc, umisc; - for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { - struct kernel_info *pos = rb_entry(nd, struct kernel_info, - rb_node); - if (is_host_kernel(pos)) { + for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); + if (machine__is_host(pos)) { kmisc = PERF_RECORD_MISC_KERNEL; umisc = PERF_RECORD_MISC_USER; } else { @@ -240,11 +239,11 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd) umisc = PERF_RECORD_MISC_GUEST_USER; } - err = __dsos__write_buildid_table(&pos->dsos__kernel, pos->pid, - kmisc, fd); + err = __dsos__write_buildid_table(&pos->kernel_dsos, pos->pid, + kmisc, fd); if (err == 0) - err = __dsos__write_buildid_table(&pos->dsos__user, - pos->pid, umisc, fd); + err = __dsos__write_buildid_table(&pos->user_dsos, + pos->pid, umisc, fd); if (err) break; } @@ -378,11 +377,10 @@ static int dsos__cache_build_ids(struct perf_header *self) if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) return -1; - for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { - struct kernel_info *pos = rb_entry(nd, struct kernel_info, - rb_node); - ret |= __dsos__cache_build_ids(&pos->dsos__kernel, debugdir); - ret |= __dsos__cache_build_ids(&pos->dsos__user, debugdir); + for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); + ret |= __dsos__cache_build_ids(&pos->kernel_dsos, debugdir); + ret |= __dsos__cache_build_ids(&pos->user_dsos, debugdir); } return ret ? -1 : 0; } @@ -394,11 +392,10 @@ static bool dsos__read_build_ids(struct perf_header *self, bool with_hits) struct perf_session, header); struct rb_node *nd; - for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { - struct kernel_info *pos = rb_entry(nd, struct kernel_info, - rb_node); - ret |= __dsos__read_build_ids(&pos->dsos__kernel, with_hits); - ret |= __dsos__read_build_ids(&pos->dsos__user, with_hits); + for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); + ret |= __dsos__read_build_ids(&pos->kernel_dsos, with_hits); + ret |= __dsos__read_build_ids(&pos->user_dsos, with_hits); } return ret; @@ -439,7 +436,6 @@ static int perf_header__adds_write(struct perf_header *self, int fd) trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; } - if (perf_header__has_feat(self, HEADER_BUILD_ID)) { struct perf_file_section *buildid_sec; @@ -685,13 +681,13 @@ static int __event_process_build_id(struct build_id_event *bev, { int err = -1; struct list_head *head; - struct kernel_info *kerninfo; + struct machine *machine; u16 misc; struct dso *dso; enum dso_kernel_type dso_type; - kerninfo = kerninfo__findnew(&session->kerninfo_root, bev->pid); - if (!kerninfo) + machine = perf_session__findnew_machine(session, bev->pid); + if (!machine) goto out; misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; @@ -699,16 +695,16 @@ static int __event_process_build_id(struct build_id_event *bev, switch (misc) { case PERF_RECORD_MISC_KERNEL: dso_type = DSO_TYPE_KERNEL; - head = &kerninfo->dsos__kernel; + head = &machine->kernel_dsos; break; case PERF_RECORD_MISC_GUEST_KERNEL: dso_type = DSO_TYPE_GUEST_KERNEL; - head = &kerninfo->dsos__kernel; + head = &machine->kernel_dsos; break; case PERF_RECORD_MISC_USER: case PERF_RECORD_MISC_GUEST_USER: dso_type = DSO_TYPE_USER; - head = &kerninfo->dsos__user; + head = &machine->user_dsos; break; default: goto out; @@ -716,10 +712,18 @@ static int __event_process_build_id(struct build_id_event *bev, dso = __dsos__findnew(head, filename); if (dso != NULL) { + char sbuild_id[BUILD_ID_SIZE * 2 + 1]; + dso__set_build_id(dso, &bev->build_id); - if (filename[0] == '[') - dso->kernel = dso_type; - } + + if (filename[0] == '[') + dso->kernel = dso_type; + + build_id__sprintf(dso->build_id, sizeof(dso->build_id), + sbuild_id); + pr_debug("build id event received for %s: %s\n", + dso->long_name, sbuild_id); + } err = 0; out: @@ -770,7 +774,7 @@ static int perf_file_section__process(struct perf_file_section *self, switch (feat) { case HEADER_TRACE_INFO: - trace_report(fd); + trace_report(fd, false); break; case HEADER_BUILD_ID: @@ -785,12 +789,16 @@ static int perf_file_section__process(struct perf_file_section *self, } static int perf_file_header__read_pipe(struct perf_pipe_file_header *self, - struct perf_header *ph, int fd) + struct perf_header *ph, int fd, + bool repipe) { if (do_read(fd, self, sizeof(*self)) <= 0 || memcmp(&self->magic, __perf_magic, sizeof(self->magic))) return -1; + if (repipe && do_write(STDOUT_FILENO, self, sizeof(*self)) < 0) + return -1; + if (self->size != sizeof(*self)) { u64 size = bswap_64(self->size); @@ -808,7 +816,8 @@ static int perf_header__read_pipe(struct perf_session *session, int fd) struct perf_header *self = &session->header; struct perf_pipe_file_header f_header; - if (perf_file_header__read_pipe(&f_header, self, fd) < 0) { + if (perf_file_header__read_pipe(&f_header, self, fd, + session->repipe) < 0) { pr_debug("incompatible file format\n"); return -EINVAL; } @@ -913,6 +922,14 @@ perf_header__find_attr(u64 id, struct perf_header *header) { int i; + /* + * We set id to -1 if the data file doesn't contain sample + * ids. Check for this and avoid walking through the entire + * list of ids which may be large. + */ + if (id == -1ULL) + return NULL; + for (i = 0; i < header->attrs; i++) { struct perf_header_attr *attr = header->attr[i]; int j; @@ -1099,12 +1116,17 @@ int event__process_tracing_data(event_t *self, lseek(session->fd, offset + sizeof(struct tracing_data_event), SEEK_SET); - size_read = trace_report(session->fd); + size_read = trace_report(session->fd, session->repipe); padding = ALIGN(size_read, sizeof(u64)) - size_read; if (read(session->fd, buf, padding) < 0) die("reading input file"); + if (session->repipe) { + int retw = write(STDOUT_FILENO, buf, padding); + if (retw <= 0 || retw != padding) + die("repiping tracing data padding"); + } if (size_read + padding != size) die("tracing data size mismatch"); @@ -1114,7 +1136,7 @@ int event__process_tracing_data(event_t *self, int event__synthesize_build_id(struct dso *pos, u16 misc, event__handler_t process, - struct kernel_info *kerninfo, + struct machine *machine, struct perf_session *session) { event_t ev; @@ -1131,7 +1153,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id)); ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; ev.build_id.header.misc = misc; - ev.build_id.pid = kerninfo->pid; + ev.build_id.pid = machine->pid; ev.build_id.header.size = sizeof(ev.build_id) + len; memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len); @@ -1140,67 +1162,6 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, return err; } -static int __event_synthesize_build_ids(struct list_head *head, u16 misc, - event__handler_t process, - struct kernel_info *kerninfo, - struct perf_session *session) -{ - struct dso *pos; - - dsos__for_each_with_build_id(pos, head) { - int err; - if (!pos->hit) - continue; - - err = event__synthesize_build_id(pos, misc, process, - kerninfo, session); - if (err < 0) - return err; - } - - return 0; -} - -int event__synthesize_build_ids(event__handler_t process, - struct perf_session *session) -{ - int err = 0; - u16 kmisc, umisc; - struct kernel_info *pos; - struct rb_node *nd; - - if (!dsos__read_build_ids(&session->header, true)) - return 0; - - for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) { - pos = rb_entry(nd, struct kernel_info, rb_node); - if (is_host_kernel(pos)) { - kmisc = PERF_RECORD_MISC_KERNEL; - umisc = PERF_RECORD_MISC_USER; - } else { - kmisc = PERF_RECORD_MISC_GUEST_KERNEL; - umisc = PERF_RECORD_MISC_GUEST_USER; - } - - err = __event_synthesize_build_ids(&pos->dsos__kernel, - kmisc, process, pos, session); - if (err == 0) - err = __event_synthesize_build_ids(&pos->dsos__user, - umisc, process, pos, session); - if (err) - break; - } - - if (err < 0) { - pr_debug("failed to synthesize build ids\n"); - return err; - } - - dsos__cache_build_ids(&session->header); - - return 0; -} - int event__process_build_id(event_t *self, struct perf_session *session) { diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 27591545814..402ac2454cf 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -120,10 +120,8 @@ int event__process_tracing_data(event_t *self, int event__synthesize_build_id(struct dso *pos, u16 misc, event__handler_t process, - struct kernel_info *kerninfo, + struct machine *machine, struct perf_session *session); -int event__synthesize_build_ids(event__handler_t process, - struct perf_session *session); int event__process_build_id(event_t *self, struct perf_session *session); #endif /* __PERF_HEADER_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index ad6b22dde27..9a71c94f057 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1,3 +1,4 @@ +#include "util.h" #include "hist.h" #include "session.h" #include "sort.h" @@ -8,24 +9,21 @@ struct callchain_param callchain_param = { .min_percent = 0.5 }; -void __perf_session__add_count(struct hist_entry *he, - struct addr_location *al, - u64 count) +static void hist_entry__add_cpumode_period(struct hist_entry *self, + unsigned int cpumode, u64 period) { - he->count += count; - - switch (al->cpumode) { + switch (cpumode) { case PERF_RECORD_MISC_KERNEL: - he->count_sys += count; + self->period_sys += period; break; case PERF_RECORD_MISC_USER: - he->count_us += count; + self->period_us += period; break; case PERF_RECORD_MISC_GUEST_KERNEL: - he->count_guest_sys += count; + self->period_guest_sys += period; break; case PERF_RECORD_MISC_GUEST_USER: - he->count_guest_us += count; + self->period_guest_us += period; break; default: break; @@ -33,15 +31,36 @@ void __perf_session__add_count(struct hist_entry *he, } /* - * histogram, sorted on item, collects counts + * histogram, sorted on item, collects periods */ -struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, - struct addr_location *al, - struct symbol *sym_parent, - u64 count, bool *hit) +static struct hist_entry *hist_entry__new(struct hist_entry *template) +{ + size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_node) : 0; + struct hist_entry *self = malloc(sizeof(*self) + callchain_size); + + if (self != NULL) { + *self = *template; + self->nr_events = 1; + if (symbol_conf.use_callchain) + callchain_init(self->callchain); + } + + return self; +} + +static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) { - struct rb_node **p = &hists->rb_node; + if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen) + self->max_sym_namelen = entry->ms.sym->namelen; + ++self->nr_entries; +} + +struct hist_entry *__hists__add_entry(struct hists *self, + struct addr_location *al, + struct symbol *sym_parent, u64 period) +{ + struct rb_node **p = &self->entries.rb_node; struct rb_node *parent = NULL; struct hist_entry *he; struct hist_entry entry = { @@ -52,7 +71,7 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, }, .ip = al->addr, .level = al->level, - .count = count, + .period = period, .parent = sym_parent, }; int cmp; @@ -64,8 +83,9 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, cmp = hist_entry__cmp(&entry, he); if (!cmp) { - *hit = true; - return he; + he->period += period; + ++he->nr_events; + goto out; } if (cmp < 0) @@ -74,14 +94,14 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, p = &(*p)->rb_right; } - he = malloc(sizeof(*he) + (symbol_conf.use_callchain ? - sizeof(struct callchain_node) : 0)); + he = hist_entry__new(&entry); if (!he) return NULL; - *he = entry; rb_link_node(&he->rb_node, parent, p); - rb_insert_color(&he->rb_node, hists); - *hit = false; + rb_insert_color(&he->rb_node, &self->entries); + hists__inc_nr_entries(self, he); +out: + hist_entry__add_cpumode_period(he, al->cpumode, period); return he; } @@ -128,7 +148,7 @@ void hist_entry__free(struct hist_entry *he) * collapse the histogram */ -static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) +static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; @@ -142,9 +162,9 @@ static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) cmp = hist_entry__collapse(iter, he); if (!cmp) { - iter->count += he->count; + iter->period += he->period; hist_entry__free(he); - return; + return false; } if (cmp < 0) @@ -155,9 +175,10 @@ static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, root); + return true; } -void perf_session__collapse_resort(struct rb_root *hists) +void hists__collapse_resort(struct hists *self) { struct rb_root tmp; struct rb_node *next; @@ -167,28 +188,31 @@ void perf_session__collapse_resort(struct rb_root *hists) return; tmp = RB_ROOT; - next = rb_first(hists); + next = rb_first(&self->entries); + self->nr_entries = 0; + self->max_sym_namelen = 0; while (next) { n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); - rb_erase(&n->rb_node, hists); - collapse__insert_entry(&tmp, n); + rb_erase(&n->rb_node, &self->entries); + if (collapse__insert_entry(&tmp, n)) + hists__inc_nr_entries(self, n); } - *hists = tmp; + self->entries = tmp; } /* - * reverse the map, sort on count. + * reverse the map, sort on period. */ -static void perf_session__insert_output_hist_entry(struct rb_root *root, - struct hist_entry *he, - u64 min_callchain_hits) +static void __hists__insert_output_entry(struct rb_root *entries, + struct hist_entry *he, + u64 min_callchain_hits) { - struct rb_node **p = &root->rb_node; + struct rb_node **p = &entries->rb_node; struct rb_node *parent = NULL; struct hist_entry *iter; @@ -200,42 +224,41 @@ static void perf_session__insert_output_hist_entry(struct rb_root *root, parent = *p; iter = rb_entry(parent, struct hist_entry, rb_node); - if (he->count > iter->count) + if (he->period > iter->period) p = &(*p)->rb_left; else p = &(*p)->rb_right; } rb_link_node(&he->rb_node, parent, p); - rb_insert_color(&he->rb_node, root); + rb_insert_color(&he->rb_node, entries); } -u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples) +void hists__output_resort(struct hists *self) { struct rb_root tmp; struct rb_node *next; struct hist_entry *n; u64 min_callchain_hits; - u64 nr_hists = 0; - min_callchain_hits = - total_samples * (callchain_param.min_percent / 100); + min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100); tmp = RB_ROOT; - next = rb_first(hists); + next = rb_first(&self->entries); + + self->nr_entries = 0; + self->max_sym_namelen = 0; while (next) { n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); - rb_erase(&n->rb_node, hists); - perf_session__insert_output_hist_entry(&tmp, n, - min_callchain_hits); - ++nr_hists; + rb_erase(&n->rb_node, &self->entries); + __hists__insert_output_entry(&tmp, n, min_callchain_hits); + hists__inc_nr_entries(self, n); } - *hists = tmp; - return nr_hists; + self->entries = tmp; } static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) @@ -267,7 +290,7 @@ static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, } static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, - int depth, int depth_mask, int count, + int depth, int depth_mask, int period, u64 total_samples, int hits, int left_margin) { @@ -280,7 +303,7 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, ret += fprintf(fp, "|"); else ret += fprintf(fp, " "); - if (!count && i == depth - 1) { + if (!period && i == depth - 1) { double percent; percent = hits * 100.0 / total_samples; @@ -323,6 +346,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, u64 remaining; size_t ret = 0; int i; + uint entries_printed = 0; if (callchain_param.mode == CHAIN_GRAPH_REL) new_total = self->children_hit; @@ -369,6 +393,8 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, new_depth_mask | (1 << depth), left_margin); node = next; + if (++entries_printed == callchain_param.print_limit) + break; } if (callchain_param.mode == CHAIN_GRAPH_REL && @@ -394,6 +420,7 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, bool printed = false; int i = 0; int ret = 0; + u32 entries_printed = 0; list_for_each_entry(chain, &self->val, list) { if (!i++ && sort__first_dimension == SORT_SYM) @@ -414,6 +441,9 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, ret += fprintf(fp, " %s\n", chain->ms.sym->name); else ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); + + if (++entries_printed == callchain_param.print_limit) + break; } ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); @@ -452,6 +482,7 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, struct rb_node *rb_node; struct callchain_node *chain; size_t ret = 0; + u32 entries_printed = 0; rb_node = rb_first(&self->sorted_chain); while (rb_node) { @@ -474,89 +505,88 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, break; } ret += fprintf(fp, "\n"); + if (++entries_printed == callchain_param.print_limit) + break; rb_node = rb_next(rb_node); } return ret; } -int hist_entry__snprintf(struct hist_entry *self, - char *s, size_t size, - struct perf_session *pair_session, - bool show_displacement, - long displacement, bool color, - u64 session_total) +int hist_entry__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 count, total, count_sys, count_us, count_guest_sys, count_guest_us; + u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; const char *sep = symbol_conf.field_sep; int ret; if (symbol_conf.exclude_other && !self->parent) return 0; - if (pair_session) { - count = self->pair ? self->pair->count : 0; - total = pair_session->events_stats.total; - count_sys = self->pair ? self->pair->count_sys : 0; - count_us = self->pair ? self->pair->count_us : 0; - count_guest_sys = self->pair ? self->pair->count_guest_sys : 0; - count_guest_us = self->pair ? self->pair->count_guest_us : 0; + if (pair_hists) { + period = self->pair ? self->pair->period : 0; + total = pair_hists->stats.total_period; + period_sys = self->pair ? self->pair->period_sys : 0; + period_us = self->pair ? self->pair->period_us : 0; + period_guest_sys = self->pair ? self->pair->period_guest_sys : 0; + period_guest_us = self->pair ? self->pair->period_guest_us : 0; } else { - count = self->count; + period = self->period; total = session_total; - count_sys = self->count_sys; - count_us = self->count_us; - count_guest_sys = self->count_guest_sys; - count_guest_us = self->count_guest_us; + period_sys = self->period_sys; + period_us = self->period_us; + period_guest_sys = self->period_guest_sys; + period_guest_us = self->period_guest_us; } if (total) { if (color) ret = percent_color_snprintf(s, size, sep ? "%.2f" : " %6.2f%%", - (count * 100.0) / total); + (period * 100.0) / total); else ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", - (count * 100.0) / total); + (period * 100.0) / total); if (symbol_conf.show_cpu_utilization) { ret += percent_color_snprintf(s + ret, size - ret, sep ? "%.2f" : " %6.2f%%", - (count_sys * 100.0) / total); + (period_sys * 100.0) / total); ret += percent_color_snprintf(s + ret, size - ret, sep ? "%.2f" : " %6.2f%%", - (count_us * 100.0) / total); + (period_us * 100.0) / total); if (perf_guest) { ret += percent_color_snprintf(s + ret, size - ret, sep ? "%.2f" : " %6.2f%%", - (count_guest_sys * 100.0) / + (period_guest_sys * 100.0) / total); ret += percent_color_snprintf(s + ret, size - ret, sep ? "%.2f" : " %6.2f%%", - (count_guest_us * 100.0) / + (period_guest_us * 100.0) / total); } } } else - ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count); + ret = snprintf(s, size, sep ? "%lld" : "%12lld ", period); if (symbol_conf.show_nr_samples) { if (sep) - ret += snprintf(s + ret, size - ret, "%c%lld", *sep, count); + ret += snprintf(s + ret, size - ret, "%c%lld", *sep, period); else - ret += snprintf(s + ret, size - ret, "%11lld", count); + ret += snprintf(s + ret, size - ret, "%11lld", period); } - if (pair_session) { + if (pair_hists) { char bf[32]; double old_percent = 0, new_percent = 0, diff; if (total > 0) - old_percent = (count * 100.0) / total; + old_percent = (period * 100.0) / total; if (session_total > 0) - new_percent = (self->count * 100.0) / session_total; + new_percent = (self->period * 100.0) / session_total; diff = new_percent - old_percent; @@ -595,14 +625,12 @@ int hist_entry__snprintf(struct hist_entry *self, return ret; } -int hist_entry__fprintf(struct hist_entry *self, - struct perf_session *pair_session, - bool show_displacement, - long displacement, FILE *fp, +int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, + bool show_displacement, long displacement, FILE *fp, u64 session_total) { char bf[512]; - hist_entry__snprintf(self, bf, sizeof(bf), pair_session, + hist_entry__snprintf(self, bf, sizeof(bf), pair_hists, show_displacement, displacement, true, session_total); return fprintf(fp, "%s\n", bf); @@ -624,10 +652,8 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, left_margin); } -size_t perf_session__fprintf_hists(struct rb_root *hists, - struct perf_session *pair, - bool show_displacement, FILE *fp, - u64 session_total) +size_t hists__fprintf(struct hists *self, struct hists *pair, + bool show_displacement, FILE *fp) { struct sort_entry *se; struct rb_node *nd; @@ -636,7 +662,7 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, long displacement = 0; unsigned int width; const char *sep = symbol_conf.field_sep; - char *col_width = symbol_conf.col_width_list_str; + const char *col_width = symbol_conf.col_width_list_str; init_rem_hits(); @@ -733,7 +759,7 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, fprintf(fp, "\n#\n"); print_entries: - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { + for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); if (show_displacement) { @@ -745,10 +771,10 @@ print_entries: ++position; } ret += hist_entry__fprintf(h, pair, show_displacement, - displacement, fp, session_total); + displacement, fp, self->stats.total_period); if (symbol_conf.use_callchain) - ret += hist_entry__fprintf_callchain(h, fp, session_total); + ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period); if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(&h->thread->mg, @@ -761,3 +787,271 @@ print_entries: return ret; } + +enum hist_filter { + HIST_FILTER__DSO, + HIST_FILTER__THREAD, +}; + +void hists__filter_by_dso(struct hists *self, const struct dso *dso) +{ + struct rb_node *nd; + + self->nr_entries = self->stats.total_period = 0; + self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; + self->max_sym_namelen = 0; + + for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (symbol_conf.exclude_other && !h->parent) + continue; + + if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { + h->filtered |= (1 << HIST_FILTER__DSO); + continue; + } + + h->filtered &= ~(1 << HIST_FILTER__DSO); + if (!h->filtered) { + ++self->nr_entries; + self->stats.total_period += h->period; + self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; + if (h->ms.sym && + self->max_sym_namelen < h->ms.sym->namelen) + self->max_sym_namelen = h->ms.sym->namelen; + } + } +} + +void hists__filter_by_thread(struct hists *self, const struct thread *thread) +{ + struct rb_node *nd; + + self->nr_entries = self->stats.total_period = 0; + self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; + self->max_sym_namelen = 0; + + for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (thread != NULL && h->thread != thread) { + h->filtered |= (1 << HIST_FILTER__THREAD); + continue; + } + h->filtered &= ~(1 << HIST_FILTER__THREAD); + if (!h->filtered) { + ++self->nr_entries; + self->stats.total_period += h->period; + self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; + if (h->ms.sym && + self->max_sym_namelen < h->ms.sym->namelen) + self->max_sym_namelen = h->ms.sym->namelen; + } + } +} + +static int symbol__alloc_hist(struct symbol *self) +{ + struct sym_priv *priv = symbol__priv(self); + const int size = (sizeof(*priv->hist) + + (self->end - self->start) * sizeof(u64)); + + priv->hist = zalloc(size); + return priv->hist == NULL ? -1 : 0; +} + +int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) +{ + unsigned int sym_size, offset; + struct symbol *sym = self->ms.sym; + struct sym_priv *priv; + struct sym_hist *h; + + if (!sym || !self->ms.map) + return 0; + + priv = symbol__priv(sym); + if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) + return -ENOMEM; + + sym_size = sym->end - sym->start; + offset = ip - sym->start; + + pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); + + if (offset >= sym_size) + return 0; + + h = priv->hist; + h->sum++; + h->ip[offset]++; + + pr_debug3("%#Lx %s: period++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, + self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); + return 0; +} + +static struct objdump_line *objdump_line__new(s64 offset, char *line) +{ + struct objdump_line *self = malloc(sizeof(*self)); + + if (self != NULL) { + self->offset = offset; + self->line = line; + } + + return self; +} + +void objdump_line__free(struct objdump_line *self) +{ + free(self->line); + free(self); +} + +static void objdump__add_line(struct list_head *head, struct objdump_line *line) +{ + list_add_tail(&line->node, head); +} + +struct objdump_line *objdump__get_next_ip_line(struct list_head *head, + struct objdump_line *pos) +{ + list_for_each_entry_continue(pos, head, node) + if (pos->offset >= 0) + return pos; + + return NULL; +} + +static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, + struct list_head *head) +{ + struct symbol *sym = self->ms.sym; + struct objdump_line *objdump_line; + char *line = NULL, *tmp, *tmp2, *c; + size_t line_len; + s64 line_ip, offset = -1; + + if (getline(&line, &line_len, file) < 0) + return -1; + + if (!line) + return -1; + + while (line_len != 0 && isspace(line[line_len - 1])) + line[--line_len] = '\0'; + + c = strchr(line, '\n'); + if (c) + *c = 0; + + line_ip = -1; + + /* + * Strip leading spaces: + */ + tmp = line; + while (*tmp) { + if (*tmp != ' ') + break; + tmp++; + } + + if (*tmp) { + /* + * Parse hexa addresses followed by ':' + */ + line_ip = strtoull(tmp, &tmp2, 16); + if (*tmp2 != ':') + line_ip = -1; + } + + if (line_ip != -1) { + u64 start = map__rip_2objdump(self->ms.map, sym->start); + offset = line_ip - start; + } + + objdump_line = objdump_line__new(offset, line); + if (objdump_line == NULL) { + free(line); + return -1; + } + objdump__add_line(head, objdump_line); + + return 0; +} + +int hist_entry__annotate(struct hist_entry *self, struct list_head *head) +{ + struct symbol *sym = self->ms.sym; + struct map *map = self->ms.map; + struct dso *dso = map->dso; + const char *filename = dso->long_name; + char command[PATH_MAX * 2]; + FILE *file; + u64 len; + + if (!filename) + return -1; + + if (dso->origin == DSO__ORIG_KERNEL) { + if (dso->annotate_warned) + return 0; + dso->annotate_warned = 1; + pr_err("Can't annotate %s: No vmlinux file was found in the " + "path:\n", sym->name); + vmlinux_path__fprintf(stderr); + return -1; + } + + pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, + filename, sym->name, map->unmap_ip(map, sym->start), + map->unmap_ip(map, sym->end)); + + len = sym->end - sym->start; + + pr_debug("annotating [%p] %30s : [%p] %30s\n", + dso, dso->long_name, sym, sym->name); + + snprintf(command, sizeof(command), + "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", + map__rip_2objdump(map, sym->start), + map__rip_2objdump(map, sym->end), + filename, filename); + + pr_debug("Executing: %s\n", command); + + file = popen(command, "r"); + if (!file) + return -1; + + while (!feof(file)) + if (hist_entry__parse_objdump_line(self, file, head) < 0) + break; + + pclose(file); + return 0; +} + +void hists__inc_nr_events(struct hists *self, u32 type) +{ + ++self->stats.nr_events[0]; + ++self->stats.nr_events[type]; +} + +size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) +{ + int i; + size_t ret = 0; + + for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { + if (!event__name[i]) + continue; + ret += fprintf(fp, "%10s events: %10d\n", + event__name[i], self->stats.nr_events[i]); + } + + return ret; +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 9df1c340ec9..6f17dcd8412 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -6,37 +6,104 @@ extern struct callchain_param callchain_param; -struct perf_session; struct hist_entry; struct addr_location; struct symbol; struct rb_root; -void __perf_session__add_count(struct hist_entry *he, - struct addr_location *al, - u64 count); -struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, - struct addr_location *al, - struct symbol *parent, - u64 count, bool *hit); +struct objdump_line { + struct list_head node; + s64 offset; + char *line; +}; + +void objdump_line__free(struct objdump_line *self); +struct objdump_line *objdump__get_next_ip_line(struct list_head *head, + struct objdump_line *pos); + +struct sym_hist { + u64 sum; + u64 ip[0]; +}; + +struct sym_ext { + struct rb_node node; + double percent; + char *path; +}; + +struct sym_priv { + struct sym_hist *hist; + struct sym_ext *ext; +}; + +/* + * The kernel collects the number of events it couldn't send in a stretch and + * when possible sends this number in a PERF_RECORD_LOST event. The number of + * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while + * total_lost tells exactly how many events the kernel in fact lost, i.e. it is + * the sum of all struct lost_event.lost fields reported. + * + * The total_period is needed because by default auto-freq is used, so + * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get + * the total number of low level events, it is necessary to to sum all struct + * sample_event.period and stash the result in total_period. + */ +struct events_stats { + u64 total_period; + u64 total_lost; + u32 nr_events[PERF_RECORD_HEADER_MAX]; + u32 nr_unknown_events; +}; + +struct hists { + struct rb_node rb_node; + struct rb_root entries; + u64 nr_entries; + struct events_stats stats; + u64 config; + u64 event_stream; + u32 type; + u32 max_sym_namelen; +}; + +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 perf_session *pair_session, - bool show_displacement, - long displacement, FILE *fp, - u64 session_total); -int hist_entry__snprintf(struct hist_entry *self, - char *bf, size_t size, - struct perf_session *pair_session, - bool show_displacement, long displacement, - bool color, u64 session_total); +int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, + bool show_displacement, long displacement, FILE *fp, + u64 total); +int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, + struct hists *pair_hists, bool show_displacement, + long displacement, bool color, u64 total); void hist_entry__free(struct hist_entry *); -u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples); -void perf_session__collapse_resort(struct rb_root *hists); -size_t perf_session__fprintf_hists(struct rb_root *hists, - struct perf_session *pair, - bool show_displacement, FILE *fp, - u64 session_total); +void hists__output_resort(struct hists *self); +void hists__collapse_resort(struct hists *self); + +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); + +int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); +int hist_entry__annotate(struct hist_entry *self, struct list_head *head); + +void hists__filter_by_dso(struct hists *self, const struct dso *dso); +void hists__filter_by_thread(struct hists *self, const struct thread *thread); + +#ifdef NO_NEWT_SUPPORT +static inline int hists__browse(struct hists *self __used, + const char *helpline __used, + const char *input_name __used) +{ + return 0; +} +#else +int hists__browse(struct hists *self, const char *helpline, + const char *input_name); +#endif #endif /* __PERF_HIST_H */ diff --git a/tools/perf/util/hweight.c b/tools/perf/util/hweight.c new file mode 100644 index 00000000000..5c1d0d099f0 --- /dev/null +++ b/tools/perf/util/hweight.c @@ -0,0 +1,31 @@ +#include <linux/bitops.h> + +/** + * hweightN - returns the hamming weight of a N-bit word + * @x: the word to weigh + * + * The Hamming Weight of a number is the total number of bits set in it. + */ + +unsigned int hweight32(unsigned int w) +{ + unsigned int res = w - ((w >> 1) & 0x55555555); + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res + (res >> 4)) & 0x0F0F0F0F; + res = res + (res >> 8); + return (res + (res >> 16)) & 0x000000FF; +} + +unsigned long hweight64(__u64 w) +{ +#if BITS_PER_LONG == 32 + return hweight32((unsigned int)(w >> 32)) + hweight32((unsigned int)w); +#elif BITS_PER_LONG == 64 + __u64 res = w - ((w >> 1) & 0x5555555555555555ul); + res = (res & 0x3333333333333333ul) + ((res >> 2) & 0x3333333333333333ul); + res = (res + (res >> 4)) & 0x0F0F0F0F0F0F0F0Ful; + res = res + (res >> 8); + res = res + (res >> 16); + return (res + (res >> 32)) & 0x00000000000000FFul; +#endif +} diff --git a/tools/perf/util/include/asm/bitops.h b/tools/perf/util/include/asm/bitops.h deleted file mode 100644 index 58e9817ffae..00000000000 --- a/tools/perf/util/include/asm/bitops.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _PERF_ASM_BITOPS_H_ -#define _PERF_ASM_BITOPS_H_ - -#include <sys/types.h> -#include "../../types.h" -#include <linux/compiler.h> - -/* CHECKME: Not sure both always match */ -#define BITS_PER_LONG __WORDSIZE - -#include "../../../../include/asm-generic/bitops/__fls.h" -#include "../../../../include/asm-generic/bitops/fls.h" -#include "../../../../include/asm-generic/bitops/fls64.h" -#include "../../../../include/asm-generic/bitops/__ffs.h" -#include "../../../../include/asm-generic/bitops/ffz.h" -#include "../../../../include/asm-generic/bitops/hweight.h" - -#endif diff --git a/tools/perf/util/include/asm/hweight.h b/tools/perf/util/include/asm/hweight.h new file mode 100644 index 00000000000..36cf26d434a --- /dev/null +++ b/tools/perf/util/include/asm/hweight.h @@ -0,0 +1,8 @@ +#ifndef PERF_HWEIGHT_H +#define PERF_HWEIGHT_H + +#include <linux/types.h> +unsigned int hweight32(unsigned int w); +unsigned long hweight64(__u64 w); + +#endif /* PERF_HWEIGHT_H */ diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h new file mode 100644 index 00000000000..cf6727e99c4 --- /dev/null +++ b/tools/perf/util/include/dwarf-regs.h @@ -0,0 +1,8 @@ +#ifndef _PERF_DWARF_REGS_H_ +#define _PERF_DWARF_REGS_H_ + +#ifdef DWARF_SUPPORT +const char *get_arch_regstr(unsigned int n); +#endif + +#endif diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h index 94507639a8c..eda4416efa0 100644 --- a/tools/perf/util/include/linux/bitmap.h +++ b/tools/perf/util/include/linux/bitmap.h @@ -1,3 +1,35 @@ -#include "../../../../include/linux/bitmap.h" -#include "../../../../include/asm-generic/bitops/find.h" -#include <linux/errno.h> +#ifndef _PERF_BITOPS_H +#define _PERF_BITOPS_H + +#include <string.h> +#include <linux/bitops.h> + +int __bitmap_weight(const unsigned long *bitmap, int bits); + +#define BITMAP_LAST_WORD_MASK(nbits) \ +( \ + ((nbits) % BITS_PER_LONG) ? \ + (1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \ +) + +#define small_const_nbits(nbits) \ + (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG) + +static inline void bitmap_zero(unsigned long *dst, int nbits) +{ + if (small_const_nbits(nbits)) + *dst = 0UL; + else { + int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); + memset(dst, 0, len); + } +} + +static inline int bitmap_weight(const unsigned long *src, int nbits) +{ + if (small_const_nbits(nbits)) + return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits)); + return __bitmap_weight(src, nbits); +} + +#endif /* _PERF_BITOPS_H */ diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h index 8d63116e943..bb4ac2e0538 100644 --- a/tools/perf/util/include/linux/bitops.h +++ b/tools/perf/util/include/linux/bitops.h @@ -1,13 +1,12 @@ #ifndef _PERF_LINUX_BITOPS_H_ #define _PERF_LINUX_BITOPS_H_ -#define __KERNEL__ +#include <linux/kernel.h> +#include <asm/hweight.h> -#define CONFIG_GENERIC_FIND_NEXT_BIT -#define CONFIG_GENERIC_FIND_FIRST_BIT -#include "../../../../include/linux/bitops.h" - -#undef __KERNEL__ +#define BITS_PER_LONG __WORDSIZE +#define BITS_PER_BYTE 8 +#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) static inline void set_bit(int nr, unsigned long *addr) { @@ -20,10 +19,9 @@ static __always_inline int test_bit(unsigned int nr, const unsigned long *addr) (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0; } -unsigned long generic_find_next_zero_le_bit(const unsigned long *addr, unsigned - long size, unsigned long offset); - -unsigned long generic_find_next_le_bit(const unsigned long *addr, unsigned - long size, unsigned long offset); +static inline unsigned long hweight_long(unsigned long w) +{ + return sizeof(w) == 4 ? hweight32(w) : hweight64(w); +} #endif diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h index 388ab1bfd11..1eb804fd3fb 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h @@ -28,6 +28,8 @@ (type *)((char *)__mptr - offsetof(type, member)); }) #endif +#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) + #ifndef max #define max(x, y) ({ \ typeof(x) _max1 = (x); \ diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 7facd016ec9..e672f2fef65 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -245,7 +245,7 @@ void map_groups__init(struct map_groups *self) self->maps[i] = RB_ROOT; INIT_LIST_HEAD(&self->removed_maps[i]); } - self->this_kerninfo = NULL; + self->machine = NULL; } void map_groups__flush(struct map_groups *self) @@ -513,133 +513,116 @@ struct map *maps__find(struct rb_root *maps, u64 ip) return NULL; } -struct kernel_info *add_new_kernel_info(struct rb_root *kerninfo_root, - pid_t pid, const char *root_dir) +int machine__init(struct machine *self, const char *root_dir, pid_t pid) { - struct rb_node **p = &kerninfo_root->rb_node; + map_groups__init(&self->kmaps); + RB_CLEAR_NODE(&self->rb_node); + INIT_LIST_HEAD(&self->user_dsos); + INIT_LIST_HEAD(&self->kernel_dsos); + + self->kmaps.machine = self; + self->pid = pid; + self->root_dir = strdup(root_dir); + return self->root_dir == NULL ? -ENOMEM : 0; +} + +struct machine *machines__add(struct rb_root *self, pid_t pid, + const char *root_dir) +{ + struct rb_node **p = &self->rb_node; struct rb_node *parent = NULL; - struct kernel_info *kerninfo, *pos; + struct machine *pos, *machine = malloc(sizeof(*machine)); - kerninfo = malloc(sizeof(struct kernel_info)); - if (!kerninfo) + if (!machine) return NULL; - kerninfo->pid = pid; - map_groups__init(&kerninfo->kmaps); - kerninfo->root_dir = strdup(root_dir); - RB_CLEAR_NODE(&kerninfo->rb_node); - INIT_LIST_HEAD(&kerninfo->dsos__user); - INIT_LIST_HEAD(&kerninfo->dsos__kernel); - kerninfo->kmaps.this_kerninfo = kerninfo; + if (machine__init(machine, root_dir, pid) != 0) { + free(machine); + return NULL; + } while (*p != NULL) { parent = *p; - pos = rb_entry(parent, struct kernel_info, rb_node); + pos = rb_entry(parent, struct machine, rb_node); if (pid < pos->pid) p = &(*p)->rb_left; else p = &(*p)->rb_right; } - rb_link_node(&kerninfo->rb_node, parent, p); - rb_insert_color(&kerninfo->rb_node, kerninfo_root); - - return kerninfo; -} - -struct kernel_info *kerninfo__find(struct rb_root *kerninfo_root, pid_t pid) -{ - struct rb_node **p = &kerninfo_root->rb_node; - struct rb_node *parent = NULL; - struct kernel_info *kerninfo; - struct kernel_info *default_kerninfo = NULL; - - while (*p != NULL) { - parent = *p; - kerninfo = rb_entry(parent, struct kernel_info, rb_node); - if (pid < kerninfo->pid) - p = &(*p)->rb_left; - else if (pid > kerninfo->pid) - p = &(*p)->rb_right; - else - return kerninfo; - if (!kerninfo->pid) - default_kerninfo = kerninfo; - } + rb_link_node(&machine->rb_node, parent, p); + rb_insert_color(&machine->rb_node, self); - return default_kerninfo; + return machine; } -struct kernel_info *kerninfo__findhost(struct rb_root *kerninfo_root) +struct machine *machines__find(struct rb_root *self, pid_t pid) { - struct rb_node **p = &kerninfo_root->rb_node; + struct rb_node **p = &self->rb_node; struct rb_node *parent = NULL; - struct kernel_info *kerninfo; - pid_t pid = HOST_KERNEL_ID; + struct machine *machine; + struct machine *default_machine = NULL; while (*p != NULL) { parent = *p; - kerninfo = rb_entry(parent, struct kernel_info, rb_node); - if (pid < kerninfo->pid) + machine = rb_entry(parent, struct machine, rb_node); + if (pid < machine->pid) p = &(*p)->rb_left; - else if (pid > kerninfo->pid) + else if (pid > machine->pid) p = &(*p)->rb_right; else - return kerninfo; + return machine; + if (!machine->pid) + default_machine = machine; } - return NULL; + return default_machine; } -struct kernel_info *kerninfo__findnew(struct rb_root *kerninfo_root, pid_t pid) +struct machine *machines__findnew(struct rb_root *self, pid_t pid) { char path[PATH_MAX]; const char *root_dir; - int ret; - struct kernel_info *kerninfo = kerninfo__find(kerninfo_root, pid); + struct machine *machine = machines__find(self, pid); - if (!kerninfo || kerninfo->pid != pid) { + if (!machine || machine->pid != pid) { if (pid == HOST_KERNEL_ID || pid == DEFAULT_GUEST_KERNEL_ID) root_dir = ""; else { if (!symbol_conf.guestmount) goto out; sprintf(path, "%s/%d", symbol_conf.guestmount, pid); - ret = access(path, R_OK); - if (ret) { + if (access(path, R_OK)) { pr_err("Can't access file %s\n", path); goto out; } root_dir = path; } - kerninfo = add_new_kernel_info(kerninfo_root, pid, root_dir); + machine = machines__add(self, pid, root_dir); } out: - return kerninfo; + return machine; } -void kerninfo__process_allkernels(struct rb_root *kerninfo_root, - process_kernel_info process, - void *data) +void machines__process(struct rb_root *self, machine__process_t process, void *data) { struct rb_node *nd; - for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) { - struct kernel_info *pos = rb_entry(nd, struct kernel_info, - rb_node); + for (nd = rb_first(self); nd; nd = rb_next(nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); process(pos, data); } } -char *kern_mmap_name(struct kernel_info *kerninfo, char *buff) +char *machine__mmap_name(struct machine *self, char *bf, size_t size) { - if (is_host_kernel(kerninfo)) - sprintf(buff, "[%s]", "kernel.kallsyms"); - else if (is_default_guest(kerninfo)) - sprintf(buff, "[%s]", "guest.kernel.kallsyms"); + if (machine__is_host(self)) + snprintf(bf, size, "[%s]", "kernel.kallsyms"); + else if (machine__is_default_guest(self)) + snprintf(bf, size, "[%s]", "guest.kernel.kallsyms"); else - sprintf(buff, "[%s.%d]", "guest.kernel.kallsyms", kerninfo->pid); + snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms", self->pid); - return buff; + return bf; } diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 30d38d634e0..f3913451282 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -5,6 +5,7 @@ #include <linux/list.h> #include <linux/rbtree.h> #include <stdio.h> +#include <stdbool.h> #include "types.h" enum map_type { @@ -19,7 +20,7 @@ extern const char *map_type__name[MAP__NR_TYPES]; struct dso; struct ref_reloc_sym; struct map_groups; -struct kernel_info; +struct machine; struct map { union { @@ -29,6 +30,7 @@ struct map { u64 start; u64 end; enum map_type type; + u32 priv; u64 pgoff; /* ip -> dso rip */ @@ -46,25 +48,31 @@ struct kmap { }; struct map_groups { - struct rb_root maps[MAP__NR_TYPES]; - struct list_head removed_maps[MAP__NR_TYPES]; - struct kernel_info *this_kerninfo; + struct rb_root maps[MAP__NR_TYPES]; + struct list_head removed_maps[MAP__NR_TYPES]; + struct machine *machine; }; -/* Native host kernel uses -1 as pid index in kernel_info */ +/* Native host kernel uses -1 as pid index in machine */ #define HOST_KERNEL_ID (-1) #define DEFAULT_GUEST_KERNEL_ID (0) -struct kernel_info { - struct rb_node rb_node; - pid_t pid; - char *root_dir; - struct list_head dsos__user; - struct list_head dsos__kernel; +struct machine { + struct rb_node rb_node; + pid_t pid; + char *root_dir; + struct list_head user_dsos; + struct list_head kernel_dsos; struct map_groups kmaps; - struct map *vmlinux_maps[MAP__NR_TYPES]; + struct map *vmlinux_maps[MAP__NR_TYPES]; }; +static inline +struct map *machine__kernel_map(struct machine *self, enum map_type type) +{ + return self->vmlinux_maps[type]; +} + static inline struct kmap *map__kmap(struct map *self) { return (struct kmap *)(self + 1); @@ -124,36 +132,31 @@ int map_groups__clone(struct map_groups *self, 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); -struct kernel_info *add_new_kernel_info(struct rb_root *kerninfo_root, - pid_t pid, const char *root_dir); -struct kernel_info *kerninfo__find(struct rb_root *kerninfo_root, pid_t pid); -struct kernel_info *kerninfo__findnew(struct rb_root *kerninfo_root, pid_t pid); -struct kernel_info *kerninfo__findhost(struct rb_root *kerninfo_root); -char *kern_mmap_name(struct kernel_info *kerninfo, char *buff); +typedef void (*machine__process_t)(struct machine *self, void *data); + +void machines__process(struct rb_root *self, machine__process_t process, void *data); +struct machine *machines__add(struct rb_root *self, pid_t pid, + const char *root_dir); +struct machine *machines__find_host(struct rb_root *self); +struct machine *machines__find(struct rb_root *self, pid_t pid); +struct machine *machines__findnew(struct rb_root *self, pid_t pid); +char *machine__mmap_name(struct machine *self, char *bf, size_t size); +int machine__init(struct machine *self, const char *root_dir, pid_t pid); /* * Default guest kernel is defined by parameter --guestkallsyms * and --guestmodules */ -static inline int is_default_guest(struct kernel_info *kerninfo) +static inline bool machine__is_default_guest(struct machine *self) { - if (!kerninfo) - return 0; - return kerninfo->pid == DEFAULT_GUEST_KERNEL_ID; + return self ? self->pid == DEFAULT_GUEST_KERNEL_ID : false; } -static inline int is_host_kernel(struct kernel_info *kerninfo) +static inline bool machine__is_host(struct machine *self) { - if (!kerninfo) - return 0; - return kerninfo->pid == HOST_KERNEL_ID; + return self ? self->pid == HOST_KERNEL_ID : false; } -typedef void (*process_kernel_info)(struct kernel_info *kerninfo, void *data); -void kerninfo__process_allkernels(struct rb_root *kerninfo_root, - process_kernel_info process, - void *data); - static inline void map_groups__insert(struct map_groups *self, struct map *map) { maps__insert(&self->maps[map->type], map); @@ -178,10 +181,20 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, symbol_filter_t filter); static inline -struct symbol *map_groups__find_function(struct map_groups *self, u64 addr, - struct map **mapp, symbol_filter_t filter) +struct symbol *machine__find_kernel_symbol(struct machine *self, + enum map_type type, u64 addr, + struct map **mapp, + symbol_filter_t filter) +{ + return map_groups__find_symbol(&self->kmaps, type, addr, mapp, filter); +} + +static inline +struct symbol *machine__find_kernel_function(struct machine *self, u64 addr, + struct map **mapp, + symbol_filter_t filter) { - return map_groups__find_symbol(self, MAP__FUNCTION, addr, mapp, filter); + return machine__find_kernel_symbol(self, MAP__FUNCTION, addr, mapp, filter); } static inline @@ -197,10 +210,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, struct map *map_groups__find_by_name(struct map_groups *self, enum map_type type, const char *name); -struct map *map_groups__new_module(struct map_groups *self, - u64 start, - const char *filename, - struct kernel_info *kerninfo); +struct map *machine__new_module(struct machine *self, u64 start, const char *filename); void map_groups__flush(struct map_groups *self); diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 7a123a94e3f..ccb7c5bb269 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c @@ -2,16 +2,29 @@ #include <stdio.h> #undef _GNU_SOURCE +#include <slang.h> #include <stdlib.h> #include <newt.h> #include <sys/ttydefaults.h> #include "cache.h" #include "hist.h" +#include "pstack.h" #include "session.h" #include "sort.h" #include "symbol.h" +#if SLANG_VERSION < 20104 +#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args) +#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len) +#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\ + (char *)fg, (char *)bg) +#else +#define slsmg_printf SLsmg_printf +#define slsmg_write_nstring SLsmg_write_nstring +#define sltt_set_color SLtt_set_color +#endif + struct ui_progress { newtComponent form, scale; }; @@ -31,7 +44,7 @@ struct ui_progress *ui_progress__new(const char *title, u64 total) self->scale = newtScale(0, 0, cols, total); if (self->scale == NULL) goto out_free_form; - newtFormAddComponents(self->form, self->scale, NULL); + newtFormAddComponent(self->form, self->scale); newtRefresh(); } @@ -57,6 +70,43 @@ void ui_progress__delete(struct ui_progress *self) free(self); } +static void ui_helpline__pop(void) +{ + newtPopHelpLine(); +} + +static void ui_helpline__push(const char *msg) +{ + newtPushHelpLine(msg); +} + +static void ui_helpline__vpush(const char *fmt, va_list ap) +{ + char *s; + + if (vasprintf(&s, fmt, ap) < 0) + vfprintf(stderr, fmt, ap); + else { + ui_helpline__push(s); + free(s); + } +} + +static void ui_helpline__fpush(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + ui_helpline__vpush(fmt, ap); + va_end(ap); +} + +static void ui_helpline__puts(const char *msg) +{ + ui_helpline__pop(); + ui_helpline__push(msg); +} + static char browser__last_msg[1024]; int browser__show_help(const char *format, va_list ap) @@ -69,8 +119,7 @@ int browser__show_help(const char *format, va_list ap) backlog += ret; if (browser__last_msg[backlog - 1] == '\n') { - newtPopHelpLine(); - newtPushHelpLine(browser__last_msg); + ui_helpline__puts(browser__last_msg); newtRefresh(); backlog = 0; } @@ -80,6 +129,7 @@ int browser__show_help(const char *format, va_list ap) static void newt_form__set_exit_keys(newtComponent self) { + newtFormAddHotKey(self, NEWT_KEY_LEFT); newtFormAddHotKey(self, NEWT_KEY_ESCAPE); newtFormAddHotKey(self, 'Q'); newtFormAddHotKey(self, 'q'); @@ -107,7 +157,7 @@ static int popup_menu(int argc, char * const argv[]) if (listbox == NULL) goto out_destroy_form; - newtFormAddComponents(form, listbox, NULL); + newtFormAddComponent(form, listbox); for (i = 0; i < argc; ++i) { int len = strlen(argv[i]); @@ -128,6 +178,48 @@ out_destroy_form: return rc; } +static int ui__help_window(const char *text) +{ + struct newtExitStruct es; + newtComponent tb, form = newt_form__new(); + int rc = -1; + int max_len = 0, nr_lines = 0; + const char *t; + + if (form == NULL) + return -1; + + t = text; + while (1) { + const char *sep = strchr(t, '\n'); + int len; + + if (sep == NULL) + sep = strchr(t, '\0'); + len = sep - t; + if (max_len < len) + max_len = len; + ++nr_lines; + if (*sep == '\0') + break; + t = sep + 1; + } + + tb = newtTextbox(0, 0, max_len, nr_lines, 0); + if (tb == NULL) + goto out_destroy_form; + + newtTextboxSetText(tb, text); + newtFormAddComponent(form, tb); + newtCenteredWindow(max_len, nr_lines, NULL); + newtFormRun(form, &es); + newtPopWindow(); + rc = 0; +out_destroy_form: + newtFormDestroy(form); + return rc; +} + static bool dialog_yesno(const char *msg) { /* newtWinChoice should really be accepting const char pointers... */ @@ -135,6 +227,255 @@ static bool dialog_yesno(const char *msg) return newtWinChoice(NULL, yes, no, (char *)msg) == 1; } +#define HE_COLORSET_TOP 50 +#define HE_COLORSET_MEDIUM 51 +#define HE_COLORSET_NORMAL 52 +#define HE_COLORSET_SELECTED 53 +#define HE_COLORSET_CODE 54 + +static int ui_browser__percent_color(double percent, bool current) +{ + if (current) + return HE_COLORSET_SELECTED; + if (percent >= MIN_RED) + return HE_COLORSET_TOP; + if (percent >= MIN_GREEN) + return HE_COLORSET_MEDIUM; + return HE_COLORSET_NORMAL; +} + +struct ui_browser { + newtComponent form, sb; + u64 index, first_visible_entry_idx; + void *first_visible_entry, *entries; + u16 top, left, width, height; + void *priv; + u32 nr_entries; +}; + +static void ui_browser__refresh_dimensions(struct ui_browser *self) +{ + int cols, rows; + newtGetScreenSize(&cols, &rows); + + if (self->width > cols - 4) + self->width = cols - 4; + self->height = rows - 5; + if (self->height > self->nr_entries) + self->height = self->nr_entries; + self->top = (rows - self->height) / 2; + self->left = (cols - self->width) / 2; +} + +static void ui_browser__reset_index(struct ui_browser *self) +{ + self->index = self->first_visible_entry_idx = 0; + self->first_visible_entry = NULL; +} + +static int objdump_line__show(struct objdump_line *self, struct list_head *head, + int width, struct hist_entry *he, int len, + bool current_entry) +{ + if (self->offset != -1) { + struct symbol *sym = he->ms.sym; + unsigned int hits = 0; + double percent = 0.0; + int color; + struct sym_priv *priv = symbol__priv(sym); + struct sym_ext *sym_ext = priv->ext; + struct sym_hist *h = priv->hist; + s64 offset = self->offset; + struct objdump_line *next = objdump__get_next_ip_line(head, self); + + while (offset < (s64)len && + (next == NULL || offset < next->offset)) { + if (sym_ext) { + percent += sym_ext[offset].percent; + } else + hits += h->ip[offset]; + + ++offset; + } + + if (sym_ext == NULL && h->sum) + percent = 100.0 * hits / h->sum; + + color = ui_browser__percent_color(percent, current_entry); + SLsmg_set_color(color); + slsmg_printf(" %7.2f ", percent); + if (!current_entry) + SLsmg_set_color(HE_COLORSET_CODE); + } else { + int color = ui_browser__percent_color(0, current_entry); + SLsmg_set_color(color); + slsmg_write_nstring(" ", 9); + } + + SLsmg_write_char(':'); + slsmg_write_nstring(" ", 8); + if (!*self->line) + slsmg_write_nstring(" ", width - 18); + else + slsmg_write_nstring(self->line, width - 18); + + return 0; +} + +static int ui_browser__refresh_entries(struct ui_browser *self) +{ + struct objdump_line *pos; + struct list_head *head = self->entries; + struct hist_entry *he = self->priv; + int row = 0; + int len = he->ms.sym->end - he->ms.sym->start; + + if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) + self->first_visible_entry = head->next; + + pos = list_entry(self->first_visible_entry, struct objdump_line, node); + + list_for_each_entry_from(pos, head, node) { + bool current_entry = (self->first_visible_entry_idx + row) == self->index; + SLsmg_gotorc(self->top + row, self->left); + objdump_line__show(pos, head, self->width, + he, len, current_entry); + if (++row == self->height) + break; + } + + SLsmg_set_color(HE_COLORSET_NORMAL); + SLsmg_fill_region(self->top + row, self->left, + self->height - row, self->width, ' '); + + return 0; +} + +static int ui_browser__run(struct ui_browser *self, const char *title, + struct newtExitStruct *es) +{ + if (self->form) { + newtFormDestroy(self->form); + newtPopWindow(); + } + + ui_browser__refresh_dimensions(self); + newtCenteredWindow(self->width + 2, self->height, title); + self->form = newt_form__new(); + if (self->form == NULL) + return -1; + + self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, + HE_COLORSET_NORMAL, + HE_COLORSET_SELECTED); + if (self->sb == NULL) + return -1; + + newtFormAddHotKey(self->form, NEWT_KEY_UP); + newtFormAddHotKey(self->form, NEWT_KEY_DOWN); + newtFormAddHotKey(self->form, NEWT_KEY_PGUP); + newtFormAddHotKey(self->form, NEWT_KEY_PGDN); + newtFormAddHotKey(self->form, NEWT_KEY_HOME); + newtFormAddHotKey(self->form, NEWT_KEY_END); + + if (ui_browser__refresh_entries(self) < 0) + return -1; + newtFormAddComponent(self->form, self->sb); + + while (1) { + unsigned int offset; + + newtFormRun(self->form, es); + + if (es->reason != NEWT_EXIT_HOTKEY) + break; + switch (es->u.key) { + case NEWT_KEY_DOWN: + if (self->index == self->nr_entries - 1) + break; + ++self->index; + if (self->index == self->first_visible_entry_idx + self->height) { + struct list_head *pos = self->first_visible_entry; + ++self->first_visible_entry_idx; + self->first_visible_entry = pos->next; + } + break; + case NEWT_KEY_UP: + if (self->index == 0) + break; + --self->index; + if (self->index < self->first_visible_entry_idx) { + struct list_head *pos = self->first_visible_entry; + --self->first_visible_entry_idx; + self->first_visible_entry = pos->prev; + } + break; + case NEWT_KEY_PGDN: + if (self->first_visible_entry_idx + self->height > self->nr_entries - 1) + break; + + offset = self->height; + if (self->index + offset > self->nr_entries - 1) + offset = self->nr_entries - 1 - self->index; + self->index += offset; + self->first_visible_entry_idx += offset; + + while (offset--) { + struct list_head *pos = self->first_visible_entry; + self->first_visible_entry = pos->next; + } + + break; + case NEWT_KEY_PGUP: + if (self->first_visible_entry_idx == 0) + break; + + if (self->first_visible_entry_idx < self->height) + offset = self->first_visible_entry_idx; + else + offset = self->height; + + self->index -= offset; + self->first_visible_entry_idx -= offset; + + while (offset--) { + struct list_head *pos = self->first_visible_entry; + self->first_visible_entry = pos->prev; + } + break; + case NEWT_KEY_HOME: + ui_browser__reset_index(self); + break; + case NEWT_KEY_END: { + struct list_head *head = self->entries; + offset = self->height - 1; + + if (offset > self->nr_entries) + offset = self->nr_entries; + + self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset; + self->first_visible_entry = head->prev; + while (offset-- != 0) { + struct list_head *pos = self->first_visible_entry; + self->first_visible_entry = pos->prev; + } + } + break; + case NEWT_KEY_ESCAPE: + case NEWT_KEY_LEFT: + case CTRL('c'): + case 'Q': + case 'q': + return 0; + default: + continue; + } + if (ui_browser__refresh_entries(self) < 0) + return -1; + } + return 0; +} + /* * When debugging newt problems it was useful to be able to "unroll" * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate @@ -317,62 +658,40 @@ static size_t hist_entry__append_browser(struct hist_entry *self, return ret; } -static void map_symbol__annotate_browser(const struct map_symbol *self, - const char *input_name) +static void hist_entry__annotate_browser(struct hist_entry *self) { - FILE *fp; - int cols, rows; - newtComponent form, tree; + struct ui_browser browser; struct newtExitStruct es; - char *str; - size_t line_len, max_line_len = 0; - size_t max_usable_width; - char *line = NULL; + struct objdump_line *pos, *n; + LIST_HEAD(head); - if (self->sym == NULL) + if (self->ms.sym == NULL) return; - if (asprintf(&str, "perf annotate -i \"%s\" -d \"%s\" %s 2>&1 | expand", - input_name, self->map->dso->name, self->sym->name) < 0) + if (hist_entry__annotate(self, &head) < 0) return; - fp = popen(str, "r"); - if (fp == NULL) - goto out_free_str; - - newtPushHelpLine("Press ESC to exit"); - newtGetScreenSize(&cols, &rows); - tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); - - while (!feof(fp)) { - if (getline(&line, &line_len, fp) < 0 || !line_len) - break; - while (line_len != 0 && isspace(line[line_len - 1])) - line[--line_len] = '\0'; + ui_helpline__push("Press <- or ESC to exit"); - if (line_len > max_line_len) - max_line_len = line_len; - newtListboxAppendEntry(tree, line, NULL); + memset(&browser, 0, sizeof(browser)); + browser.entries = &head; + browser.priv = self; + list_for_each_entry(pos, &head, node) { + size_t line_len = strlen(pos->line); + if (browser.width < line_len) + browser.width = line_len; + ++browser.nr_entries; } - fclose(fp); - free(line); - - max_usable_width = cols - 22; - if (max_line_len > max_usable_width) - max_line_len = max_usable_width; - newtListboxSetWidth(tree, max_line_len); - - newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); - form = newt_form__new(); - newtFormAddComponents(form, tree, NULL); - - newtFormRun(form, &es); - newtFormDestroy(form); + browser.width += 18; /* Percentage */ + ui_browser__run(&browser, self->ms.sym->name, &es); + newtFormDestroy(browser.form); newtPopWindow(); - newtPopHelpLine(); -out_free_str: - free(str); + list_for_each_entry_safe(pos, n, &head, node) { + list_del(&pos->node); + objdump_line__free(pos); + } + ui_helpline__pop(); } static const void *newt__symbol_tree_get_current(newtComponent self) @@ -410,23 +729,25 @@ static void hist_browser__delete(struct hist_browser *self) free(self); } -static int hist_browser__populate(struct hist_browser *self, struct rb_root *hists, - u64 nr_hists, u64 session_total, const char *title) +static int hist_browser__populate(struct hist_browser *self, struct hists *hists, + const char *title) { int max_len = 0, idx, cols, rows; struct ui_progress *progress; struct rb_node *nd; u64 curr_hist = 0; - char seq[] = "."; + char seq[] = ".", unit; char str[256]; + unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; if (self->form) { newtFormDestroy(self->form); newtPopWindow(); } - snprintf(str, sizeof(str), "Samples: %Ld ", - session_total); + nr_events = convert_unit(nr_events, &unit); + snprintf(str, sizeof(str), "Events: %lu%c ", + nr_events, unit); newtDrawRootText(0, 0, str); newtGetScreenSize(NULL, &rows); @@ -442,24 +763,25 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his newtComponentAddCallback(self->tree, hist_browser__selection, &self->selection); - progress = ui_progress__new("Adding entries to the browser...", nr_hists); + progress = ui_progress__new("Adding entries to the browser...", + hists->nr_entries); if (progress == NULL) return -1; idx = 0; - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { + for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); int len; if (h->filtered) continue; - len = hist_entry__append_browser(h, self->tree, session_total); + len = hist_entry__append_browser(h, self->tree, hists->stats.total_period); if (len > max_len) max_len = len; if (symbol_conf.use_callchain) hist_entry__append_callchain_browser(h, self->tree, - session_total, idx++); + hists->stats.total_period, idx++); ++curr_hist; if (curr_hist % 5) ui_progress__update(progress, curr_hist); @@ -483,6 +805,14 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his newtFormAddHotKey(self->form, 'A'); newtFormAddHotKey(self->form, 'a'); + newtFormAddHotKey(self->form, 'D'); + newtFormAddHotKey(self->form, 'd'); + newtFormAddHotKey(self->form, 'T'); + newtFormAddHotKey(self->form, 't'); + newtFormAddHotKey(self->form, '?'); + newtFormAddHotKey(self->form, 'H'); + newtFormAddHotKey(self->form, 'h'); + newtFormAddHotKey(self->form, NEWT_KEY_F1); newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); newtFormAddComponents(self->form, self->tree, NULL); self->selection = newt__symbol_tree_get_current(self->tree); @@ -490,58 +820,7 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his return 0; } -enum hist_filter { - HIST_FILTER__DSO, - HIST_FILTER__THREAD, -}; - -static u64 hists__filter_by_dso(struct rb_root *hists, const struct dso *dso, - u64 *session_total) -{ - struct rb_node *nd; - u64 nr_hists = 0; - - *session_total = 0; - - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - - if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { - h->filtered |= (1 << HIST_FILTER__DSO); - continue; - } - h->filtered &= ~(1 << HIST_FILTER__DSO); - ++nr_hists; - *session_total += h->count; - } - - return nr_hists; -} - -static u64 hists__filter_by_thread(struct rb_root *hists, const struct thread *thread, - u64 *session_total) -{ - struct rb_node *nd; - u64 nr_hists = 0; - - *session_total = 0; - - for (nd = rb_first(hists); nd; nd = rb_next(nd)) { - struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - - if (thread != NULL && h->thread != thread) { - h->filtered |= (1 << HIST_FILTER__THREAD); - continue; - } - h->filtered &= ~(1 << HIST_FILTER__THREAD); - ++nr_hists; - *session_total += h->count; - } - - return nr_hists; -} - -static struct thread *hist_browser__selected_thread(struct hist_browser *self) +static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) { int *indexes; @@ -557,7 +836,13 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self) } return NULL; out: - return *(struct thread **)(self->selection + 1); + return container_of(self->selection, struct hist_entry, ms); +} + +static struct thread *hist_browser__selected_thread(struct hist_browser *self) +{ + struct hist_entry *he = hist_browser__selected_entry(self); + return he ? he->thread : NULL; } static int hist_browser__title(char *bf, size_t size, const char *input_name, @@ -577,11 +862,10 @@ static int hist_browser__title(char *bf, size_t size, const char *input_name, return printed ?: snprintf(bf, size, "Report: %s", input_name); } -int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline, - const char *input_name) +int hists__browse(struct hists *self, const char *helpline, const char *input_name) { struct hist_browser *browser = hist_browser__new(); + struct pstack *fstack = pstack__new(2); const struct thread *thread_filter = NULL; const struct dso *dso_filter = NULL; struct newtExitStruct es; @@ -591,12 +875,16 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, if (browser == NULL) return -1; - newtPushHelpLine(helpline); + fstack = pstack__new(2); + if (fstack == NULL) + goto out; + + ui_helpline__push(helpline); hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) - goto out; + if (hist_browser__populate(browser, self, msg) < 0) + goto out_free_stack; while (1) { const struct thread *thread; @@ -606,17 +894,56 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, annotate = -2, zoom_dso = -2, zoom_thread = -2; newtFormRun(browser->form, &es); + + thread = hist_browser__selected_thread(browser); + dso = browser->selection->map ? browser->selection->map->dso : NULL; + if (es.reason == NEWT_EXIT_HOTKEY) { - if (toupper(es.u.key) == 'A') + if (es.u.key == NEWT_KEY_F1) + goto do_help; + + switch (toupper(es.u.key)) { + case 'A': goto do_annotate; - if (es.u.key == NEWT_KEY_ESCAPE || - toupper(es.u.key) == 'Q' || - es.u.key == CTRL('c')) { + case 'D': + goto zoom_dso; + case 'T': + goto zoom_thread; + case 'H': + case '?': +do_help: + ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" + "<- Zoom out\n" + "a Annotate current symbol\n" + "h/?/F1 Show this window\n" + "d Zoom into current DSO\n" + "t Zoom into current Thread\n" + "q/CTRL+C Exit browser"); + continue; + default:; + } + if (toupper(es.u.key) == 'Q' || + es.u.key == CTRL('c')) + break; + if (es.u.key == NEWT_KEY_ESCAPE) { if (dialog_yesno("Do you really want to exit?")) break; else continue; } + + if (es.u.key == NEWT_KEY_LEFT) { + const void *top; + + if (pstack__empty(fstack)) + continue; + top = pstack__pop(fstack); + if (top == &dso_filter) + goto zoom_out_dso; + if (top == &thread_filter) + goto zoom_out_thread; + continue; + } } if (browser->selection->sym != NULL && @@ -624,7 +951,6 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, browser->selection->sym->name) > 0) annotate = nr_options++; - thread = hist_browser__selected_thread(browser); if (thread != NULL && asprintf(&options[nr_options], "Zoom %s %s(%d) thread", (thread_filter ? "out of" : "into"), @@ -632,7 +958,6 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, thread->pid) > 0) zoom_thread = nr_options++; - dso = browser->selection->map ? browser->selection->map->dso : NULL; if (dso != NULL && asprintf(&options[nr_options], "Zoom %s %s DSO", (dso_filter ? "out of" : "into"), @@ -651,66 +976,100 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, if (choice == -1) continue; -do_annotate: + if (choice == annotate) { + struct hist_entry *he; +do_annotate: if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { - newtPopHelpLine(); - newtPushHelpLine("No vmlinux file found, can't " + ui_helpline__puts("No vmlinux file found, can't " "annotate with just a " "kallsyms file"); continue; } - map_symbol__annotate_browser(browser->selection, input_name); + + he = hist_browser__selected_entry(browser); + if (he == NULL) + continue; + + hist_entry__annotate_browser(he); } else if (choice == zoom_dso) { +zoom_dso: if (dso_filter) { - newtPopHelpLine(); + pstack__remove(fstack, &dso_filter); +zoom_out_dso: + ui_helpline__pop(); dso_filter = NULL; } else { - snprintf(msg, sizeof(msg), - "To zoom out press -> + \"Zoom out of %s DSO\"", - dso->kernel ? "the Kernel" : dso->short_name); - newtPushHelpLine(msg); + 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); } - nr_hists = hists__filter_by_dso(hists, dso_filter, &session_total); + hists__filter_by_dso(self, dso_filter); hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) + if (hist_browser__populate(browser, self, msg) < 0) goto out; } else if (choice == zoom_thread) { +zoom_thread: if (thread_filter) { - newtPopHelpLine(); + pstack__remove(fstack, &thread_filter); +zoom_out_thread: + ui_helpline__pop(); thread_filter = NULL; } else { - snprintf(msg, sizeof(msg), - "To zoom out press -> + \"Zoom out of %s(%d) thread\"", - (thread->comm_set ? thread->comm : ""), - thread->pid); - newtPushHelpLine(msg); + ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", + thread->comm_set ? thread->comm : "", + thread->pid); thread_filter = thread; + pstack__push(fstack, &thread_filter); } - nr_hists = hists__filter_by_thread(hists, thread_filter, &session_total); + hists__filter_by_thread(self, thread_filter); hist_browser__title(msg, sizeof(msg), input_name, dso_filter, thread_filter); - if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0) + if (hist_browser__populate(browser, self, msg) < 0) goto out; } } err = 0; +out_free_stack: + pstack__delete(fstack); out: hist_browser__delete(browser); return err; } +static struct newtPercentTreeColors { + const char *topColorFg, *topColorBg; + const char *mediumColorFg, *mediumColorBg; + const char *normalColorFg, *normalColorBg; + const char *selColorFg, *selColorBg; + const char *codeColorFg, *codeColorBg; +} defaultPercentTreeColors = { + "red", "lightgray", + "green", "lightgray", + "black", "lightgray", + "lightgray", "magenta", + "blue", "lightgray", +}; + void setup_browser(void) { + struct newtPercentTreeColors *c = &defaultPercentTreeColors; if (!isatty(1)) return; use_browser = true; newtInit(); newtCls(); - newtPushHelpLine(" "); + ui_helpline__puts(" "); + sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); + sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); + sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); + sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); + sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); } void exit_browser(bool wait_for_ok) diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 3b4ec679756..9bf0f402ca7 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -418,12 +418,6 @@ parse_single_tracepoint_event(char *sys_name, u64 id; int fd; - attr->sample_type |= PERF_SAMPLE_RAW; - attr->sample_type |= PERF_SAMPLE_TIME; - attr->sample_type |= PERF_SAMPLE_CPU; - - attr->sample_period = 1; - snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, sys_name, evt_name); @@ -442,6 +436,13 @@ parse_single_tracepoint_event(char *sys_name, attr->type = PERF_TYPE_TRACEPOINT; *strp = evt_name + evt_length; + attr->sample_type |= PERF_SAMPLE_RAW; + attr->sample_type |= PERF_SAMPLE_TIME; + attr->sample_type |= PERF_SAMPLE_CPU; + + attr->sample_period = 1; + + return EVT_HANDLED; } @@ -653,10 +654,6 @@ parse_raw_event(const char **strp, struct perf_event_attr *attr) return EVT_FAILED; n = hex2u64(str + 1, &config); if (n > 0) { - if (str[n+1] == 'p') { - attr->precise = 1; - n++; - } *strp = str + n + 1; attr->type = PERF_TYPE_RAW; attr->config = config; @@ -691,19 +688,29 @@ static enum event_result parse_event_modifier(const char **strp, struct perf_event_attr *attr) { const char *str = *strp; - int eu = 1, ek = 1, eh = 1; + int exclude = 0; + int eu = 0, ek = 0, eh = 0, precise = 0; if (*str++ != ':') return 0; while (*str) { - if (*str == 'u') + if (*str == 'u') { + if (!exclude) + exclude = eu = ek = eh = 1; eu = 0; - else if (*str == 'k') + } else if (*str == 'k') { + if (!exclude) + exclude = eu = ek = eh = 1; ek = 0; - else if (*str == 'h') + } else if (*str == 'h') { + if (!exclude) + exclude = eu = ek = eh = 1; eh = 0; - else + } else if (*str == 'p') { + precise++; + } else break; + ++str; } if (str >= *strp + 2) { @@ -711,6 +718,7 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) attr->exclude_user = eu; attr->exclude_kernel = ek; attr->exclude_hv = eh; + attr->precise_ip = precise; return 1; } return 0; @@ -935,7 +943,8 @@ void print_events(void) printf("\n"); printf(" %-42s [%s]\n", - "rNNN", event_type_descriptors[PERF_TYPE_RAW]); + "rNNN (see 'perf list --help' on how to encode it)", + event_type_descriptors[PERF_TYPE_RAW]); printf("\n"); printf(" %-42s [%s]\n", diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index b8c1f64bc93..fc4ab3fe877 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -13,6 +13,7 @@ struct tracepoint_path { }; extern struct tracepoint_path *tracepoint_id_to_path(u64 config); +extern bool have_tracepoints(struct perf_event_attr *pattrs, int nb_events); extern int nr_counters; diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index ed887642460..99d02aa57db 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -51,7 +51,7 @@ static int get_value(struct parse_opt_ctx_t *p, case OPTION_BOOLEAN: case OPTION_INCR: case OPTION_BIT: - case OPTION_SET_INT: + case OPTION_SET_UINT: case OPTION_SET_PTR: return opterror(opt, "takes no value", flags); case OPTION_END: @@ -59,7 +59,9 @@ static int get_value(struct parse_opt_ctx_t *p, case OPTION_GROUP: case OPTION_STRING: case OPTION_INTEGER: + case OPTION_UINTEGER: case OPTION_LONG: + case OPTION_U64: default: break; } @@ -81,8 +83,8 @@ static int get_value(struct parse_opt_ctx_t *p, *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; return 0; - case OPTION_SET_INT: - *(int *)opt->value = unset ? 0 : opt->defval; + case OPTION_SET_UINT: + *(unsigned int *)opt->value = unset ? 0 : opt->defval; return 0; case OPTION_SET_PTR: @@ -125,6 +127,22 @@ static int get_value(struct parse_opt_ctx_t *p, return opterror(opt, "expects a numerical value", flags); return 0; + case OPTION_UINTEGER: + if (unset) { + *(unsigned int *)opt->value = 0; + return 0; + } + if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + *(unsigned int *)opt->value = opt->defval; + return 0; + } + if (get_arg(p, opt, flags, &arg)) + return -1; + *(unsigned int *)opt->value = strtol(arg, (char **)&s, 10); + if (*s) + return opterror(opt, "expects a numerical value", flags); + return 0; + case OPTION_LONG: if (unset) { *(long *)opt->value = 0; @@ -141,6 +159,22 @@ static int get_value(struct parse_opt_ctx_t *p, return opterror(opt, "expects a numerical value", flags); return 0; + case OPTION_U64: + if (unset) { + *(u64 *)opt->value = 0; + return 0; + } + if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + *(u64 *)opt->value = opt->defval; + return 0; + } + if (get_arg(p, opt, flags, &arg)) + return -1; + *(u64 *)opt->value = strtoull(arg, (char **)&s, 10); + if (*s) + return opterror(opt, "expects a numerical value", flags); + return 0; + case OPTION_END: case OPTION_ARGUMENT: case OPTION_GROUP: @@ -446,7 +480,10 @@ int usage_with_options_internal(const char * const *usagestr, switch (opts->type) { case OPTION_ARGUMENT: break; + case OPTION_LONG: + case OPTION_U64: case OPTION_INTEGER: + case OPTION_UINTEGER: if (opts->flags & PARSE_OPT_OPTARG) if (opts->long_name) pos += fprintf(stderr, "[=<n>]"); @@ -478,15 +515,14 @@ int usage_with_options_internal(const char * const *usagestr, pos += fprintf(stderr, " ..."); } break; - default: /* OPTION_{BIT,BOOLEAN,SET_INT,SET_PTR} */ + default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ case OPTION_END: case OPTION_GROUP: case OPTION_BIT: case OPTION_BOOLEAN: case OPTION_INCR: - case OPTION_SET_INT: + case OPTION_SET_UINT: case OPTION_SET_PTR: - case OPTION_LONG: break; } diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index b2da725f102..c7d72dce54b 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -1,6 +1,9 @@ #ifndef __PERF_PARSE_OPTIONS_H #define __PERF_PARSE_OPTIONS_H +#include <linux/kernel.h> +#include <stdbool.h> + enum parse_opt_type { /* special types */ OPTION_END, @@ -10,13 +13,15 @@ enum parse_opt_type { OPTION_BIT, OPTION_BOOLEAN, OPTION_INCR, - OPTION_SET_INT, + OPTION_SET_UINT, OPTION_SET_PTR, /* options with arguments (usually) */ OPTION_STRING, OPTION_INTEGER, OPTION_LONG, OPTION_CALLBACK, + OPTION_U64, + OPTION_UINTEGER, }; enum parse_opt_flags { @@ -74,7 +79,7 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset); * * `defval`:: * default value to fill (*->value) with for PARSE_OPT_OPTARG. - * OPTION_{BIT,SET_INT,SET_PTR} store the {mask,integer,pointer} to put in + * OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in * the value when met. * CALLBACKS can use it like they want. */ @@ -91,17 +96,21 @@ struct option { intptr_t defval; }; +#define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v ) + #define OPT_END() { .type = OPTION_END } #define OPT_ARGUMENT(l, h) { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) } #define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) } -#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (b) } -#define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } -#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } -#define OPT_SET_INT(s, l, v, h, i) { .type = OPTION_SET_INT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (i) } +#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) } +#define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) } +#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } +#define OPT_SET_UINT(s, l, v, h, i) { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) } #define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } -#define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } -#define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = (v), .help = (h) } -#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h) } +#define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } +#define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h) } +#define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) } +#define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) } +#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) } #define OPT_DATE(s, l, v, h) \ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb } #define OPT_CALLBACK(s, l, v, a, h, f) \ diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 4fb480367c3..914c67095d9 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -72,8 +72,7 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) } static char *synthesize_perf_probe_point(struct perf_probe_point *pp); -static struct map_groups kmap_groups; -static struct map *kmaps[MAP__NR_TYPES]; +static struct machine machine; /* Initialize symbol maps and path of vmlinux */ static int init_vmlinux(void) @@ -92,12 +91,15 @@ static int init_vmlinux(void) goto out; } + ret = machine__init(&machine, "/", 0); + if (ret < 0) + goto out; + kernel = dso__new_kernel(symbol_conf.vmlinux_name); if (kernel == NULL) die("Failed to create kernel dso."); - map_groups__init(&kmap_groups); - ret = __map_groups__create_kernel_maps(&kmap_groups, kmaps, kernel); + ret = __machine__create_kernel_maps(&machine, kernel); if (ret < 0) pr_debug("Failed to create kernel maps.\n"); @@ -110,12 +112,12 @@ out: #ifdef DWARF_SUPPORT static int open_vmlinux(void) { - if (map__load(kmaps[MAP__FUNCTION], NULL) < 0) { + if (map__load(machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) { pr_debug("Failed to load kernel map.\n"); return -EINVAL; } - pr_debug("Try to open %s\n", kmaps[MAP__FUNCTION]->dso->long_name); - return open(kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY); + pr_debug("Try to open %s\n", machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name); + return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY); } /* Convert trace point to probe point with debuginfo */ @@ -125,7 +127,7 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, struct symbol *sym; int fd, ret = -ENOENT; - sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], + sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], tp->symbol, NULL); if (sym) { fd = open_vmlinux(); @@ -150,7 +152,8 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, /* Try to find perf_probe_event with debuginfo */ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, - struct kprobe_trace_event **tevs) + struct kprobe_trace_event **tevs, + int max_tevs) { bool need_dwarf = perf_probe_event_need_dwarf(pev); int fd, ntevs; @@ -166,7 +169,7 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, } /* Searching trace events corresponding to probe event */ - ntevs = find_kprobe_trace_events(fd, pev, tevs); + ntevs = find_kprobe_trace_events(fd, pev, tevs, max_tevs); close(fd); if (ntevs > 0) { /* Succeeded to find trace events */ @@ -180,15 +183,16 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, return -ENOENT; } /* Error path : ntevs < 0 */ - if (need_dwarf) { - if (ntevs == -EBADF) - pr_warning("No dwarf info found in the vmlinux - " - "please rebuild with CONFIG_DEBUG_INFO=y.\n"); - return ntevs; + pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs); + if (ntevs == -EBADF) { + pr_warning("Warning: No dwarf info found in the vmlinux - " + "please rebuild kernel with CONFIG_DEBUG_INFO=y.\n"); + if (!need_dwarf) { + pr_debug("Trying to use symbols.\nn"); + return 0; + } } - pr_debug("An error occurred in debuginfo analysis." - " Try to use symbols.\n"); - return 0; + return ntevs; } #define LINEBUF_SIZE 256 @@ -317,7 +321,8 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, } static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, - struct kprobe_trace_event **tevs __unused) + struct kprobe_trace_event **tevs __unused, + int max_tevs __unused) { if (perf_probe_event_need_dwarf(pev)) { pr_warning("Debuginfo-analysis is not supported.\n"); @@ -1407,14 +1412,15 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev, } static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, - struct kprobe_trace_event **tevs) + struct kprobe_trace_event **tevs, + int max_tevs) { struct symbol *sym; int ret = 0, i; struct kprobe_trace_event *tev; /* Convert perf_probe_event with debuginfo */ - ret = try_to_find_kprobe_trace_events(pev, tevs); + ret = try_to_find_kprobe_trace_events(pev, tevs, max_tevs); if (ret != 0) return ret; @@ -1462,7 +1468,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, } /* Currently just checking function name from symbol map */ - sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], + sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], tev->point.symbol, NULL); if (!sym) { pr_warning("Kernel symbol \'%s\' not found.\n", @@ -1486,7 +1492,7 @@ struct __event_package { }; int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, - bool force_add) + bool force_add, int max_tevs) { int i, j, ret; struct __event_package *pkgs; @@ -1505,7 +1511,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, pkgs[i].pev = &pevs[i]; /* Convert with or without debuginfo */ ret = convert_to_kprobe_trace_events(pkgs[i].pev, - &pkgs[i].tevs); + &pkgs[i].tevs, max_tevs); if (ret < 0) goto end; pkgs[i].ntevs = ret; diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index e7ff0d02c0d..e9db1a214ca 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -115,8 +115,8 @@ extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev); extern int parse_line_range_desc(const char *cmd, struct line_range *lr); -extern int add_perf_probe_events(struct perf_probe_event *pevs, int ntevs, - bool force_add); +extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, + bool force_add, int max_probe_points); extern int del_perf_probe_events(struct strlist *dellist); extern int show_perf_probe_events(void); extern int show_line_range(struct line_range *lr); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 3e7977560be..562b1443e78 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -31,6 +31,7 @@ #include <string.h> #include <stdarg.h> #include <ctype.h> +#include <dwarf-regs.h> #include "string.h" #include "event.h" @@ -38,61 +39,9 @@ #include "util.h" #include "probe-finder.h" - -/* - * Generic dwarf analysis helpers - */ - -#define X86_32_MAX_REGS 8 -const char *x86_32_regs_table[X86_32_MAX_REGS] = { - "%ax", - "%cx", - "%dx", - "%bx", - "$stack", /* Stack address instead of %sp */ - "%bp", - "%si", - "%di", -}; - -#define X86_64_MAX_REGS 16 -const char *x86_64_regs_table[X86_64_MAX_REGS] = { - "%ax", - "%dx", - "%cx", - "%bx", - "%si", - "%di", - "%bp", - "%sp", - "%r8", - "%r9", - "%r10", - "%r11", - "%r12", - "%r13", - "%r14", - "%r15", -}; - -/* TODO: switching by dwarf address size */ -#ifdef __x86_64__ -#define ARCH_MAX_REGS X86_64_MAX_REGS -#define arch_regs_table x86_64_regs_table -#else -#define ARCH_MAX_REGS X86_32_MAX_REGS -#define arch_regs_table x86_32_regs_table -#endif - /* Kprobe tracer basic type is up to u64 */ #define MAX_BASIC_TYPE_BITS 64 -/* Return architecture dependent register string (for kprobe-tracer) */ -static const char *get_arch_regstr(unsigned int n) -{ - return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL; -} - /* * Compare the tail of two strings. * Return 0 if whole of either string is same as another's tail part. @@ -447,7 +396,7 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf) regs = get_arch_regstr(regn); if (!regs) { - pr_warning("%u exceeds max register number.\n", regn); + pr_warning("Mapping for DWARF register number %u missing on this architecture.", regn); return -ERANGE; } @@ -677,8 +626,9 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) Dwarf_Attribute fb_attr; size_t nops; - if (pf->ntevs == MAX_PROBES) { - pr_warning("Too many( > %d) probe point found.\n", MAX_PROBES); + if (pf->ntevs == pf->max_tevs) { + pr_warning("Too many( > %d) probe point found.\n", + pf->max_tevs); return -ERANGE; } tev = &pf->tevs[pf->ntevs++]; @@ -922,6 +872,8 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) (uintmax_t)pf->addr); param->retval = convert_probe_point(in_die, pf); + if (param->retval < 0) + return DWARF_CB_ABORT; } return DWARF_CB_OK; @@ -981,9 +933,9 @@ static int find_probe_point_by_func(struct probe_finder *pf) /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, - struct kprobe_trace_event **tevs) + struct kprobe_trace_event **tevs, int max_tevs) { - struct probe_finder pf = {.pev = pev}; + struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; struct perf_probe_point *pp = &pev->point; Dwarf_Off off, noff; size_t cuhl; @@ -991,7 +943,7 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, Dwarf *dbg; int ret = 0; - pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES); + pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * max_tevs); if (pf.tevs == NULL) return -ENOMEM; *tevs = pf.tevs; @@ -1157,6 +1109,8 @@ static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data) return DWARF_CB_OK; param->retval = line_range_add_line(src, lineno, lf->lr); + if (param->retval < 0) + return DWARF_CB_ABORT; return DWARF_CB_OK; } diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 310ce897229..66f1980e385 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -18,7 +18,8 @@ static inline int is_c_varname(const char *name) #ifdef DWARF_SUPPORT /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, - struct kprobe_trace_event **tevs); + struct kprobe_trace_event **tevs, + int max_tevs); /* Find a perf_probe_point from debuginfo */ extern int find_perf_probe_point(int fd, unsigned long addr, @@ -32,7 +33,8 @@ extern int find_line_range(int fd, struct line_range *lr); struct probe_finder { struct perf_probe_event *pev; /* Target probe event */ struct kprobe_trace_event *tevs; /* Result trace events */ - int ntevs; /* number of trace events */ + int ntevs; /* Number of trace events */ + int max_tevs; /* Max number of trace events */ /* For function searching */ int lno; /* Line number */ diff --git a/tools/perf/util/pstack.c b/tools/perf/util/pstack.c new file mode 100644 index 00000000000..13d36faf64e --- /dev/null +++ b/tools/perf/util/pstack.c @@ -0,0 +1,75 @@ +/* + * Simple pointer stack + * + * (c) 2010 Arnaldo Carvalho de Melo <acme@redhat.com> + */ + +#include "util.h" +#include "pstack.h" +#include <linux/kernel.h> +#include <stdlib.h> + +struct pstack { + unsigned short top; + unsigned short max_nr_entries; + void *entries[0]; +}; + +struct pstack *pstack__new(unsigned short max_nr_entries) +{ + struct pstack *self = zalloc((sizeof(*self) + + max_nr_entries * sizeof(void *))); + if (self != NULL) + self->max_nr_entries = max_nr_entries; + return self; +} + +void pstack__delete(struct pstack *self) +{ + free(self); +} + +bool pstack__empty(const struct pstack *self) +{ + return self->top == 0; +} + +void pstack__remove(struct pstack *self, void *key) +{ + unsigned short i = self->top, last_index = self->top - 1; + + while (i-- != 0) { + if (self->entries[i] == key) { + if (i < last_index) + memmove(self->entries + i, + self->entries + i + 1, + (last_index - i) * sizeof(void *)); + --self->top; + return; + } + } + pr_err("%s: %p not on the pstack!\n", __func__, key); +} + +void pstack__push(struct pstack *self, void *key) +{ + if (self->top == self->max_nr_entries) { + pr_err("%s: top=%d, overflow!\n", __func__, self->top); + return; + } + self->entries[self->top++] = key; +} + +void *pstack__pop(struct pstack *self) +{ + void *ret; + + if (self->top == 0) { + pr_err("%s: underflow!\n", __func__); + return NULL; + } + + ret = self->entries[--self->top]; + self->entries[self->top] = NULL; + return ret; +} diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h new file mode 100644 index 00000000000..5ad07023504 --- /dev/null +++ b/tools/perf/util/pstack.h @@ -0,0 +1,12 @@ +#ifndef _PERF_PSTACK_ +#define _PERF_PSTACK_ + +struct pstack; +struct pstack *pstack__new(unsigned short max_nr_entries); +void pstack__delete(struct pstack *self); +bool pstack__empty(const struct pstack *self); +void pstack__remove(struct pstack *self, void *key); +void pstack__push(struct pstack *self, void *key); +void *pstack__pop(struct pstack *self); + +#endif /* _PERF_PSTACK_ */ diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 5376378e0cf..b059dc50cc2 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -371,7 +371,6 @@ static int perl_start_script(const char *script, int argc, const char **argv) run_start_sub(); free(command_line); - fprintf(stderr, "perf trace started with Perl script %s\n\n", script); return 0; error: perl_free(my_perl); @@ -394,8 +393,6 @@ static int perl_stop_script(void) perl_destruct(my_perl); perl_free(my_perl); - fprintf(stderr, "\nperf trace Perl script stopped\n"); - return 0; } diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 6a72f14c598..81f39cab3aa 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -374,8 +374,6 @@ static int python_start_script(const char *script, int argc, const char **argv) } free(command_line); - fprintf(stderr, "perf trace started with Python script %s\n\n", - script); return err; error: @@ -407,8 +405,6 @@ out: Py_XDECREF(main_module); Py_Finalize(); - fprintf(stderr, "\nperf trace Python script stopped\n"); - return err; } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 7d88ae5c270..25bfca4f10f 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -69,16 +69,14 @@ void perf_session__update_sample_type(struct perf_session *self) int perf_session__create_kernel_maps(struct perf_session *self) { - int ret; - struct rb_root *root = &self->kerninfo_root; + int ret = machine__create_kernel_maps(&self->host_machine); - ret = map_groups__create_kernel_maps(root, HOST_KERNEL_ID); if (ret >= 0) - ret = map_groups__create_guest_kernel_maps(root); + ret = machines__create_guest_kernel_maps(&self->machines); return ret; } -struct perf_session *perf_session__new(const char *filename, int mode, bool force) +struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe) { size_t len = filename ? strlen(filename) + 1 : 0; struct perf_session *self = zalloc(sizeof(*self) + len); @@ -91,13 +89,15 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc memcpy(self->filename, filename, len); self->threads = RB_ROOT; - self->stats_by_id = RB_ROOT; + self->hists_tree = RB_ROOT; self->last_match = NULL; self->mmap_window = 32; self->cwd = NULL; self->cwdlen = 0; - self->unknown_events = 0; - self->kerninfo_root = RB_ROOT; + self->machines = RB_ROOT; + self->repipe = repipe; + INIT_LIST_HEAD(&self->ordered_samples.samples_head); + machine__init(&self->host_machine, "", HOST_KERNEL_ID); if (mode == O_RDONLY) { if (perf_session__open(self, force) < 0) @@ -192,6 +192,18 @@ static int process_event_stub(event_t *event __used, return 0; } +static int process_finished_round_stub(event_t *event __used, + struct perf_session *session __used, + struct perf_event_ops *ops __used) +{ + dump_printf(": unhandled!\n"); + return 0; +} + +static int process_finished_round(event_t *event, + struct perf_session *session, + struct perf_event_ops *ops); + static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) { if (handler->sample == NULL) @@ -220,35 +232,11 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) handler->tracing_data = process_event_stub; if (handler->build_id == NULL) handler->build_id = process_event_stub; -} - -static const char *event__name[] = { - [0] = "TOTAL", - [PERF_RECORD_MMAP] = "MMAP", - [PERF_RECORD_LOST] = "LOST", - [PERF_RECORD_COMM] = "COMM", - [PERF_RECORD_EXIT] = "EXIT", - [PERF_RECORD_THROTTLE] = "THROTTLE", - [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", - [PERF_RECORD_FORK] = "FORK", - [PERF_RECORD_READ] = "READ", - [PERF_RECORD_SAMPLE] = "SAMPLE", - [PERF_RECORD_HEADER_ATTR] = "ATTR", - [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", - [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", - [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", -}; - -unsigned long event__total[PERF_RECORD_HEADER_MAX]; - -void event__print_totals(void) -{ - int i; - for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { - if (!event__name[i]) - continue; - pr_info("%10s events: %10ld\n", - event__name[i], event__total[i]); + if (handler->finished_round == NULL) { + if (handler->ordered_samples) + handler->finished_round = process_finished_round; + else + handler->finished_round = process_finished_round_stub; } } @@ -351,6 +339,205 @@ static event__swap_op event__swap_ops[] = { [PERF_RECORD_HEADER_MAX] = NULL, }; +struct sample_queue { + u64 timestamp; + struct sample_event *event; + struct list_head list; +}; + +static void flush_sample_queue(struct perf_session *s, + struct perf_event_ops *ops) +{ + struct list_head *head = &s->ordered_samples.samples_head; + u64 limit = s->ordered_samples.next_flush; + struct sample_queue *tmp, *iter; + + if (!ops->ordered_samples || !limit) + return; + + list_for_each_entry_safe(iter, tmp, head, list) { + if (iter->timestamp > limit) + return; + + if (iter == s->ordered_samples.last_inserted) + s->ordered_samples.last_inserted = NULL; + + ops->sample((event_t *)iter->event, s); + + s->ordered_samples.last_flush = iter->timestamp; + list_del(&iter->list); + free(iter->event); + free(iter); + } +} + +/* + * When perf record finishes a pass on every buffers, it records this pseudo + * event. + * We record the max timestamp t found in the pass n. + * Assuming these timestamps are monotonic across cpus, we know that if + * a buffer still has events with timestamps below t, they will be all + * available and then read in the pass n + 1. + * Hence when we start to read the pass n + 2, we can safely flush every + * events with timestamps below t. + * + * ============ PASS n ================= + * CPU 0 | CPU 1 + * | + * cnt1 timestamps | cnt2 timestamps + * 1 | 2 + * 2 | 3 + * - | 4 <--- max recorded + * + * ============ PASS n + 1 ============== + * CPU 0 | CPU 1 + * | + * cnt1 timestamps | cnt2 timestamps + * 3 | 5 + * 4 | 6 + * 5 | 7 <---- max recorded + * + * Flush every events below timestamp 4 + * + * ============ PASS n + 2 ============== + * CPU 0 | CPU 1 + * | + * cnt1 timestamps | cnt2 timestamps + * 6 | 8 + * 7 | 9 + * - | 10 + * + * Flush every events below timestamp 7 + * etc... + */ +static int process_finished_round(event_t *event __used, + struct perf_session *session, + struct perf_event_ops *ops) +{ + flush_sample_queue(session, ops); + session->ordered_samples.next_flush = session->ordered_samples.max_timestamp; + + return 0; +} + +static void __queue_sample_end(struct sample_queue *new, struct list_head *head) +{ + struct sample_queue *iter; + + list_for_each_entry_reverse(iter, head, list) { + if (iter->timestamp < new->timestamp) { + list_add(&new->list, &iter->list); + return; + } + } + + list_add(&new->list, head); +} + +static void __queue_sample_before(struct sample_queue *new, + struct sample_queue *iter, + struct list_head *head) +{ + list_for_each_entry_continue_reverse(iter, head, list) { + if (iter->timestamp < new->timestamp) { + list_add(&new->list, &iter->list); + return; + } + } + + list_add(&new->list, head); +} + +static void __queue_sample_after(struct sample_queue *new, + struct sample_queue *iter, + struct list_head *head) +{ + list_for_each_entry_continue(iter, head, list) { + if (iter->timestamp > new->timestamp) { + list_add_tail(&new->list, &iter->list); + return; + } + } + list_add_tail(&new->list, head); +} + +/* The queue is ordered by time */ +static void __queue_sample_event(struct sample_queue *new, + struct perf_session *s) +{ + struct sample_queue *last_inserted = s->ordered_samples.last_inserted; + struct list_head *head = &s->ordered_samples.samples_head; + + + if (!last_inserted) { + __queue_sample_end(new, head); + return; + } + + /* + * Most of the time the current event has a timestamp + * very close to the last event inserted, unless we just switched + * to another event buffer. Having a sorting based on a list and + * on the last inserted event that is close to the current one is + * probably more efficient than an rbtree based sorting. + */ + if (last_inserted->timestamp >= new->timestamp) + __queue_sample_before(new, last_inserted, head); + else + __queue_sample_after(new, last_inserted, head); +} + +static int queue_sample_event(event_t *event, struct sample_data *data, + struct perf_session *s) +{ + u64 timestamp = data->time; + struct sample_queue *new; + + + if (timestamp < s->ordered_samples.last_flush) { + printf("Warning: Timestamp below last timeslice flush\n"); + return -EINVAL; + } + + new = malloc(sizeof(*new)); + if (!new) + return -ENOMEM; + + new->timestamp = timestamp; + + new->event = malloc(event->header.size); + if (!new->event) { + free(new); + return -ENOMEM; + } + + memcpy(new->event, event, event->header.size); + + __queue_sample_event(new, s); + s->ordered_samples.last_inserted = new; + + if (new->timestamp > s->ordered_samples.max_timestamp) + s->ordered_samples.max_timestamp = new->timestamp; + + return 0; +} + +static int perf_session__process_sample(event_t *event, struct perf_session *s, + struct perf_event_ops *ops) +{ + struct sample_data data; + + if (!ops->ordered_samples) + return ops->sample(event, s); + + bzero(&data, sizeof(struct sample_data)); + event__parse_sample(event, s->sample_type, &data); + + queue_sample_event(event, &data, s); + + return 0; +} + static int perf_session__process_event(struct perf_session *self, event_t *event, struct perf_event_ops *ops, @@ -362,8 +549,7 @@ static int perf_session__process_event(struct perf_session *self, dump_printf("%#Lx [%#x]: PERF_RECORD_%s", offset + head, event->header.size, event__name[event->header.type]); - ++event__total[0]; - ++event__total[event->header.type]; + hists__inc_nr_events(&self->hists, event->header.type); } if (self->header.needs_swap && event__swap_ops[event->header.type]) @@ -371,7 +557,7 @@ static int perf_session__process_event(struct perf_session *self, switch (event->header.type) { case PERF_RECORD_SAMPLE: - return ops->sample(event, self); + return perf_session__process_sample(event, self, ops); case PERF_RECORD_MMAP: return ops->mmap(event, self); case PERF_RECORD_COMM: @@ -398,8 +584,10 @@ static int perf_session__process_event(struct perf_session *self, return ops->tracing_data(event, self); case PERF_RECORD_HEADER_BUILD_ID: return ops->build_id(event, self); + case PERF_RECORD_FINISHED_ROUND: + return ops->finished_round(event, self, ops); default: - self->unknown_events++; + ++self->hists.stats.nr_unknown_events; return -1; } } @@ -476,15 +664,18 @@ more: p = &event; p += sizeof(struct perf_event_header); - err = do_read(self->fd, p, size - sizeof(struct perf_event_header)); - if (err <= 0) { - if (err == 0) { - pr_err("unexpected end of event stream\n"); - goto done; - } + if (size - sizeof(struct perf_event_header)) { + err = do_read(self->fd, p, + size - sizeof(struct perf_event_header)); + if (err <= 0) { + if (err == 0) { + pr_err("unexpected end of event stream\n"); + goto done; + } - pr_err("failed to read event data\n"); - goto out_err; + pr_err("failed to read event data\n"); + goto out_err; + } } if (size == 0 || @@ -611,6 +802,9 @@ more: goto more; done: err = 0; + /* do the final flush for ordered samples */ + self->ordered_samples.next_flush = ULLONG_MAX; + flush_sample_queue(self, ops); out_err: ui_progress__delete(progress); return err; @@ -693,3 +887,10 @@ int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps, return 0; } + +size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) +{ + return __dsos__fprintf(&self->host_machine.kernel_dsos, fp) + + __dsos__fprintf(&self->host_machine.user_dsos, fp) + + machines__fprintf_dsos(&self->machines, fp); +} diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 5e47c87b926..e7fce486ebe 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -1,6 +1,7 @@ #ifndef __PERF_SESSION_H #define __PERF_SESSION_H +#include "hist.h" #include "event.h" #include "header.h" #include "symbol.h" @@ -8,48 +9,69 @@ #include <linux/rbtree.h> #include "../../../include/linux/perf_event.h" +struct sample_queue; struct ip_callchain; struct thread; +struct ordered_samples { + u64 last_flush; + u64 next_flush; + u64 max_timestamp; + struct list_head samples_head; + struct sample_queue *last_inserted; +}; + struct perf_session { struct perf_header header; unsigned long size; unsigned long mmap_window; struct rb_root threads; struct thread *last_match; - struct rb_root kerninfo_root; - struct events_stats events_stats; - struct rb_root stats_by_id; - unsigned long event_total[PERF_RECORD_MAX]; - unsigned long unknown_events; - struct rb_root hists; + struct machine host_machine; + struct rb_root machines; + struct rb_root hists_tree; + /* + * FIXME: should point to the first entry in hists_tree and + * be a hists instance. Right now its only 'report' + * that is using ->hists_tree while all the rest use + * ->hists. + */ + struct hists hists; u64 sample_type; int fd; bool fd_pipe; + bool repipe; int cwdlen; char *cwd; + struct ordered_samples ordered_samples; char filename[0]; }; +struct perf_event_ops; + typedef int (*event_op)(event_t *self, struct perf_session *session); +typedef int (*event_op2)(event_t *self, struct perf_session *session, + struct perf_event_ops *ops); struct perf_event_ops { - event_op sample, - mmap, - comm, - fork, - exit, - lost, - read, - throttle, - unthrottle, - attr, - event_type, - tracing_data, - build_id; + event_op sample, + mmap, + comm, + fork, + exit, + lost, + read, + throttle, + unthrottle, + attr, + event_type, + tracing_data, + build_id; + event_op2 finished_round; + bool ordered_samples; }; -struct perf_session *perf_session__new(const char *filename, int mode, bool force); +struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe); void perf_session__delete(struct perf_session *self); void perf_event_header__bswap(struct perf_event_header *self); @@ -78,18 +100,48 @@ int perf_session__create_kernel_maps(struct perf_session *self); int do_read(int fd, void *buf, size_t size); void perf_session__update_sample_type(struct perf_session *self); -#ifdef NO_NEWT_SUPPORT -static inline int perf_session__browse_hists(struct rb_root *hists __used, - u64 nr_hists __used, - u64 session_total __used, - const char *helpline __used, - const char *input_name __used) +static inline +struct machine *perf_session__find_host_machine(struct perf_session *self) +{ + return &self->host_machine; +} + +static inline +struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) +{ + if (pid == HOST_KERNEL_ID) + return &self->host_machine; + return machines__find(&self->machines, pid); +} + +static inline +struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid) +{ + if (pid == HOST_KERNEL_ID) + return &self->host_machine; + return machines__findnew(&self->machines, pid); +} + +static inline +void perf_session__process_machines(struct perf_session *self, + machine__process_t process) +{ + process(&self->host_machine, self); + return machines__process(&self->machines, process, self); +} + +size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp); + +static inline +size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, + bool with_hits) +{ + return machines__fprintf_dsos_buildid(&self->machines, fp, with_hits); +} + +static inline +size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp) { - return 0; + return hists__fprintf_nr_events(&self->hists, fp); } -#else -int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists, - u64 session_total, const char *helpline, - const char *input_name); -#endif #endif /* __PERF_SESSION_H */ diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index da30b305fba..2316cb5a411 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1,10 +1,10 @@ #include "sort.h" regex_t parent_regex; -char default_parent_pattern[] = "^sys_|^do_page_fault"; -char *parent_pattern = default_parent_pattern; -char default_sort_order[] = "comm,dso,symbol"; -char *sort_order = default_sort_order; +const char default_parent_pattern[] = "^sys_|^do_page_fault"; +const char *parent_pattern = default_parent_pattern; +const char default_sort_order[] = "comm,dso,symbol"; +const char *sort_order = default_sort_order; int sort__need_collapse = 0; int sort__has_parent = 0; diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index b7c54eeed9c..0d61c4082f4 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -25,10 +25,10 @@ #include "sort.h" extern regex_t parent_regex; -extern char *sort_order; -extern char default_parent_pattern[]; -extern char *parent_pattern; -extern char default_sort_order[]; +extern const char *sort_order; +extern const char default_parent_pattern[]; +extern const char *parent_pattern; +extern const char default_sort_order[]; extern int sort__need_collapse; extern int sort__has_parent; extern char *field_sep; @@ -43,20 +43,15 @@ extern enum sort_type sort__first_dimension; struct hist_entry { struct rb_node rb_node; - u64 count; - u64 count_sys; - u64 count_us; - u64 count_guest_sys; - u64 count_guest_us; - - /* - * XXX WARNING! - * thread _has_ to come after ms, see - * hist_browser__selected_thread in util/newt.c - */ + u64 period; + u64 period_sys; + u64 period_us; + u64 period_guest_sys; + u64 period_guest_us; struct map_symbol ms; struct thread *thread; u64 ip; + u32 nr_events; char level; u8 filtered; struct symbol *parent; diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index e782e7db16c..a06131f6259 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -130,8 +130,9 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name) if (symbol_conf.priv_size) self = ((void *)self) + symbol_conf.priv_size; - self->start = start; - self->end = len ? start + len - 1 : start; + self->start = start; + self->end = len ? start + len - 1 : start; + self->namelen = namelen - 1; pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); @@ -189,6 +190,7 @@ struct dso *dso__new(const char *name) self->sorted_by_name = 0; self->has_build_id = 0; self->kernel = DSO_TYPE_USER; + INIT_LIST_HEAD(&self->node); } return self; @@ -484,7 +486,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, symbol_filter_t filter) { struct map_groups *kmaps = map__kmap(map)->kmaps; - struct kernel_info *kerninfo = kmaps->this_kerninfo; + struct machine *machine = kmaps->machine; struct map *curr_map = map; struct symbol *pos; int count = 0; @@ -507,8 +509,8 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, if (strcmp(curr_map->dso->short_name, module)) { if (curr_map != map && - self->kernel == DSO_TYPE_GUEST_KERNEL && - is_default_guest(kerninfo)) { + self->kernel == DSO_TYPE_GUEST_KERNEL && + machine__is_default_guest(machine)) { /* * We assume all symbols of a module are * continuous in * kallsyms, so curr_map @@ -523,16 +525,16 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, curr_map = map_groups__find_by_name(kmaps, map->type, module); if (curr_map == NULL) { - pr_err("%s/proc/{kallsyms,modules} " + pr_debug("%s/proc/{kallsyms,modules} " "inconsistency while looking " "for \"%s\" module!\n", - kerninfo->root_dir, module); + machine->root_dir, module); curr_map = map; goto discard_symbol; } if (curr_map->dso->loaded && - !is_default_guest(kmaps->this_kerninfo)) + !machine__is_default_guest(machine)) goto discard_symbol; } /* @@ -585,7 +587,7 @@ discard_symbol: rb_erase(&pos->rb_node, root); if (curr_map != map && self->kernel == DSO_TYPE_GUEST_KERNEL && - is_default_guest(kmaps->this_kerninfo)) { + machine__is_default_guest(kmaps->machine)) { dso__set_loaded(curr_map->dso, curr_map->type); } @@ -1290,7 +1292,7 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) char build_id_hex[BUILD_ID_SIZE * 2 + 1]; int ret = -1; int fd; - struct kernel_info *kerninfo; + struct machine *machine; const char *root_dir; dso__set_loaded(self, map->type); @@ -1300,10 +1302,10 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) else if (self->kernel == DSO_TYPE_GUEST_KERNEL) return dso__load_guest_kernel_sym(self, map, filter); - if (map->groups && map->groups->this_kerninfo) - kerninfo = map->groups->this_kerninfo; + if (map->groups && map->groups->machine) + machine = map->groups->machine; else - kerninfo = NULL; + machine = NULL; name = malloc(size); if (!name) @@ -1358,8 +1360,8 @@ more: snprintf(name, size, "%s", self->long_name); break; case DSO__ORIG_GUEST_KMODULE: - if (map->groups && map->groups->this_kerninfo) - root_dir = map->groups->this_kerninfo->root_dir; + if (map->groups && map->groups->machine) + root_dir = map->groups->machine->root_dir; else root_dir = ""; snprintf(name, size, "%s%s", root_dir, self->long_name); @@ -1527,21 +1529,20 @@ static char *get_kernel_version(const char *root_dir) return strdup(name); } -static int map_groups__set_modules_path(struct map_groups *self, - const char *root_dir) +static int machine__set_modules_path(struct machine *self) { char *version; char modules_path[PATH_MAX]; - version = get_kernel_version(root_dir); + version = get_kernel_version(self->root_dir); if (!version) return -1; snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel", - root_dir, version); + self->root_dir, version); free(version); - return map_groups__set_modules_path_dir(self, modules_path); + return map_groups__set_modules_path_dir(&self->kmaps, modules_path); } /* @@ -1563,14 +1564,12 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) return self; } -struct map *map_groups__new_module(struct map_groups *self, u64 start, - const char *filename, - struct kernel_info *kerninfo) +struct map *machine__new_module(struct machine *self, u64 start, + const char *filename) { struct map *map; - struct dso *dso; + struct dso *dso = __dsos__findnew(&self->kernel_dsos, filename); - dso = __dsos__findnew(&kerninfo->dsos__kernel, filename); if (dso == NULL) return NULL; @@ -1578,28 +1577,27 @@ struct map *map_groups__new_module(struct map_groups *self, u64 start, if (map == NULL) return NULL; - if (is_host_kernel(kerninfo)) + if (machine__is_host(self)) dso->origin = DSO__ORIG_KMODULE; else dso->origin = DSO__ORIG_GUEST_KMODULE; - map_groups__insert(self, map); + map_groups__insert(&self->kmaps, map); return map; } -static int map_groups__create_modules(struct kernel_info *kerninfo) +static int machine__create_modules(struct machine *self) { char *line = NULL; size_t n; FILE *file; struct map *map; - const char *root_dir; const char *modules; char path[PATH_MAX]; - if (is_default_guest(kerninfo)) + if (machine__is_default_guest(self)) modules = symbol_conf.default_guest_modules; else { - sprintf(path, "%s/proc/modules", kerninfo->root_dir); + sprintf(path, "%s/proc/modules", self->root_dir); modules = path; } @@ -1607,8 +1605,6 @@ static int map_groups__create_modules(struct kernel_info *kerninfo) if (file == NULL) return -1; - root_dir = kerninfo->root_dir; - while (!feof(file)) { char name[PATH_MAX]; u64 start; @@ -1637,17 +1633,16 @@ static int map_groups__create_modules(struct kernel_info *kerninfo) *sep = '\0'; snprintf(name, sizeof(name), "[%s]", line); - map = map_groups__new_module(&kerninfo->kmaps, - start, name, kerninfo); + map = machine__new_module(self, start, name); if (map == NULL) goto out_delete_line; - dso__kernel_module_get_build_id(map->dso, root_dir); + dso__kernel_module_get_build_id(map->dso, self->root_dir); } free(line); fclose(file); - return map_groups__set_modules_path(&kerninfo->kmaps, root_dir); + return machine__set_modules_path(self); out_delete_line: free(line); @@ -1819,16 +1814,16 @@ static int dso__load_guest_kernel_sym(struct dso *self, struct map *map, { int err; const char *kallsyms_filename = NULL; - struct kernel_info *kerninfo; + struct machine *machine; char path[PATH_MAX]; if (!map->groups) { pr_debug("Guest kernel map hasn't the point to groups\n"); return -1; } - kerninfo = map->groups->this_kerninfo; + machine = map->groups->machine; - if (is_default_guest(kerninfo)) { + if (machine__is_default_guest(machine)) { /* * if the user specified a vmlinux filename, use it and only * it, reporting errors to the user if it cannot be used. @@ -1844,7 +1839,7 @@ static int dso__load_guest_kernel_sym(struct dso *self, struct map *map, if (!kallsyms_filename) return -1; } else { - sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir); + sprintf(path, "%s/proc/kallsyms", machine->root_dir); kallsyms_filename = path; } @@ -1855,9 +1850,8 @@ static int dso__load_guest_kernel_sym(struct dso *self, struct map *map, out_try_fixup: if (err > 0) { if (kallsyms_filename != NULL) { - kern_mmap_name(kerninfo, path); - dso__set_long_name(self, - strdup(path)); + machine__mmap_name(machine, path, sizeof(path)); + dso__set_long_name(self, strdup(path)); } map__fixup_start(map); map__fixup_end(map); @@ -1896,27 +1890,32 @@ struct dso *__dsos__findnew(struct list_head *head, const char *name) return dso; } -static void __dsos__fprintf(struct list_head *head, FILE *fp) +size_t __dsos__fprintf(struct list_head *head, FILE *fp) { struct dso *pos; + size_t ret = 0; list_for_each_entry(pos, head, node) { int i; for (i = 0; i < MAP__NR_TYPES; ++i) - dso__fprintf(pos, i, fp); + ret += dso__fprintf(pos, i, fp); } + + return ret; } -void dsos__fprintf(struct rb_root *kerninfo_root, FILE *fp) +size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp) { struct rb_node *nd; + size_t ret = 0; - for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) { - struct kernel_info *pos = rb_entry(nd, struct kernel_info, - rb_node); - __dsos__fprintf(&pos->dsos__kernel, fp); - __dsos__fprintf(&pos->dsos__user, fp); + for (nd = rb_first(self); nd; nd = rb_next(nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); + ret += __dsos__fprintf(&pos->kernel_dsos, fp); + ret += __dsos__fprintf(&pos->user_dsos, fp); } + + return ret; } static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, @@ -1934,19 +1933,15 @@ static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, return ret; } -size_t dsos__fprintf_buildid(struct rb_root *kerninfo_root, - FILE *fp, bool with_hits) +size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits) { struct rb_node *nd; size_t ret = 0; - for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) { - struct kernel_info *pos = rb_entry(nd, struct kernel_info, - rb_node); - ret += __dsos__fprintf_buildid(&pos->dsos__kernel, - fp, with_hits); - ret += __dsos__fprintf_buildid(&pos->dsos__user, - fp, with_hits); + for (nd = rb_first(self); nd; nd = rb_next(nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); + ret += __dsos__fprintf_buildid(&pos->kernel_dsos, fp, with_hits); + ret += __dsos__fprintf_buildid(&pos->user_dsos, fp, with_hits); } return ret; } @@ -1963,14 +1958,12 @@ struct dso *dso__new_kernel(const char *name) return self; } -static struct dso *dso__new_guest_kernel(struct kernel_info *kerninfo, +static struct dso *dso__new_guest_kernel(struct machine *machine, const char *name) { - char buff[PATH_MAX]; - struct dso *self; + char bf[PATH_MAX]; + struct dso *self = dso__new(name ?: machine__mmap_name(machine, bf, sizeof(bf))); - kern_mmap_name(kerninfo, buff); - self = dso__new(name ?: buff); if (self != NULL) { dso__set_short_name(self, "[guest.kernel]"); self->kernel = DSO_TYPE_GUEST_KERNEL; @@ -1979,64 +1972,78 @@ static struct dso *dso__new_guest_kernel(struct kernel_info *kerninfo, return self; } -void dso__read_running_kernel_build_id(struct dso *self, - struct kernel_info *kerninfo) +void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine) { char path[PATH_MAX]; - if (is_default_guest(kerninfo)) + if (machine__is_default_guest(machine)) return; - sprintf(path, "%s/sys/kernel/notes", kerninfo->root_dir); + sprintf(path, "%s/sys/kernel/notes", machine->root_dir); if (sysfs__read_build_id(path, self->build_id, sizeof(self->build_id)) == 0) self->has_build_id = true; } -static struct dso *dsos__create_kernel(struct kernel_info *kerninfo) +static struct dso *machine__create_kernel(struct machine *self) { const char *vmlinux_name = NULL; struct dso *kernel; - if (is_host_kernel(kerninfo)) { + if (machine__is_host(self)) { vmlinux_name = symbol_conf.vmlinux_name; kernel = dso__new_kernel(vmlinux_name); } else { - if (is_default_guest(kerninfo)) + if (machine__is_default_guest(self)) vmlinux_name = symbol_conf.default_guest_vmlinux_name; - kernel = dso__new_guest_kernel(kerninfo, vmlinux_name); + kernel = dso__new_guest_kernel(self, vmlinux_name); } if (kernel != NULL) { - dso__read_running_kernel_build_id(kernel, kerninfo); - dsos__add(&kerninfo->dsos__kernel, kernel); + dso__read_running_kernel_build_id(kernel, self); + dsos__add(&self->kernel_dsos, kernel); } return kernel; } -int __map_groups__create_kernel_maps(struct map_groups *self, - struct map *vmlinux_maps[MAP__NR_TYPES], - struct dso *kernel) +int __machine__create_kernel_maps(struct machine *self, struct dso *kernel) { enum map_type type; for (type = 0; type < MAP__NR_TYPES; ++type) { struct kmap *kmap; - vmlinux_maps[type] = map__new2(0, kernel, type); - if (vmlinux_maps[type] == NULL) + self->vmlinux_maps[type] = map__new2(0, kernel, type); + if (self->vmlinux_maps[type] == NULL) return -1; - vmlinux_maps[type]->map_ip = - vmlinux_maps[type]->unmap_ip = identity__map_ip; + self->vmlinux_maps[type]->map_ip = + self->vmlinux_maps[type]->unmap_ip = identity__map_ip; - kmap = map__kmap(vmlinux_maps[type]); - kmap->kmaps = self; - map_groups__insert(self, vmlinux_maps[type]); + kmap = map__kmap(self->vmlinux_maps[type]); + kmap->kmaps = &self->kmaps; + map_groups__insert(&self->kmaps, self->vmlinux_maps[type]); } return 0; } +int machine__create_kernel_maps(struct machine *self) +{ + struct dso *kernel = machine__create_kernel(self); + + if (kernel == NULL || + __machine__create_kernel_maps(self, kernel) < 0) + return -1; + + if (symbol_conf.use_modules && machine__create_modules(self) < 0) + pr_debug("Problems creating module maps, continuing anyway...\n"); + /* + * Now that we have all the maps created, just set the ->end of them: + */ + map_groups__fixup_end(&self->kmaps); + return 0; +} + static void vmlinux_path__exit(void) { while (--vmlinux_path__nr_entries >= 0) { @@ -2153,30 +2160,14 @@ out_free_comm_list: return -1; } -int map_groups__create_kernel_maps(struct rb_root *kerninfo_root, pid_t pid) +int machines__create_kernel_maps(struct rb_root *self, pid_t pid) { - struct kernel_info *kerninfo; - struct dso *kernel; - - kerninfo = kerninfo__findnew(kerninfo_root, pid); - if (kerninfo == NULL) - return -1; - kernel = dsos__create_kernel(kerninfo); - if (kernel == NULL) - return -1; + struct machine *machine = machines__findnew(self, pid); - if (__map_groups__create_kernel_maps(&kerninfo->kmaps, - kerninfo->vmlinux_maps, kernel) < 0) + if (machine == NULL) return -1; - if (symbol_conf.use_modules && - map_groups__create_modules(kerninfo) < 0) - pr_debug("Problems creating module maps, continuing anyway...\n"); - /* - * Now that we have all the maps created, just set the ->end of them: - */ - map_groups__fixup_end(&kerninfo->kmaps); - return 0; + return machine__create_kernel_maps(machine); } static int hex(char ch) @@ -2222,7 +2213,7 @@ char *strxfrchar(char *s, char from, char to) return s; } -int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root) +int machines__create_guest_kernel_maps(struct rb_root *self) { int ret = 0; struct dirent **namelist = NULL; @@ -2233,8 +2224,7 @@ int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root) if (symbol_conf.default_guest_vmlinux_name || symbol_conf.default_guest_modules || symbol_conf.default_guest_kallsyms) { - map_groups__create_kernel_maps(kerninfo_root, - DEFAULT_GUEST_KERNEL_ID); + machines__create_kernel_maps(self, DEFAULT_GUEST_KERNEL_ID); } if (symbol_conf.guestmount) { @@ -2255,8 +2245,7 @@ int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root) pr_debug("Can't access file %s\n", path); goto failure; } - map_groups__create_kernel_maps(kerninfo_root, - pid); + machines__create_kernel_maps(self, pid); } failure: free(namelist); @@ -2264,3 +2253,36 @@ failure: return ret; } + +int machine__load_kallsyms(struct machine *self, const char *filename, + enum map_type type, symbol_filter_t filter) +{ + struct map *map = self->vmlinux_maps[type]; + int ret = dso__load_kallsyms(map->dso, filename, map, filter); + + if (ret > 0) { + dso__set_loaded(map->dso, type); + /* + * Since /proc/kallsyms will have multiple sessions for the + * kernel, with modules between them, fixup the end of all + * sections. + */ + __map_groups__fixup_end(&self->kmaps, type); + } + + return ret; +} + +int machine__load_vmlinux_path(struct machine *self, enum map_type type, + symbol_filter_t filter) +{ + struct map *map = self->vmlinux_maps[type]; + int ret = dso__load_vmlinux_path(map->dso, map, filter); + + if (ret > 0) { + dso__set_loaded(map->dso, type); + map__reloc_vmlinux(map); + } + + return ret; +} diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 478f5ab3778..032469e4187 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -54,6 +54,7 @@ struct symbol { struct rb_node rb_node; u64 start; u64 end; + u16 namelen; char name[0]; }; @@ -77,7 +78,7 @@ struct symbol_conf { *default_guest_kallsyms, *default_guest_modules; const char *guestmount; - char *dso_list_str, + const char *dso_list_str, *comm_list_str, *sym_list_str, *col_width_list_str; @@ -162,9 +163,15 @@ int dso__load_vmlinux_path(struct dso *self, struct map *map, symbol_filter_t filter); int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, symbol_filter_t filter); -void dsos__fprintf(struct rb_root *kerninfo_root, FILE *fp); -size_t dsos__fprintf_buildid(struct rb_root *kerninfo_root, - FILE *fp, bool with_hits); +int machine__load_kallsyms(struct machine *self, const char *filename, + enum map_type type, symbol_filter_t filter); +int machine__load_vmlinux_path(struct machine *self, enum map_type type, + symbol_filter_t filter); + +size_t __dsos__fprintf(struct list_head *head, FILE *fp); + +size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp); +size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits); size_t dso__fprintf_buildid(struct dso *self, FILE *fp); size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); @@ -186,8 +193,7 @@ enum dso_origin { char dso__symtab_origin(const struct dso *self); void dso__set_long_name(struct dso *self, char *name); void dso__set_build_id(struct dso *self, void *build_id); -void dso__read_running_kernel_build_id(struct dso *self, - struct kernel_info *kerninfo); +void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine); struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, const char *name); @@ -200,11 +206,11 @@ int kallsyms__parse(const char *filename, void *arg, int (*process_symbol)(void *arg, const char *name, char type, u64 start)); -int __map_groups__create_kernel_maps(struct map_groups *self, - struct map *vmlinux_maps[MAP__NR_TYPES], - struct dso *kernel); -int map_groups__create_kernel_maps(struct rb_root *kerninfo_root, pid_t pid); -int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root); +int __machine__create_kernel_maps(struct machine *self, struct dso *kernel); +int machine__create_kernel_maps(struct machine *self); + +int machines__create_kernel_maps(struct rb_root *self, pid_t pid); +int machines__create_guest_kernel_maps(struct rb_root *self); int symbol__init(void); bool symbol_type__is_a(char symbol_type, enum map_type map_type); diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 30cd9b57595..b1572601286 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -487,6 +487,17 @@ get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events) return nr_tracepoints > 0 ? path.next : NULL; } +bool have_tracepoints(struct perf_event_attr *pattrs, int nb_events) +{ + int i; + + for (i = 0; i < nb_events; i++) + if (pattrs[i].type == PERF_TYPE_TRACEPOINT) + return true; + + return false; +} + int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events) { char buf[BUFSIZ]; diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index d6ef414075a..73a02223c62 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -691,11 +691,6 @@ static int __read_expected(enum event_type expect, const char *str, return ret; } -static int read_expected_warn(enum event_type expect, const char *str, bool warn) -{ - return __read_expected(expect, str, 1, warn); -} - static int read_expected(enum event_type expect, const char *str) { return __read_expected(expect, str, 1, true); @@ -1942,7 +1937,7 @@ void *raw_field_ptr(struct event *event, const char *name, void *data) if (!field) return NULL; - if (field->flags & FIELD_IS_STRING) { + if (field->flags & FIELD_IS_DYNAMIC) { int offset; offset = *(int *)(data + field->offset); @@ -3104,90 +3099,6 @@ static void print_args(struct print_arg *args) } } -static void parse_header_field(const char *field, - int *offset, int *size, bool warn) -{ - char *token; - int type; - - if (read_expected(EVENT_ITEM, "field") < 0) - return; - if (read_expected(EVENT_OP, ":") < 0) - return; - - /* type */ - if (read_expect_type(EVENT_ITEM, &token) < 0) - goto fail; - free_token(token); - - if (read_expected_warn(EVENT_ITEM, field, warn) < 0) - return; - if (read_expected(EVENT_OP, ";") < 0) - return; - if (read_expected(EVENT_ITEM, "offset") < 0) - return; - if (read_expected(EVENT_OP, ":") < 0) - return; - if (read_expect_type(EVENT_ITEM, &token) < 0) - goto fail; - *offset = atoi(token); - free_token(token); - if (read_expected(EVENT_OP, ";") < 0) - return; - if (read_expected(EVENT_ITEM, "size") < 0) - return; - if (read_expected(EVENT_OP, ":") < 0) - return; - if (read_expect_type(EVENT_ITEM, &token) < 0) - goto fail; - *size = atoi(token); - free_token(token); - if (read_expected(EVENT_OP, ";") < 0) - return; - type = read_token(&token); - if (type != EVENT_NEWLINE) { - /* newer versions of the kernel have a "signed" type */ - if (type != EVENT_ITEM) - goto fail; - - if (strcmp(token, "signed") != 0) - goto fail; - - free_token(token); - - if (read_expected(EVENT_OP, ":") < 0) - return; - - if (read_expect_type(EVENT_ITEM, &token)) - goto fail; - - free_token(token); - if (read_expected(EVENT_OP, ";") < 0) - return; - - if (read_expect_type(EVENT_NEWLINE, &token)) - goto fail; - } - fail: - free_token(token); -} - -int parse_header_page(char *buf, unsigned long size) -{ - init_input_buf(buf, size); - - parse_header_field("timestamp", &header_page_ts_offset, - &header_page_ts_size, true); - parse_header_field("commit", &header_page_size_offset, - &header_page_size_size, true); - parse_header_field("overwrite", &header_page_overwrite_offset, - &header_page_overwrite_size, false); - parse_header_field("data", &header_page_data_offset, - &header_page_data_size, true); - - return 0; -} - int parse_ftrace_file(char *buf, unsigned long size) { struct format_field *field; diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 44889c9b563..cb54cd002f4 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -51,6 +51,13 @@ static int long_size; static unsigned long page_size; static ssize_t calc_data_size; +static bool repipe; + +/* If it fails, the next read will report it */ +static void skip(int size) +{ + lseek(input_fd, size, SEEK_CUR); +} static int do_read(int fd, void *buf, int size) { @@ -62,6 +69,13 @@ static int do_read(int fd, void *buf, int size) if (ret <= 0) return -1; + if (repipe) { + int retw = write(STDOUT_FILENO, buf, ret); + + if (retw <= 0 || retw != ret) + die("repiping input file"); + } + size -= ret; buf += ret; } @@ -116,6 +130,13 @@ static char *read_string(void) if (!r) die("no data"); + if (repipe) { + int retw = write(STDOUT_FILENO, &c, 1); + + if (retw <= 0 || retw != r) + die("repiping input file string"); + } + buf[size++] = c; if (!c) @@ -169,7 +190,6 @@ static void read_ftrace_printk(void) static void read_header_files(void) { unsigned long long size; - char *header_page; char *header_event; char buf[BUFSIZ]; @@ -179,10 +199,7 @@ static void read_header_files(void) die("did not read header page"); size = read8(); - header_page = malloc_or_die(size); - read_or_die(header_page, size); - parse_header_page(header_page, size); - free(header_page); + skip(size); /* * The size field in the page is of type long, @@ -454,7 +471,7 @@ struct record *trace_read_data(int cpu) return data; } -ssize_t trace_report(int fd) +ssize_t trace_report(int fd, bool __repipe) { char buf[BUFSIZ]; char test[] = { 23, 8, 68 }; @@ -465,6 +482,7 @@ ssize_t trace_report(int fd) ssize_t size; calc_data_size = 1; + repipe = __repipe; input_fd = fd; @@ -499,6 +517,7 @@ ssize_t trace_report(int fd) size = calc_data_size - 1; calc_data_size = 0; + repipe = false; if (show_funcs) { print_funcs(); diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 1f45d468fd9..406d452956d 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -163,7 +163,7 @@ struct record *trace_read_data(int cpu); void parse_set_info(int nr_cpus, int long_sz); -ssize_t trace_report(int fd); +ssize_t trace_report(int fd, bool repipe); void *malloc_or_die(unsigned int size); @@ -244,7 +244,6 @@ extern int header_page_data_size; extern bool latency_format; -int parse_header_page(char *buf, unsigned long size); int trace_parse_common_type(void *data); int trace_parse_common_pid(void *data); int parse_common_pc(void *data); diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index f9b890fde68..214265674dd 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -92,3 +92,25 @@ out_close_from: out: return err; } + +unsigned long convert_unit(unsigned long value, char *unit) +{ + *unit = ' '; + + if (value > 1000) { + value /= 1000; + *unit = 'K'; + } + + if (value > 1000) { + value /= 1000; + *unit = 'M'; + } + + if (value > 1000) { + value /= 1000; + *unit = 'G'; + } + + return value; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index fbf45d1b26f..0795bf304b1 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -423,6 +423,7 @@ char **argv_split(const char *str, int *argcp); void argv_free(char **argv); bool strglobmatch(const char *str, const char *pat); bool strlazymatch(const char *str, const char *pat); +unsigned long convert_unit(unsigned long value, char *unit); #define _STR(x) #x #define STR(x) _STR(x) |