diff options
Diffstat (limited to 'tools/perf/util')
32 files changed, 1956 insertions, 618 deletions
diff --git a/tools/perf/util/data_map.c b/tools/perf/util/data_map.c index ca0bedf637c..b557b836de3 100644 --- a/tools/perf/util/data_map.c +++ b/tools/perf/util/data_map.c @@ -1,20 +1,17 @@ -#include "data_map.h" #include "symbol.h" #include "util.h" #include "debug.h" +#include "thread.h" +#include "session.h" - -static struct perf_file_handler *curr_handler; -static unsigned long mmap_window = 32; -static char __cwd[PATH_MAX]; - -static int process_event_stub(event_t *event __used) +static int process_event_stub(event_t *event __used, + struct perf_session *session __used) { dump_printf(": unhandled!\n"); return 0; } -void register_perf_file_handler(struct perf_file_handler *handler) +static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) { if (!handler->process_sample_event) handler->process_sample_event = process_event_stub; @@ -34,8 +31,6 @@ void register_perf_file_handler(struct perf_file_handler *handler) handler->process_throttle_event = process_event_stub; if (!handler->process_unthrottle_event) handler->process_unthrottle_event = process_event_stub; - - curr_handler = handler; } static const char *event__name[] = { @@ -61,8 +56,9 @@ void event__print_totals(void) event__name[i], event__total[i]); } -static int -process_event(event_t *event, unsigned long offset, unsigned long head) +static int process_event(event_t *event, struct perf_session *session, + struct perf_event_ops *ops, + unsigned long offset, unsigned long head) { trace_event(event); @@ -77,34 +73,34 @@ process_event(event_t *event, unsigned long offset, unsigned long head) switch (event->header.type) { case PERF_RECORD_SAMPLE: - return curr_handler->process_sample_event(event); + return ops->process_sample_event(event, session); case PERF_RECORD_MMAP: - return curr_handler->process_mmap_event(event); + return ops->process_mmap_event(event, session); case PERF_RECORD_COMM: - return curr_handler->process_comm_event(event); + return ops->process_comm_event(event, session); case PERF_RECORD_FORK: - return curr_handler->process_fork_event(event); + return ops->process_fork_event(event, session); case PERF_RECORD_EXIT: - return curr_handler->process_exit_event(event); + return ops->process_exit_event(event, session); case PERF_RECORD_LOST: - return curr_handler->process_lost_event(event); + return ops->process_lost_event(event, session); case PERF_RECORD_READ: - return curr_handler->process_read_event(event); + return ops->process_read_event(event, session); case PERF_RECORD_THROTTLE: - return curr_handler->process_throttle_event(event); + return ops->process_throttle_event(event, session); case PERF_RECORD_UNTHROTTLE: - return curr_handler->process_unthrottle_event(event); + return ops->process_unthrottle_event(event, session); default: - curr_handler->total_unknown++; + ops->total_unknown++; return -1; } } -int perf_header__read_build_ids(int input, off_t offset, off_t size) +int perf_header__read_build_ids(int input, u64 offset, u64 size) { struct build_id_event bev; char filename[PATH_MAX]; - off_t limit = offset + size; + u64 limit = offset + size; int err = -1; while (offset < limit) { @@ -129,88 +125,58 @@ out: return err; } -int mmap_dispatch_perf_file(struct perf_header **pheader, - const char *input_name, - int force, - int full_paths, - int *cwdlen, - char **cwd) +static struct thread *perf_session__register_idle_thread(struct perf_session *self) +{ + struct thread *thread = perf_session__findnew(self, 0); + + if (!thread || thread__set_comm(thread, "swapper")) { + pr_err("problem inserting idle task.\n"); + thread = NULL; + } + + return thread; +} + +int perf_session__process_events(struct perf_session *self, + struct perf_event_ops *ops) { int err; - struct perf_header *header; unsigned long head, shift; unsigned long offset = 0; - struct stat input_stat; size_t page_size; - u64 sample_type; event_t *event; uint32_t size; - int input; char *buf; - if (curr_handler == NULL) { - pr_debug("Forgot to register perf file handler\n"); - return -EINVAL; - } - - page_size = getpagesize(); - - input = open(input_name, O_RDONLY); - if (input < 0) { - pr_err("Failed to open file: %s", input_name); - if (!strcmp(input_name, "perf.data")) - pr_err(" (try 'perf record' first)"); - pr_err("\n"); - return -errno; - } - - if (fstat(input, &input_stat) < 0) { - pr_err("failed to stat file"); - err = -errno; - goto out_close; - } - - err = -EACCES; - if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { - pr_err("file: %s not owned by current user or root\n", - input_name); - goto out_close; - } - - if (input_stat.st_size == 0) { - pr_info("zero-sized file, nothing to do!\n"); - goto done; - } + if (perf_session__register_idle_thread(self) == NULL) + return -ENOMEM; - err = -ENOMEM; - header = perf_header__new(); - if (header == NULL) - goto out_close; + perf_event_ops__fill_defaults(ops); - err = perf_header__read(header, input); - if (err < 0) - goto out_delete; - *pheader = header; - head = header->data_offset; + page_size = getpagesize(); - sample_type = perf_header__sample_type(header); + head = self->header.data_offset; + self->sample_type = perf_header__sample_type(&self->header); err = -EINVAL; - if (curr_handler->sample_type_check && - curr_handler->sample_type_check(sample_type) < 0) - goto out_delete; + if (ops->sample_type_check && ops->sample_type_check(self) < 0) + goto out_err; - if (!full_paths) { - if (getcwd(__cwd, sizeof(__cwd)) == NULL) { - pr_err("failed to get the current directory\n"); + if (!ops->full_paths) { + char bf[PATH_MAX]; + + if (getcwd(bf, sizeof(bf)) == NULL) { err = -errno; - goto out_delete; +out_getcwd_err: + pr_err("failed to get the current directory\n"); + goto out_err; + } + self->cwd = strdup(bf); + if (self->cwd == NULL) { + err = -ENOMEM; + goto out_getcwd_err; } - *cwd = __cwd; - *cwdlen = strlen(*cwd); - } else { - *cwd = NULL; - *cwdlen = 0; + self->cwdlen = strlen(self->cwd); } shift = page_size * (head / page_size); @@ -218,12 +184,12 @@ int mmap_dispatch_perf_file(struct perf_header **pheader, head -= shift; remap: - buf = mmap(NULL, page_size * mmap_window, PROT_READ, - MAP_SHARED, input, offset); + buf = mmap(NULL, page_size * self->mmap_window, PROT_READ, + MAP_SHARED, self->fd, offset); if (buf == MAP_FAILED) { pr_err("failed to mmap file\n"); err = -errno; - goto out_delete; + goto out_err; } more: @@ -233,12 +199,12 @@ more: if (!size) size = 8; - if (head + event->header.size >= page_size * mmap_window) { + if (head + event->header.size >= page_size * self->mmap_window) { int munmap_ret; shift = page_size * (head / page_size); - munmap_ret = munmap(buf, page_size * mmap_window); + munmap_ret = munmap(buf, page_size * self->mmap_window); assert(munmap_ret == 0); offset += shift; @@ -253,7 +219,7 @@ more: (void *)(long)event->header.size, event->header.type); - if (!size || process_event(event, offset, head) < 0) { + if (!size || process_event(event, self, ops, offset, head) < 0) { dump_printf("%p [%p]: skipping unknown header type: %d\n", (void *)(offset + head), @@ -273,19 +239,14 @@ more: head += size; - if (offset + head >= header->data_offset + header->data_size) + if (offset + head >= self->header.data_offset + self->header.data_size) goto done; - if (offset + head < (unsigned long)input_stat.st_size) + if (offset + head < self->size) goto more; done: err = 0; -out_close: - close(input); - +out_err: return err; -out_delete: - perf_header__delete(header); - goto out_close; } diff --git a/tools/perf/util/data_map.h b/tools/perf/util/data_map.h deleted file mode 100644 index 3180ff7e363..00000000000 --- a/tools/perf/util/data_map.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef __PERF_DATAMAP_H -#define __PERF_DATAMAP_H - -#include "event.h" -#include "header.h" - -typedef int (*event_type_handler_t)(event_t *); - -struct perf_file_handler { - event_type_handler_t process_sample_event; - event_type_handler_t process_mmap_event; - event_type_handler_t process_comm_event; - event_type_handler_t process_fork_event; - event_type_handler_t process_exit_event; - event_type_handler_t process_lost_event; - event_type_handler_t process_read_event; - event_type_handler_t process_throttle_event; - event_type_handler_t process_unthrottle_event; - int (*sample_type_check)(u64 sample_type); - unsigned long total_unknown; -}; - -void register_perf_file_handler(struct perf_file_handler *handler); -int mmap_dispatch_perf_file(struct perf_header **pheader, - const char *input_name, - int force, - int full_paths, - int *cwdlen, - char **cwd); -int perf_header__read_build_ids(int input, off_t offset, off_t file_size); - -#endif diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 414b89d1bde..bb0fd6da2d5 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -1,11 +1,16 @@ #include <linux/types.h> #include "event.h" #include "debug.h" +#include "session.h" +#include "sort.h" #include "string.h" +#include "strlist.h" #include "thread.h" static pid_t event__synthesize_comm(pid_t pid, int full, - int (*process)(event_t *event)) + int (*process)(event_t *event, + struct perf_session *session), + struct perf_session *session) { event_t ev; char filename[PATH_MAX]; @@ -54,7 +59,7 @@ out_race: if (!full) { ev.comm.tid = pid; - process(&ev); + process(&ev, session); goto out_fclose; } @@ -72,7 +77,7 @@ out_race: ev.comm.tid = pid; - process(&ev); + process(&ev, session); } closedir(tasks); @@ -86,7 +91,9 @@ out_failure: } static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, - int (*process)(event_t *event)) + int (*process)(event_t *event, + struct perf_session *session), + struct perf_session *session) { char filename[PATH_MAX]; FILE *fp; @@ -141,7 +148,7 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, ev.mmap.pid = tgid; ev.mmap.tid = pid; - process(&ev); + process(&ev, session); } } @@ -149,15 +156,20 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, return 0; } -int event__synthesize_thread(pid_t pid, int (*process)(event_t *event)) +int event__synthesize_thread(pid_t pid, + int (*process)(event_t *event, + struct perf_session *session), + struct perf_session *session) { - pid_t tgid = event__synthesize_comm(pid, 1, process); + pid_t tgid = event__synthesize_comm(pid, 1, process, session); if (tgid == -1) return -1; - return event__synthesize_mmap_events(pid, tgid, process); + return event__synthesize_mmap_events(pid, tgid, process, session); } -void event__synthesize_threads(int (*process)(event_t *event)) +void event__synthesize_threads(int (*process)(event_t *event, + struct perf_session *session), + struct perf_session *session) { DIR *proc; struct dirent dirent, *next; @@ -171,24 +183,47 @@ void event__synthesize_threads(int (*process)(event_t *event)) if (*end) /* only interested in proper numerical dirents */ continue; - event__synthesize_thread(pid, process); + event__synthesize_thread(pid, process, session); } closedir(proc); } -char *event__cwd; -int event__cwdlen; +static void thread__comm_adjust(struct thread *self) +{ + char *comm = self->comm; + + if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && + (!symbol_conf.comm_list || + strlist__has_entry(symbol_conf.comm_list, comm))) { + unsigned int slen = strlen(comm); + + if (slen > comms__col_width) { + comms__col_width = slen; + threads__col_width = slen + 6; + } + } +} + +static int thread__set_comm_adjust(struct thread *self, const char *comm) +{ + int ret = thread__set_comm(self, comm); + + if (ret) + return ret; -struct events_stats event__stats; + thread__comm_adjust(self); -int event__process_comm(event_t *self) + return 0; +} + +int event__process_comm(event_t *self, struct perf_session *session) { - struct thread *thread = threads__findnew(self->comm.pid); + struct thread *thread = perf_session__findnew(session, self->comm.pid); dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid); - if (thread == NULL || thread__set_comm(thread, self->comm.comm)) { + if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) { dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); return -1; } @@ -196,18 +231,18 @@ int event__process_comm(event_t *self) return 0; } -int event__process_lost(event_t *self) +int event__process_lost(event_t *self, struct perf_session *session) { dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); - event__stats.lost += self->lost.lost; + session->events_stats.lost += self->lost.lost; return 0; } -int event__process_mmap(event_t *self) +int event__process_mmap(event_t *self, struct perf_session *session) { - struct thread *thread = threads__findnew(self->mmap.pid); + struct thread *thread = perf_session__findnew(session, self->mmap.pid); struct map *map = map__new(&self->mmap, MAP__FUNCTION, - event__cwd, event__cwdlen); + session->cwd, session->cwdlen); dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n", self->mmap.pid, self->mmap.tid, @@ -224,10 +259,10 @@ int event__process_mmap(event_t *self) return 0; } -int event__process_task(event_t *self) +int event__process_task(event_t *self, struct perf_session *session) { - struct thread *thread = threads__findnew(self->fork.pid); - struct thread *parent = threads__findnew(self->fork.ppid); + struct thread *thread = perf_session__findnew(session, self->fork.pid); + struct thread *parent = perf_session__findnew(session, self->fork.ppid); dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, self->fork.ppid, self->fork.ptid); @@ -249,18 +284,20 @@ int event__process_task(event_t *self) return 0; } -void thread__find_addr_location(struct thread *self, u8 cpumode, +void thread__find_addr_location(struct thread *self, + struct perf_session *session, u8 cpumode, enum map_type type, u64 addr, struct addr_location *al, symbol_filter_t filter) { - struct thread *thread = al->thread = self; + struct map_groups *mg = &self->mg; + al->thread = self; al->addr = addr; if (cpumode & PERF_RECORD_MISC_KERNEL) { al->level = 'k'; - thread = kthread; + mg = &session->kmaps; } else if (cpumode & PERF_RECORD_MISC_USER) al->level = '.'; else { @@ -270,7 +307,7 @@ void thread__find_addr_location(struct thread *self, u8 cpumode, return; } try_again: - al->map = thread__find_map(thread, type, al->addr); + al->map = map_groups__find(mg, type, al->addr); if (al->map == NULL) { /* * If this is outside of all known maps, and is a negative @@ -281,32 +318,139 @@ try_again: * "[vdso]" dso, but for now lets use the old trick of looking * in the whole kernel symbol list. */ - if ((long long)al->addr < 0 && thread != kthread) { - thread = kthread; + if ((long long)al->addr < 0 && mg != &session->kmaps) { + mg = &session->kmaps; goto try_again; } al->sym = NULL; } else { al->addr = al->map->map_ip(al->map, al->addr); - al->sym = map__find_symbol(al->map, al->addr, filter); + al->sym = map__find_symbol(al->map, session, al->addr, filter); } } -int event__preprocess_sample(const event_t *self, struct addr_location *al, - symbol_filter_t filter) +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) + dsos__col_width = slen; + } + + self->slen_calculated = 1; +} + +int event__preprocess_sample(const event_t *self, struct perf_session *session, + struct addr_location *al, symbol_filter_t filter) { u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - struct thread *thread = threads__findnew(self->ip.pid); + struct thread *thread = perf_session__findnew(session, self->ip.pid); if (thread == NULL) return -1; + if (symbol_conf.comm_list && + !strlist__has_entry(symbol_conf.comm_list, thread->comm)) + goto out_filtered; + dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); - thread__find_addr_location(thread, cpumode, MAP__FUNCTION, + thread__find_addr_location(thread, session, cpumode, MAP__FUNCTION, self->ip.ip, al, filter); dump_printf(" ...... dso: %s\n", al->map ? al->map->dso->long_name : al->level == 'H' ? "[hypervisor]" : "<not found>"); + /* + * We have to do this here as we may have a dso with no symbol hit that + * has a name longer than the ones with symbols sampled. + */ + if (al->map && !sort_dso.elide && !al->map->dso->slen_calculated) + dso__calc_col_width(al->map->dso); + + if (symbol_conf.dso_list && + (!al->map || !al->map->dso || + !(strlist__has_entry(symbol_conf.dso_list, al->map->dso->short_name) || + (al->map->dso->short_name != al->map->dso->long_name && + strlist__has_entry(symbol_conf.dso_list, al->map->dso->long_name))))) + goto out_filtered; + + if (symbol_conf.sym_list && al->sym && + !strlist__has_entry(symbol_conf.sym_list, al->sym->name)) + goto out_filtered; + + al->filtered = false; + return 0; + +out_filtered: + al->filtered = true; + return 0; +} + +int event__parse_sample(event_t *event, u64 type, struct sample_data *data) +{ + u64 *array = event->sample.array; + + if (type & PERF_SAMPLE_IP) { + data->ip = event->ip.ip; + array++; + } + + if (type & PERF_SAMPLE_TID) { + u32 *p = (u32 *)array; + data->pid = p[0]; + data->tid = p[1]; + array++; + } + + if (type & PERF_SAMPLE_TIME) { + data->time = *array; + array++; + } + + if (type & PERF_SAMPLE_ADDR) { + data->addr = *array; + array++; + } + + if (type & PERF_SAMPLE_ID) { + data->id = *array; + array++; + } + + if (type & PERF_SAMPLE_STREAM_ID) { + data->stream_id = *array; + array++; + } + + if (type & PERF_SAMPLE_CPU) { + u32 *p = (u32 *)array; + data->cpu = *p; + array++; + } + + if (type & PERF_SAMPLE_PERIOD) { + data->period = *array; + array++; + } + + if (type & PERF_SAMPLE_READ) { + pr_debug("PERF_SAMPLE_READ is unsuported for now\n"); + return -1; + } + + if (type & PERF_SAMPLE_CALLCHAIN) { + data->callchain = (struct ip_callchain *)array; + array += 1 + data->callchain->nr; + } + + if (type & PERF_SAMPLE_RAW) { + u32 *p = (u32 *)array; + data->raw_size = *p; + p++; + data->raw_data = p; + } + return 0; } diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index a4cc8105cf6..690a96d0467 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -56,11 +56,25 @@ struct read_event { u64 id; }; -struct sample_event{ +struct sample_event { struct perf_event_header header; u64 array[]; }; +struct sample_data { + u64 ip; + u32 pid, tid; + u64 time; + u64 addr; + u64 id; + u64 stream_id; + u32 cpu; + u64 period; + struct ip_callchain *callchain; + u32 raw_size; + void *raw_data; +}; + #define BUILD_ID_SIZE 20 struct build_id_event { @@ -81,18 +95,19 @@ typedef union event_union { } event_t; struct events_stats { - unsigned long total; - unsigned long lost; + u64 total; + u64 lost; }; void event__print_totals(void); enum map_type { MAP__FUNCTION = 0, - - MAP__NR_TYPES, + MAP__VARIABLE, }; +#define MAP__NR_TYPES (MAP__VARIABLE + 1) + struct map { union { struct rb_node rb_node; @@ -134,26 +149,35 @@ void map__delete(struct map *self); struct map *map__clone(struct map *self); int map__overlap(struct map *l, struct map *r); size_t map__fprintf(struct map *self, FILE *fp); -struct symbol *map__find_symbol(struct map *self, u64 addr, - symbol_filter_t filter); + +struct perf_session; + +int map__load(struct map *self, struct perf_session *session, + symbol_filter_t filter); +struct symbol *map__find_symbol(struct map *self, struct perf_session *session, + u64 addr, symbol_filter_t filter); +struct symbol *map__find_symbol_by_name(struct map *self, const char *name, + struct perf_session *session, + symbol_filter_t filter); void map__fixup_start(struct map *self); void map__fixup_end(struct map *self); -int event__synthesize_thread(pid_t pid, int (*process)(event_t *event)); -void event__synthesize_threads(int (*process)(event_t *event)); - -extern char *event__cwd; -extern int event__cwdlen; -extern struct events_stats event__stats; -extern unsigned long event__total[PERF_RECORD_MAX]; +int event__synthesize_thread(pid_t pid, + int (*process)(event_t *event, + struct perf_session *session), + struct perf_session *session); +void event__synthesize_threads(int (*process)(event_t *event, + struct perf_session *session), + struct perf_session *session); -int event__process_comm(event_t *self); -int event__process_lost(event_t *self); -int event__process_mmap(event_t *self); -int event__process_task(event_t *self); +int event__process_comm(event_t *self, struct perf_session *session); +int event__process_lost(event_t *self, struct perf_session *session); +int event__process_mmap(event_t *self, struct perf_session *session); +int event__process_task(event_t *self, struct perf_session *session); struct addr_location; -int event__preprocess_sample(const event_t *self, struct addr_location *al, - symbol_filter_t filter); +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); #endif /* __PERF_RECORD_H */ diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 4805e6dfd23..8a0bca55106 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -8,8 +8,8 @@ #include "header.h" #include "../perf.h" #include "trace-event.h" +#include "session.h" #include "symbol.h" -#include "data_map.h" #include "debug.h" /* @@ -58,35 +58,19 @@ int perf_header_attr__add_id(struct perf_header_attr *self, u64 id) return 0; } -/* - * Create new perf.data header: - */ -struct perf_header *perf_header__new(void) +int perf_header__init(struct perf_header *self) { - struct perf_header *self = zalloc(sizeof(*self)); - - if (self != NULL) { - self->size = 1; - self->attr = malloc(sizeof(void *)); - - if (self->attr == NULL) { - free(self); - self = NULL; - } - } - - return self; + self->size = 1; + self->attr = malloc(sizeof(void *)); + return self->attr == NULL ? -ENOMEM : 0; } -void perf_header__delete(struct perf_header *self) +void perf_header__exit(struct perf_header *self) { int i; - for (i = 0; i < self->attrs; ++i) - perf_header_attr__delete(self->attr[i]); - + perf_header_attr__delete(self->attr[i]); free(self->attr); - free(self); } int perf_header__add_attr(struct perf_header *self, @@ -187,7 +171,9 @@ static int do_write(int fd, const void *buf, size_t size) static int __dsos__write_buildid_table(struct list_head *head, int fd) { +#define NAME_ALIGN 64 struct dso *pos; + static const char zero_buf[NAME_ALIGN]; list_for_each_entry(pos, head, node) { int err; @@ -197,14 +183,17 @@ static int __dsos__write_buildid_table(struct list_head *head, int fd) if (!pos->has_build_id) continue; len = pos->long_name_len + 1; - len = ALIGN(len, 64); + len = ALIGN(len, NAME_ALIGN); memset(&b, 0, sizeof(b)); memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); b.header.size = sizeof(b) + len; err = do_write(fd, &b, sizeof(b)); if (err < 0) return err; - err = do_write(fd, pos->long_name, len); + err = do_write(fd, pos->long_name, pos->long_name_len + 1); + if (err < 0) + return err; + err = do_write(fd, zero_buf, len - pos->long_name_len - 1); if (err < 0) return err; } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index d1dbe2b79c4..d118d05d3ab 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -55,8 +55,8 @@ struct perf_header { DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); }; -struct perf_header *perf_header__new(void); -void perf_header__delete(struct perf_header *self); +int perf_header__init(struct perf_header *self); +void perf_header__exit(struct perf_header *self); int perf_header__read(struct perf_header *self, int fd); int perf_header__write(struct perf_header *self, int fd, bool at_exit); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 0ebf6ee16ca..e8daf5ca6fd 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1,9 +1,7 @@ #include "hist.h" - -struct rb_root hist; -struct rb_root collapse_hists; -struct rb_root output_hists; -int callchain; +#include "session.h" +#include "sort.h" +#include <math.h> struct callchain_param callchain_param = { .mode = CHAIN_GRAPH_REL, @@ -14,11 +12,12 @@ struct callchain_param callchain_param = { * histogram, sorted on item, collects counts */ -struct hist_entry *__hist_entry__add(struct addr_location *al, - struct symbol *sym_parent, - u64 count, bool *hit) +struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self, + struct addr_location *al, + struct symbol *sym_parent, + u64 count, bool *hit) { - struct rb_node **p = &hist.rb_node; + struct rb_node **p = &self->hists.rb_node; struct rb_node *parent = NULL; struct hist_entry *he; struct hist_entry entry = { @@ -54,7 +53,7 @@ struct hist_entry *__hist_entry__add(struct addr_location *al, return NULL; *he = entry; rb_link_node(&he->rb_node, parent, p); - rb_insert_color(&he->rb_node, &hist); + rb_insert_color(&he->rb_node, &self->hists); *hit = false; return he; } @@ -102,9 +101,9 @@ void hist_entry__free(struct hist_entry *he) * collapse the histogram */ -void collapse__insert_entry(struct hist_entry *he) +static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) { - struct rb_node **p = &collapse_hists.rb_node; + struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; struct hist_entry *iter; int64_t cmp; @@ -128,38 +127,45 @@ void collapse__insert_entry(struct hist_entry *he) } rb_link_node(&he->rb_node, parent, p); - rb_insert_color(&he->rb_node, &collapse_hists); + rb_insert_color(&he->rb_node, root); } -void collapse__resort(void) +void perf_session__collapse_resort(struct perf_session *self) { + struct rb_root tmp; struct rb_node *next; struct hist_entry *n; if (!sort__need_collapse) return; - next = rb_first(&hist); + tmp = RB_ROOT; + next = rb_first(&self->hists); + while (next) { n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); - rb_erase(&n->rb_node, &hist); - collapse__insert_entry(n); + rb_erase(&n->rb_node, &self->hists); + collapse__insert_entry(&tmp, n); } + + self->hists = tmp; } /* * reverse the map, sort on count. */ -void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits) +static void perf_session__insert_output_hist_entry(struct rb_root *root, + struct hist_entry *he, + u64 min_callchain_hits) { - struct rb_node **p = &output_hists.rb_node; + struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; struct hist_entry *iter; - if (callchain) + if (symbol_conf.use_callchain) callchain_param.sort(&he->sorted_chain, &he->callchain, min_callchain_hits, &callchain_param); @@ -174,29 +180,483 @@ void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits) } rb_link_node(&he->rb_node, parent, p); - rb_insert_color(&he->rb_node, &output_hists); + rb_insert_color(&he->rb_node, root); } -void output__resort(u64 total_samples) +void perf_session__output_resort(struct perf_session *self, u64 total_samples) { + struct rb_root tmp; struct rb_node *next; struct hist_entry *n; - struct rb_root *tree = &hist; u64 min_callchain_hits; min_callchain_hits = total_samples * (callchain_param.min_percent / 100); - if (sort__need_collapse) - tree = &collapse_hists; - - next = rb_first(tree); + tmp = RB_ROOT; + next = rb_first(&self->hists); while (next) { n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); - rb_erase(&n->rb_node, tree); - output__insert_entry(n, min_callchain_hits); + rb_erase(&n->rb_node, &self->hists); + perf_session__insert_output_hist_entry(&tmp, n, + min_callchain_hits); + } + + self->hists = tmp; +} + +static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) +{ + int i; + int ret = fprintf(fp, " "); + + for (i = 0; i < left_margin; i++) + ret += fprintf(fp, " "); + + return ret; +} + +static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, + int left_margin) +{ + int i; + size_t ret = callchain__fprintf_left_margin(fp, left_margin); + + for (i = 0; i < depth; i++) + if (depth_mask & (1 << i)) + ret += fprintf(fp, "| "); + else + ret += fprintf(fp, " "); + + ret += fprintf(fp, "\n"); + + return ret; +} + +static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, + int depth, int depth_mask, int count, + u64 total_samples, int hits, + int left_margin) +{ + int i; + size_t ret = 0; + + ret += callchain__fprintf_left_margin(fp, left_margin); + for (i = 0; i < depth; i++) { + if (depth_mask & (1 << i)) + ret += fprintf(fp, "|"); + else + ret += fprintf(fp, " "); + if (!count && i == depth - 1) { + double percent; + + percent = hits * 100.0 / total_samples; + ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); + } else + ret += fprintf(fp, "%s", " "); + } + if (chain->sym) + ret += fprintf(fp, "%s\n", chain->sym->name); + else + ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); + + return ret; +} + +static struct symbol *rem_sq_bracket; +static struct callchain_list rem_hits; + +static void init_rem_hits(void) +{ + rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); + if (!rem_sq_bracket) { + fprintf(stderr, "Not enough memory to display remaining hits\n"); + return; + } + + strcpy(rem_sq_bracket->name, "[...]"); + rem_hits.sym = rem_sq_bracket; +} + +static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, + u64 total_samples, int depth, + int depth_mask, int left_margin) +{ + struct rb_node *node, *next; + struct callchain_node *child; + struct callchain_list *chain; + int new_depth_mask = depth_mask; + u64 new_total; + u64 remaining; + size_t ret = 0; + int i; + + if (callchain_param.mode == CHAIN_GRAPH_REL) + new_total = self->children_hit; + else + new_total = total_samples; + + remaining = new_total; + + node = rb_first(&self->rb_root); + while (node) { + u64 cumul; + + child = rb_entry(node, struct callchain_node, rb_node); + cumul = cumul_hits(child); + remaining -= cumul; + + /* + * The depth mask manages the output of pipes that show + * the depth. We don't want to keep the pipes of the current + * level for the last child of this depth. + * Except if we have remaining filtered hits. They will + * supersede the last child + */ + next = rb_next(node); + if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) + new_depth_mask &= ~(1 << (depth - 1)); + + /* + * But we keep the older depth mask for the line seperator + * to keep the level link until we reach the last child + */ + ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, + left_margin); + i = 0; + list_for_each_entry(chain, &child->val, list) { + if (chain->ip >= PERF_CONTEXT_MAX) + continue; + ret += ipchain__fprintf_graph(fp, chain, depth, + new_depth_mask, i++, + new_total, + cumul, + left_margin); + } + ret += __callchain__fprintf_graph(fp, child, new_total, + depth + 1, + new_depth_mask | (1 << depth), + left_margin); + node = next; + } + + if (callchain_param.mode == CHAIN_GRAPH_REL && + remaining && remaining != new_total) { + + if (!rem_sq_bracket) + return ret; + + new_depth_mask &= ~(1 << (depth - 1)); + + ret += ipchain__fprintf_graph(fp, &rem_hits, depth, + new_depth_mask, 0, new_total, + remaining, left_margin); + } + + return ret; +} + +static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, + u64 total_samples, int left_margin) +{ + struct callchain_list *chain; + bool printed = false; + int i = 0; + int ret = 0; + + list_for_each_entry(chain, &self->val, list) { + if (chain->ip >= PERF_CONTEXT_MAX) + continue; + + if (!i++ && sort__first_dimension == SORT_SYM) + continue; + + if (!printed) { + ret += callchain__fprintf_left_margin(fp, left_margin); + ret += fprintf(fp, "|\n"); + ret += callchain__fprintf_left_margin(fp, left_margin); + ret += fprintf(fp, "---"); + + left_margin += 3; + printed = true; + } else + ret += callchain__fprintf_left_margin(fp, left_margin); + + if (chain->sym) + ret += fprintf(fp, " %s\n", chain->sym->name); + else + ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); + } + + ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); + + return ret; +} + +static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, + u64 total_samples) +{ + struct callchain_list *chain; + size_t ret = 0; + + if (!self) + return 0; + + ret += callchain__fprintf_flat(fp, self->parent, total_samples); + + + list_for_each_entry(chain, &self->val, list) { + if (chain->ip >= PERF_CONTEXT_MAX) + continue; + if (chain->sym) + ret += fprintf(fp, " %s\n", chain->sym->name); + else + ret += fprintf(fp, " %p\n", + (void *)(long)chain->ip); + } + + return ret; +} + +static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, + u64 total_samples, int left_margin) +{ + struct rb_node *rb_node; + struct callchain_node *chain; + size_t ret = 0; + + rb_node = rb_first(&self->sorted_chain); + while (rb_node) { + double percent; + + chain = rb_entry(rb_node, struct callchain_node, rb_node); + percent = chain->hit * 100.0 / total_samples; + switch (callchain_param.mode) { + case CHAIN_FLAT: + ret += percent_color_fprintf(fp, " %6.2f%%\n", + percent); + ret += callchain__fprintf_flat(fp, chain, total_samples); + break; + case CHAIN_GRAPH_ABS: /* Falldown */ + case CHAIN_GRAPH_REL: + ret += callchain__fprintf_graph(fp, chain, total_samples, + left_margin); + case CHAIN_NONE: + default: + break; + } + ret += fprintf(fp, "\n"); + rb_node = rb_next(rb_node); + } + + return ret; +} + +static size_t hist_entry__fprintf(struct hist_entry *self, + struct perf_session *session, + struct perf_session *pair_session, + bool show_displacement, + long displacement, FILE *fp) +{ + struct sort_entry *se; + u64 count, total; + const char *sep = symbol_conf.field_sep; + size_t 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; + } else { + count = self->count; + total = session->events_stats.total; + } + + if (total) + ret = percent_color_fprintf(fp, sep ? "%.2f" : " %6.2f%%", + (count * 100.0) / total); + else + ret = fprintf(fp, sep ? "%lld" : "%12lld ", count); + + if (symbol_conf.show_nr_samples) { + if (sep) + fprintf(fp, "%c%lld", *sep, count); + else + fprintf(fp, "%11lld", count); + } + + if (pair_session) { + char bf[32]; + double old_percent = 0, new_percent = 0, diff; + + if (total > 0) + old_percent = (count * 100.0) / total; + if (session->events_stats.total > 0) + new_percent = (self->count * 100.0) / session->events_stats.total; + + diff = new_percent - old_percent; + + if (fabs(diff) >= 0.01) + snprintf(bf, sizeof(bf), "%+4.2F%%", diff); + else + snprintf(bf, sizeof(bf), " "); + + if (sep) + ret += fprintf(fp, "%c%s", *sep, bf); + else + ret += fprintf(fp, "%11.11s", bf); + + if (show_displacement) { + if (displacement) + snprintf(bf, sizeof(bf), "%+4ld", displacement); + else + snprintf(bf, sizeof(bf), " "); + + if (sep) + fprintf(fp, "%c%s", *sep, bf); + else + fprintf(fp, "%6.6s", bf); + } + } + + list_for_each_entry(se, &hist_entry__sort_list, list) { + if (se->elide) + continue; + + fprintf(fp, "%s", sep ?: " "); + ret += se->print(fp, self, se->width ? *se->width : 0); + } + + ret += fprintf(fp, "\n"); + + if (symbol_conf.use_callchain) { + int left_margin = 0; + + if (sort__first_dimension == SORT_COMM) { + se = list_first_entry(&hist_entry__sort_list, typeof(*se), + list); + left_margin = se->width ? *se->width : 0; + left_margin -= thread__comm_len(self->thread); + } + + hist_entry_callchain__fprintf(fp, self, session->events_stats.total, + left_margin); + } + + return ret; +} + +size_t perf_session__fprintf_hists(struct perf_session *self, + struct perf_session *pair, + bool show_displacement, FILE *fp) +{ + struct sort_entry *se; + struct rb_node *nd; + size_t ret = 0; + unsigned long position = 1; + long displacement = 0; + unsigned int width; + const char *sep = symbol_conf.field_sep; + char *col_width = symbol_conf.col_width_list_str; + + init_rem_hits(); + + fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); + + if (symbol_conf.show_nr_samples) { + if (sep) + fprintf(fp, "%cSamples", *sep); + else + fputs(" Samples ", fp); + } + + if (pair) { + if (sep) + ret += fprintf(fp, "%cDelta", *sep); + else + ret += fprintf(fp, " Delta "); + + if (show_displacement) { + if (sep) + ret += fprintf(fp, "%cDisplacement", *sep); + else + ret += fprintf(fp, " Displ"); + } + } + + list_for_each_entry(se, &hist_entry__sort_list, list) { + if (se->elide) + continue; + if (sep) { + fprintf(fp, "%c%s", *sep, se->header); + continue; + } + width = strlen(se->header); + if (se->width) { + if (symbol_conf.col_width_list_str) { + if (col_width) { + *se->width = atoi(col_width); + col_width = strchr(col_width, ','); + if (col_width) + ++col_width; + } + } + width = *se->width = max(*se->width, width); + } + fprintf(fp, " %*s", width, se->header); + } + fprintf(fp, "\n"); + + if (sep) + goto print_entries; + + fprintf(fp, "# ........"); + if (symbol_conf.show_nr_samples) + fprintf(fp, " .........."); + if (pair) { + fprintf(fp, " .........."); + if (show_displacement) + fprintf(fp, " ....."); + } + list_for_each_entry(se, &hist_entry__sort_list, list) { + unsigned int i; + + if (se->elide) + continue; + + fprintf(fp, " "); + if (se->width) + width = *se->width; + else + width = strlen(se->header); + for (i = 0; i < width; i++) + fprintf(fp, "."); + } + + fprintf(fp, "\n#\n"); + +print_entries: + for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { + struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); + + if (show_displacement) { + if (h->pair != NULL) + displacement = ((long)h->pair->position - + (long)position); + else + displacement = 0; + ++position; + } + ret += hist_entry__fprintf(h, self, pair, show_displacement, + displacement, fp); } + + free(rem_sq_bracket); + + return ret; } diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 3020db0c929..e5f99b24048 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -1,50 +1,27 @@ #ifndef __PERF_HIST_H #define __PERF_HIST_H -#include "../builtin.h" -#include "util.h" - -#include "color.h" -#include <linux/list.h> -#include "cache.h" -#include <linux/rbtree.h> -#include "symbol.h" -#include "string.h" +#include <linux/types.h> #include "callchain.h" -#include "strlist.h" -#include "values.h" - -#include "../perf.h" -#include "debug.h" -#include "header.h" - -#include "parse-options.h" -#include "parse-events.h" -#include "thread.h" -#include "sort.h" - -extern struct rb_root hist; -extern struct rb_root collapse_hists; -extern struct rb_root output_hists; -extern int callchain; extern struct callchain_param callchain_param; -extern unsigned long total; -extern unsigned long total_mmap; -extern unsigned long total_comm; -extern unsigned long total_fork; -extern unsigned long total_unknown; -extern unsigned long total_lost; -struct hist_entry *__hist_entry__add(struct addr_location *al, - struct symbol *parent, - u64 count, bool *hit); +struct perf_session; +struct hist_entry; +struct addr_location; +struct symbol; + +struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self, + struct addr_location *al, + struct symbol *parent, + u64 count, bool *hit); extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); -extern void hist_entry__free(struct hist_entry *); -extern void collapse__insert_entry(struct hist_entry *); -extern void collapse__resort(void); -extern void output__insert_entry(struct hist_entry *, u64); -extern void output__resort(u64); +void hist_entry__free(struct hist_entry *); +void perf_session__output_resort(struct perf_session *self, u64 total_samples); +void perf_session__collapse_resort(struct perf_session *self); +size_t perf_session__fprintf_hists(struct perf_session *self, + struct perf_session *pair, + bool show_displacement, FILE *fp); #endif /* __PERF_HIST_H */ diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 69f94fe9db2..c4d55a0da2e 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -104,43 +104,70 @@ void map__fixup_end(struct map *self) #define DSO__DELETED "(deleted)" -struct symbol *map__find_symbol(struct map *self, u64 addr, - symbol_filter_t filter) +int map__load(struct map *self, struct perf_session *session, + symbol_filter_t filter) { - if (!dso__loaded(self->dso, self->type)) { - int nr = dso__load(self->dso, self, filter); - - if (nr < 0) { - if (self->dso->has_build_id) { - char sbuild_id[BUILD_ID_SIZE * 2 + 1]; - - build_id__sprintf(self->dso->build_id, - sizeof(self->dso->build_id), - sbuild_id); - pr_warning("%s with build id %s not found", - self->dso->long_name, sbuild_id); - } else - pr_warning("Failed to open %s", - self->dso->long_name); - pr_warning(", continuing without symbols\n"); - return NULL; - } else if (nr == 0) { - const char *name = self->dso->long_name; - const size_t len = strlen(name); - const size_t real_len = len - sizeof(DSO__DELETED); - - if (len > sizeof(DSO__DELETED) && - strcmp(name + real_len + 1, DSO__DELETED) == 0) { - pr_warning("%.*s was updated, restart the long running apps that use it!\n", - (int)real_len, name); - } else { - pr_warning("no symbols found in %s, maybe install a debug package?\n", name); - } - return NULL; + const char *name = self->dso->long_name; + int nr; + + if (dso__loaded(self->dso, self->type)) + return 0; + + nr = dso__load(self->dso, self, session, filter); + if (nr < 0) { + if (self->dso->has_build_id) { + char sbuild_id[BUILD_ID_SIZE * 2 + 1]; + + build_id__sprintf(self->dso->build_id, + sizeof(self->dso->build_id), + sbuild_id); + pr_warning("%s with build id %s not found", + name, sbuild_id); + } else + pr_warning("Failed to open %s", name); + + pr_warning(", continuing without symbols\n"); + return -1; + } else if (nr == 0) { + const size_t len = strlen(name); + const size_t real_len = len - sizeof(DSO__DELETED); + + if (len > sizeof(DSO__DELETED) && + strcmp(name + real_len + 1, DSO__DELETED) == 0) { + pr_warning("%.*s was updated, restart the long " + "running apps that use it!\n", + (int)real_len, name); + } else { + pr_warning("no symbols found in %s, maybe install " + "a debug package?\n", name); } + + return -1; } - return self->dso->find_symbol(self->dso, self->type, addr); + return 0; +} + +struct symbol *map__find_symbol(struct map *self, struct perf_session *session, + u64 addr, symbol_filter_t filter) +{ + if (map__load(self, session, filter) < 0) + return NULL; + + return dso__find_symbol(self->dso, self->type, addr); +} + +struct symbol *map__find_symbol_by_name(struct map *self, const char *name, + struct perf_session *session, + symbol_filter_t filter) +{ + if (map__load(self, session, filter) < 0) + return NULL; + + if (!dso__sorted_by_name(self->dso, self->type)) + dso__sort_by_name(self->dso, self->type); + + return dso__find_symbol_by_name(self->dso, self->type, name); } struct map *map__clone(struct map *self) diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 9e5dbd66d34..e5bc0fb016b 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -197,7 +197,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) if (id == config) { closedir(evt_dir); closedir(sys_dir); - path = zalloc(sizeof(path)); + path = zalloc(sizeof(*path)); path->system = malloc(MAX_EVENT_LENGTH); if (!path->system) { free(path); @@ -467,7 +467,6 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags) while ((evt_ent = readdir(evt_dir))) { char event_opt[MAX_EVOPT_LEN + 1]; int len; - unsigned int rem = MAX_EVOPT_LEN; if (!strcmp(evt_ent->d_name, ".") || !strcmp(evt_ent->d_name, "..") @@ -475,20 +474,12 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags) || !strcmp(evt_ent->d_name, "filter")) continue; - len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s", sys_name, - evt_ent->d_name); + len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name, + evt_ent->d_name, flags ? ":" : "", + flags ?: ""); if (len < 0) return EVT_FAILED; - rem -= len; - if (flags) { - if (rem < strlen(flags) + 1) - return EVT_FAILED; - - strcat(event_opt, ":"); - strcat(event_opt, flags); - } - if (parse_events(NULL, event_opt, 0)) return EVT_FAILED; } diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 6d8af48c925..efebd5b476b 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -430,6 +430,9 @@ int usage_with_options_internal(const char * const *usagestr, pos = fprintf(stderr, " "); if (opts->short_name) pos += fprintf(stderr, "-%c", opts->short_name); + else + pos += fprintf(stderr, " "); + if (opts->long_name && opts->short_name) pos += fprintf(stderr, ", "); if (opts->long_name) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index cd7fbda5e2a..29465d44004 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -48,6 +48,9 @@ /* If there is no space to write, returns -E2BIG. */ static int e_snprintf(char *str, size_t size, const char *format, ...) + __attribute__((format(printf, 3, 4))); + +static int e_snprintf(char *str, size_t size, const char *format, ...) { int ret; va_list ap; @@ -59,6 +62,18 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) return ret; } +/* Check the name is good for event/group */ +static bool check_event_name(const char *name) +{ + if (!isalpha(*name) && *name != '_') + return false; + while (*++name != '\0') { + if (!isalpha(*name) && !isdigit(*name) && *name != '_') + return false; + } + return true; +} + /* Parse probepoint definition. */ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) { @@ -66,10 +81,26 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) char c, nc = 0; /* * <Syntax> - * perf probe SRC:LN - * perf probe FUNC[+OFFS|%return][@SRC] + * perf probe [EVENT=]SRC:LN + * perf probe [EVENT=]FUNC[+OFFS|%return][@SRC] + * + * TODO:Group name support */ + ptr = strchr(arg, '='); + if (ptr) { /* Event name */ + *ptr = '\0'; + tmp = ptr + 1; + ptr = strchr(arg, ':'); + if (ptr) /* Group name is not supported yet. */ + semantic_error("Group name is not supported yet."); + if (!check_event_name(arg)) + semantic_error("%s is bad for event name -it must " + "follow C symbol-naming rule.", arg); + pp->event = strdup(arg); + arg = tmp; + } + ptr = strpbrk(arg, ":+@%"); if (ptr) { nc = *ptr; @@ -147,10 +178,13 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) } /* Parse perf-probe event definition */ -int parse_perf_probe_event(const char *str, struct probe_point *pp) +void parse_perf_probe_event(const char *str, struct probe_point *pp, + bool *need_dwarf) { char **argv; - int argc, i, need_dwarf = 0; + int argc, i; + + *need_dwarf = false; argv = argv_split(str, &argc); if (!argv) @@ -161,7 +195,7 @@ int parse_perf_probe_event(const char *str, struct probe_point *pp) /* Parse probe point */ parse_perf_probe_probepoint(argv[0], pp); if (pp->file || pp->line) - need_dwarf = 1; + *need_dwarf = true; /* Copy arguments and ensure return probe has no C argument */ pp->nr_args = argc - 1; @@ -174,17 +208,15 @@ int parse_perf_probe_event(const char *str, struct probe_point *pp) if (pp->retprobe) semantic_error("You can't specify local" " variable for kretprobe"); - need_dwarf = 1; + *need_dwarf = true; } } argv_free(argv); - return need_dwarf; } /* Parse kprobe_events event into struct probe_point */ -void parse_trace_kprobe_event(const char *str, char **group, char **event, - struct probe_point *pp) +void parse_trace_kprobe_event(const char *str, struct probe_point *pp) { char pr; char *p; @@ -200,18 +232,17 @@ void parse_trace_kprobe_event(const char *str, char **group, char **event, /* Scan event and group name. */ ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", - &pr, (float *)(void *)group, (float *)(void *)event); + &pr, (float *)(void *)&pp->group, + (float *)(void *)&pp->event); if (ret != 3) semantic_error("Failed to parse event name: %s", argv[0]); - pr_debug("Group:%s Event:%s probe:%c\n", *group, *event, pr); - - if (!pp) - goto end; + pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr); pp->retprobe = (pr == 'r'); /* Scan function name and offset */ - ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, &pp->offset); + ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, + &pp->offset); if (ret == 1) pp->offset = 0; @@ -230,15 +261,15 @@ void parse_trace_kprobe_event(const char *str, char **group, char **event, die("Failed to copy argument."); } -end: argv_free(argv); } -int synthesize_perf_probe_event(struct probe_point *pp) +/* Synthesize only probe point (not argument) */ +int synthesize_perf_probe_point(struct probe_point *pp) { char *buf; char offs[64] = "", line[64] = ""; - int i, len, ret; + int ret; pp->probes[0] = buf = zalloc(MAX_CMDLEN); if (!buf) @@ -258,11 +289,25 @@ int synthesize_perf_probe_event(struct probe_point *pp) ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, offs, pp->retprobe ? "%return" : "", line); else - ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->file, line); - if (ret <= 0) - goto error; - len = ret; + ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); + if (ret <= 0) { +error: + free(pp->probes[0]); + pp->probes[0] = NULL; + } + return ret; +} + +int synthesize_perf_probe_event(struct probe_point *pp) +{ + char *buf; + int i, len, ret; + + len = synthesize_perf_probe_point(pp); + if (len < 0) + return 0; + buf = pp->probes[0]; for (i = 0; i < pp->nr_args; i++) { ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", pp->args[i]); @@ -275,6 +320,7 @@ int synthesize_perf_probe_event(struct probe_point *pp) return pp->found; error: free(pp->probes[0]); + pp->probes[0] = NULL; return ret; } @@ -304,6 +350,7 @@ int synthesize_trace_kprobe_event(struct probe_point *pp) return pp->found; error: free(pp->probes[0]); + pp->probes[0] = NULL; return ret; } @@ -363,6 +410,10 @@ static void clear_probe_point(struct probe_point *pp) { int i; + if (pp->event) + free(pp->event); + if (pp->group) + free(pp->group); if (pp->function) free(pp->function); if (pp->file) @@ -373,15 +424,33 @@ static void clear_probe_point(struct probe_point *pp) free(pp->args); for (i = 0; i < pp->found; i++) free(pp->probes[i]); - memset(pp, 0, sizeof(pp)); + memset(pp, 0, sizeof(*pp)); +} + +/* Show an event */ +static void show_perf_probe_event(const char *event, const char *place, + struct probe_point *pp) +{ + int i, ret; + char buf[128]; + + ret = e_snprintf(buf, 128, "%s:%s", pp->group, event); + if (ret < 0) + die("Failed to copy event: %s", strerror(-ret)); + printf(" %-40s (on %s", buf, place); + + if (pp->nr_args > 0) { + printf(" with"); + for (i = 0; i < pp->nr_args; i++) + printf(" %s", pp->args[i]); + } + printf(")\n"); } /* List up current perf-probe events */ void show_perf_probe_events(void) { - unsigned int i; int fd; - char *group, *event; struct probe_point pp; struct strlist *rawlist; struct str_node *ent; @@ -390,13 +459,12 @@ void show_perf_probe_events(void) rawlist = get_trace_kprobe_event_rawlist(fd); close(fd); - for (i = 0; i < strlist__nr_entries(rawlist); i++) { - ent = strlist__entry(rawlist, i); - parse_trace_kprobe_event(ent->s, &group, &event, &pp); - synthesize_perf_probe_event(&pp); - printf("[%s:%s]\t%s\n", group, event, pp.probes[0]); - free(group); - free(event); + strlist__for_each(ent, rawlist) { + parse_trace_kprobe_event(ent->s, &pp); + /* Synthesize only event probe point */ + synthesize_perf_probe_point(&pp); + /* Show an event */ + show_perf_probe_event(pp.event, pp.probes[0], &pp); clear_probe_point(&pp); } @@ -404,21 +472,27 @@ void show_perf_probe_events(void) } /* Get current perf-probe event names */ -static struct strlist *get_perf_event_names(int fd) +static struct strlist *get_perf_event_names(int fd, bool include_group) { - unsigned int i; - char *group, *event; + char buf[128]; struct strlist *sl, *rawlist; struct str_node *ent; + struct probe_point pp; + memset(&pp, 0, sizeof(pp)); rawlist = get_trace_kprobe_event_rawlist(fd); - sl = strlist__new(false, NULL); - for (i = 0; i < strlist__nr_entries(rawlist); i++) { - ent = strlist__entry(rawlist, i); - parse_trace_kprobe_event(ent->s, &group, &event, NULL); - strlist__add(sl, event); - free(group); + sl = strlist__new(true, NULL); + strlist__for_each(ent, rawlist) { + parse_trace_kprobe_event(ent->s, &pp); + if (include_group) { + if (e_snprintf(buf, 128, "%s:%s", pp.group, + pp.event) < 0) + die("Failed to copy group:event name."); + strlist__add(sl, buf); + } else + strlist__add(sl, pp.event); + clear_probe_point(&pp); } strlist__delete(rawlist); @@ -426,24 +500,36 @@ static struct strlist *get_perf_event_names(int fd) return sl; } -static int write_trace_kprobe_event(int fd, const char *buf) +static void write_trace_kprobe_event(int fd, const char *buf) { int ret; + pr_debug("Writing event: %s\n", buf); ret = write(fd, buf, strlen(buf)); if (ret <= 0) - die("Failed to create event."); - else - printf("Added new event: %s\n", buf); - - return ret; + die("Failed to write event: %s", strerror(errno)); } static void get_new_event_name(char *buf, size_t len, const char *base, - struct strlist *namelist) + struct strlist *namelist, bool allow_suffix) { int i, ret; - for (i = 0; i < MAX_EVENT_INDEX; i++) { + + /* Try no suffix */ + ret = e_snprintf(buf, len, "%s", base); + if (ret < 0) + die("snprintf() failed: %s", strerror(-ret)); + if (!strlist__has_entry(namelist, buf)) + return; + + if (!allow_suffix) { + pr_warning("Error: event \"%s\" already exists. " + "(Use -f to force duplicates.)\n", base); + die("Can't add new event."); + } + + /* Try to add suffix */ + for (i = 1; i < MAX_EVENT_INDEX; i++) { ret = e_snprintf(buf, len, "%s_%d", base, i); if (ret < 0) die("snprintf() failed: %s", strerror(-ret)); @@ -454,31 +540,138 @@ static void get_new_event_name(char *buf, size_t len, const char *base, die("Too many events are on the same function."); } -void add_trace_kprobe_events(struct probe_point *probes, int nr_probes) +void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, + bool force_add) { int i, j, fd; struct probe_point *pp; char buf[MAX_CMDLEN]; char event[64]; struct strlist *namelist; + bool allow_suffix; fd = open_kprobe_events(O_RDWR, O_APPEND); /* Get current event names */ - namelist = get_perf_event_names(fd); + namelist = get_perf_event_names(fd, false); for (j = 0; j < nr_probes; j++) { pp = probes + j; + if (!pp->event) + pp->event = strdup(pp->function); + if (!pp->group) + pp->group = strdup(PERFPROBE_GROUP); + DIE_IF(!pp->event || !pp->group); + /* If force_add is true, suffix search is allowed */ + allow_suffix = force_add; for (i = 0; i < pp->found; i++) { /* Get an unused new event name */ - get_new_event_name(event, 64, pp->function, namelist); + get_new_event_name(event, 64, pp->event, namelist, + allow_suffix); snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", pp->retprobe ? 'r' : 'p', - PERFPROBE_GROUP, event, + pp->group, event, pp->probes[i]); write_trace_kprobe_event(fd, buf); + printf("Added new event:\n"); + /* Get the first parameter (probe-point) */ + sscanf(pp->probes[i], "%s", buf); + show_perf_probe_event(event, buf, pp); /* Add added event name to namelist */ strlist__add(namelist, event); + /* + * Probes after the first probe which comes from same + * user input are always allowed to add suffix, because + * there might be several addresses corresponding to + * one code line. + */ + allow_suffix = true; + } + } + /* Show how to use the event. */ + printf("\nYou can now use it on all perf tools, such as:\n\n"); + printf("\tperf record -e %s:%s -a sleep 1\n\n", PERFPROBE_GROUP, event); + + strlist__delete(namelist); + close(fd); +} + +static void __del_trace_kprobe_event(int fd, struct str_node *ent) +{ + char *p; + char buf[128]; + + /* Convert from perf-probe event to trace-kprobe event */ + if (e_snprintf(buf, 128, "-:%s", ent->s) < 0) + die("Failed to copy event."); + p = strchr(buf + 2, ':'); + if (!p) + die("Internal error: %s should have ':' but not.", ent->s); + *p = '/'; + + write_trace_kprobe_event(fd, buf); + printf("Remove event: %s\n", ent->s); +} + +static void del_trace_kprobe_event(int fd, const char *group, + const char *event, struct strlist *namelist) +{ + char buf[128]; + struct str_node *ent, *n; + int found = 0; + + if (e_snprintf(buf, 128, "%s:%s", group, event) < 0) + die("Failed to copy event."); + + if (strpbrk(buf, "*?")) { /* Glob-exp */ + strlist__for_each_safe(ent, n, namelist) + if (strglobmatch(ent->s, buf)) { + found++; + __del_trace_kprobe_event(fd, ent); + strlist__remove(namelist, ent); + } + } else { + ent = strlist__find(namelist, buf); + if (ent) { + found++; + __del_trace_kprobe_event(fd, ent); + strlist__remove(namelist, ent); } } + if (found == 0) + pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf); +} + +void del_trace_kprobe_events(struct strlist *dellist) +{ + int fd; + const char *group, *event; + char *p, *str; + struct str_node *ent; + struct strlist *namelist; + + fd = open_kprobe_events(O_RDWR, O_APPEND); + /* Get current event names */ + namelist = get_perf_event_names(fd, true); + + strlist__for_each(ent, dellist) { + str = strdup(ent->s); + if (!str) + die("Failed to copy event."); + pr_debug("Parsing: %s\n", str); + p = strchr(str, ':'); + if (p) { + group = str; + *p = '\0'; + event = p + 1; + } else { + group = "*"; + event = str; + } + pr_debug("Group: %s, Event: %s\n", group, event); + del_trace_kprobe_event(fd, group, event, namelist); + free(str); + } + strlist__delete(namelist); close(fd); } + diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 0c6fe56fe38..7f1d499118c 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -1,15 +1,19 @@ #ifndef _PROBE_EVENT_H #define _PROBE_EVENT_H +#include <stdbool.h> #include "probe-finder.h" #include "strlist.h" -extern int parse_perf_probe_event(const char *str, struct probe_point *pp); +extern void parse_perf_probe_event(const char *str, struct probe_point *pp, + bool *need_dwarf); +extern int synthesize_perf_probe_point(struct probe_point *pp); extern int synthesize_perf_probe_event(struct probe_point *pp); -extern void parse_trace_kprobe_event(const char *str, char **group, - char **event, struct probe_point *pp); +extern void parse_trace_kprobe_event(const char *str, struct probe_point *pp); extern int synthesize_trace_kprobe_event(struct probe_point *pp); -extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes); +extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, + bool force_add); +extern void del_trace_kprobe_events(struct strlist *dellist); extern void show_perf_probe_events(void); /* Maximum index number of event-name postfix */ diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 293cdfc1b8c..4b852c0d16a 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -106,7 +106,7 @@ static int strtailcmp(const char *s1, const char *s2) { int i1 = strlen(s1); int i2 = strlen(s2); - while (--i1 > 0 && --i2 > 0) { + while (--i1 >= 0 && --i2 >= 0) { if (s1[i1] != s2[i2]) return s1[i1] - s2[i2]; } @@ -687,10 +687,8 @@ int find_probepoint(int fd, struct probe_point *pp) struct probe_finder pf = {.pp = pp}; ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error); - if (ret != DW_DLV_OK) { - pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO.\n"); + if (ret != DW_DLV_OK) return -ENOENT; - } pp->found = 0; while (++cu_number) { diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index bdebca6697d..a4086aaddb7 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -1,9 +1,9 @@ #ifndef _PROBE_FINDER_H #define _PROBE_FINDER_H -#define MAX_PATH_LEN 256 -#define MAX_PROBE_BUFFER 1024 -#define MAX_PROBES 128 +#define MAX_PATH_LEN 256 +#define MAX_PROBE_BUFFER 1024 +#define MAX_PROBES 128 static inline int is_c_varname(const char *name) { @@ -12,45 +12,53 @@ static inline int is_c_varname(const char *name) } struct probe_point { + char *event; /* Event name */ + char *group; /* Event group */ + /* Inputs */ - char *file; /* File name */ - int line; /* Line number */ + char *file; /* File name */ + int line; /* Line number */ - char *function; /* Function name */ - int offset; /* Offset bytes */ + char *function; /* Function name */ + int offset; /* Offset bytes */ - int nr_args; /* Number of arguments */ - char **args; /* Arguments */ + int nr_args; /* Number of arguments */ + char **args; /* Arguments */ - int retprobe; /* Return probe */ + int retprobe; /* Return probe */ /* Output */ - int found; /* Number of found probe points */ - char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ + int found; /* Number of found probe points */ + char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ }; #ifndef NO_LIBDWARF extern int find_probepoint(int fd, struct probe_point *pp); -#include <libdwarf/dwarf.h> -#include <libdwarf/libdwarf.h> +/* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */ +#ifndef _MIPS_SZLONG +# define _MIPS_SZLONG 0 +#endif + +#include <dwarf.h> +#include <libdwarf.h> struct probe_finder { - struct probe_point *pp; /* Target probe point */ + struct probe_point *pp; /* Target probe point */ /* For function searching */ - Dwarf_Addr addr; /* Address */ - Dwarf_Unsigned fno; /* File number */ - Dwarf_Unsigned lno; /* Line number */ - Dwarf_Off inl_offs; /* Inline offset */ - Dwarf_Die cu_die; /* Current CU */ + Dwarf_Addr addr; /* Address */ + Dwarf_Unsigned fno; /* File number */ + Dwarf_Unsigned lno; /* Line number */ + Dwarf_Off inl_offs; /* Inline offset */ + Dwarf_Die cu_die; /* Current CU */ /* For variable searching */ - Dwarf_Addr cu_base; /* Current CU base address */ - Dwarf_Locdesc fbloc; /* Location of Current Frame Base */ - const char *var; /* Current variable name */ - char *buf; /* Current output buffer */ - int len; /* Length of output buffer */ + Dwarf_Addr cu_base; /* Current CU base address */ + Dwarf_Locdesc fbloc; /* Location of Current Frame Base */ + const char *var; /* Current variable name */ + char *buf; /* Current output buffer */ + int len; /* Length of output buffer */ }; #endif /* NO_LIBDWARF */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c new file mode 100644 index 00000000000..ce3a6c8abe7 --- /dev/null +++ b/tools/perf/util/session.c @@ -0,0 +1,150 @@ +#include <linux/kernel.h> + +#include <unistd.h> +#include <sys/types.h> + +#include "session.h" +#include "sort.h" +#include "util.h" + +static int perf_session__open(struct perf_session *self, bool force) +{ + struct stat input_stat; + + self->fd = open(self->filename, O_RDONLY); + if (self->fd < 0) { + pr_err("failed to open file: %s", self->filename); + if (!strcmp(self->filename, "perf.data")) + pr_err(" (try 'perf record' first)"); + pr_err("\n"); + return -errno; + } + + if (fstat(self->fd, &input_stat) < 0) + goto out_close; + + if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { + pr_err("file %s not owned by current user or root\n", + self->filename); + goto out_close; + } + + if (!input_stat.st_size) { + pr_info("zero-sized file (%s), nothing to do!\n", + self->filename); + goto out_close; + } + + if (perf_header__read(&self->header, self->fd) < 0) { + pr_err("incompatible file format"); + goto out_close; + } + + self->size = input_stat.st_size; + return 0; + +out_close: + close(self->fd); + self->fd = -1; + return -1; +} + +struct perf_session *perf_session__new(const char *filename, int mode, bool force) +{ + size_t len = filename ? strlen(filename) + 1 : 0; + struct perf_session *self = zalloc(sizeof(*self) + len); + + if (self == NULL) + goto out; + + if (perf_header__init(&self->header) < 0) + goto out_free; + + memcpy(self->filename, filename, len); + self->threads = RB_ROOT; + self->last_match = NULL; + self->mmap_window = 32; + self->cwd = NULL; + self->cwdlen = 0; + map_groups__init(&self->kmaps); + + if (perf_session__create_kernel_maps(self) < 0) + goto out_delete; + + if (mode == O_RDONLY && perf_session__open(self, force) < 0) + goto out_delete; +out: + return self; +out_free: + free(self); + return NULL; +out_delete: + perf_session__delete(self); + return NULL; +} + +void perf_session__delete(struct perf_session *self) +{ + perf_header__exit(&self->header); + close(self->fd); + free(self->cwd); + free(self); +} + +static bool symbol__match_parent_regex(struct symbol *sym) +{ + if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) + return 1; + + return 0; +} + +struct symbol **perf_session__resolve_callchain(struct perf_session *self, + struct thread *thread, + struct ip_callchain *chain, + struct symbol **parent) +{ + u8 cpumode = PERF_RECORD_MISC_USER; + struct symbol **syms = NULL; + unsigned int i; + + if (symbol_conf.use_callchain) { + syms = calloc(chain->nr, sizeof(*syms)); + if (!syms) { + fprintf(stderr, "Can't allocate memory for symbols\n"); + exit(-1); + } + } + + for (i = 0; i < chain->nr; i++) { + u64 ip = chain->ips[i]; + struct addr_location al; + + if (ip >= PERF_CONTEXT_MAX) { + switch (ip) { + case PERF_CONTEXT_HV: + cpumode = PERF_RECORD_MISC_HYPERVISOR; break; + case PERF_CONTEXT_KERNEL: + cpumode = PERF_RECORD_MISC_KERNEL; break; + case PERF_CONTEXT_USER: + cpumode = PERF_RECORD_MISC_USER; break; + default: + break; + } + continue; + } + + thread__find_addr_location(thread, self, cpumode, + MAP__FUNCTION, ip, &al, NULL); + if (al.sym != NULL) { + if (sort__has_parent && !*parent && + symbol__match_parent_regex(al.sym)) + *parent = al.sym; + if (!symbol_conf.use_callchain) + break; + syms[i] = al.sym; + } + } + + return syms; +} diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h new file mode 100644 index 00000000000..32eaa1bada0 --- /dev/null +++ b/tools/perf/util/session.h @@ -0,0 +1,61 @@ +#ifndef __PERF_SESSION_H +#define __PERF_SESSION_H + +#include "event.h" +#include "header.h" +#include "thread.h" +#include <linux/rbtree.h> +#include "../../../include/linux/perf_event.h" + +struct ip_callchain; +struct thread; +struct symbol; + +struct perf_session { + struct perf_header header; + unsigned long size; + unsigned long mmap_window; + struct map_groups kmaps; + struct rb_root threads; + struct thread *last_match; + struct events_stats events_stats; + unsigned long event_total[PERF_RECORD_MAX]; + struct rb_root hists; + u64 sample_type; + int fd; + int cwdlen; + char *cwd; + char filename[0]; +}; + +typedef int (*event_op)(event_t *self, struct perf_session *session); + +struct perf_event_ops { + event_op process_sample_event; + event_op process_mmap_event; + event_op process_comm_event; + event_op process_fork_event; + event_op process_exit_event; + event_op process_lost_event; + event_op process_read_event; + event_op process_throttle_event; + event_op process_unthrottle_event; + int (*sample_type_check)(struct perf_session *session); + unsigned long total_unknown; + bool full_paths; +}; + +struct perf_session *perf_session__new(const char *filename, int mode, bool force); +void perf_session__delete(struct perf_session *self); + +int perf_session__process_events(struct perf_session *self, + struct perf_event_ops *event_ops); + +struct symbol **perf_session__resolve_callchain(struct perf_session *self, + struct thread *thread, + struct ip_callchain *chain, + struct symbol **parent); + +int perf_header__read_build_ids(int input, u64 offset, u64 file_size); + +#endif /* __PERF_SESSION_H */ diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index b490354d1b2..cb0f327de9e 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -288,3 +288,29 @@ int sort_dimension__add(const char *tok) return -ESRCH; } + +void setup_sorting(const char * const usagestr[], const struct option *opts) +{ + char *tmp, *tok, *str = strdup(sort_order); + + for (tok = strtok_r(str, ", ", &tmp); + tok; tok = strtok_r(NULL, ", ", &tmp)) { + if (sort_dimension__add(tok) < 0) { + error("Unknown --sort key: `%s'", tok); + usage_with_options(usagestr, opts); + } + } + + free(str); +} + +void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, + const char *list_name, FILE *fp) +{ + if (list && strlist__nr_entries(list) == 1) { + if (fp != NULL) + fprintf(fp, "# %s: %s\n", list_name, + strlist__entry(list, 0)->s); + self->elide = true; + } +} diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 333e664ff45..753f9ea99fb 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -49,9 +49,13 @@ struct hist_entry { struct symbol *sym; u64 ip; char level; - struct symbol *parent; + struct symbol *parent; struct callchain_node callchain; - struct rb_root sorted_chain; + union { + unsigned long position; + struct hist_entry *pair; + struct rb_root sorted_chain; + }; }; enum sort_type { @@ -81,6 +85,8 @@ struct sort_entry { extern struct sort_entry sort_thread; extern struct list_head hist_entry__sort_list; +void setup_sorting(const char * const usagestr[], const struct option *opts); + extern int repsep_fprintf(FILE *fp, const char *fmt, ...); extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int); extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int); @@ -95,5 +101,7 @@ extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); extern int sort_dimension__add(const char *); +void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, + const char *list_name, FILE *fp); #endif /* __PERF_SORT_H */ diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index f24a8cc933d..5352d7dccc6 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -226,3 +226,28 @@ fail: argv_free(argv); return NULL; } + +/* Glob expression pattern matching */ +bool strglobmatch(const char *str, const char *pat) +{ + while (*str && *pat && *pat != '*') { + if (*pat == '?') { + str++; + pat++; + } else + if (*str++ != *pat++) + return false; + } + /* Check wild card */ + if (*pat == '*') { + while (*pat == '*') + pat++; + if (!*pat) /* Tail wild card matches all */ + return true; + while (*str) + if (strglobmatch(str++, pat)) + return true; + } + return !*str && !*pat; +} + diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index bfecec265a1..02ede58c54b 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h @@ -1,6 +1,7 @@ #ifndef __PERF_STRING_H_ #define __PERF_STRING_H_ +#include <stdbool.h> #include "types.h" int hex2u64(const char *ptr, u64 *val); @@ -8,6 +9,7 @@ char *strxfrchar(char *s, char from, char to); s64 perf_atoll(const char *str); char **argv_split(const char *str, int *argcp); void argv_free(char **argv); +bool strglobmatch(const char *str, const char *pat); #define _STR(x) #x #define STR(x) _STR(x) diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index 7ad38171dc2..6783a204355 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c @@ -102,7 +102,7 @@ void strlist__remove(struct strlist *self, struct str_node *sn) str_node__delete(sn, self->dupstr); } -bool strlist__has_entry(struct strlist *self, const char *entry) +struct str_node *strlist__find(struct strlist *self, const char *entry) { struct rb_node **p = &self->entries.rb_node; struct rb_node *parent = NULL; @@ -120,10 +120,10 @@ bool strlist__has_entry(struct strlist *self, const char *entry) else if (rc < 0) p = &(*p)->rb_right; else - return true; + return sn; } - return false; + return NULL; } static int strlist__parse_list_entry(struct strlist *self, const char *s) diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h index cb4659306d7..3ba839007d2 100644 --- a/tools/perf/util/strlist.h +++ b/tools/perf/util/strlist.h @@ -23,7 +23,12 @@ int strlist__load(struct strlist *self, const char *filename); int strlist__add(struct strlist *self, const char *str); struct str_node *strlist__entry(const struct strlist *self, unsigned int idx); -bool strlist__has_entry(struct strlist *self, const char *entry); +struct str_node *strlist__find(struct strlist *self, const char *entry); + +static inline bool strlist__has_entry(struct strlist *self, const char *entry) +{ + return strlist__find(self, entry) != NULL; +} static inline bool strlist__empty(const struct strlist *self) { @@ -35,5 +40,39 @@ static inline unsigned int strlist__nr_entries(const struct strlist *self) return self->nr_entries; } +/* For strlist iteration */ +static inline struct str_node *strlist__first(struct strlist *self) +{ + struct rb_node *rn = rb_first(&self->entries); + return rn ? rb_entry(rn, struct str_node, rb_node) : NULL; +} +static inline struct str_node *strlist__next(struct str_node *sn) +{ + struct rb_node *rn; + if (!sn) + return NULL; + rn = rb_next(&sn->rb_node); + return rn ? rb_entry(rn, struct str_node, rb_node) : NULL; +} + +/** + * strlist_for_each - iterate over a strlist + * @pos: the &struct str_node to use as a loop cursor. + * @self: the &struct strlist for loop. + */ +#define strlist__for_each(pos, self) \ + for (pos = strlist__first(self); pos; pos = strlist__next(pos)) + +/** + * strlist_for_each_safe - iterate over a strlist safe against removal of + * str_node + * @pos: the &struct str_node to use as a loop cursor. + * @n: another &struct str_node to use as temporary storage. + * @self: the &struct strlist for loop. + */ +#define strlist__for_each_safe(pos, n, self) \ + for (pos = strlist__first(self), n = strlist__next(pos); pos;\ + pos = n, n = strlist__next(n)) + int strlist__parse_list(struct strlist *self, const char *s); #endif /* __PERF_STRLIST_H */ diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index fffcb937cdc..ab92763edb0 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1,5 +1,7 @@ #include "util.h" #include "../perf.h" +#include "session.h" +#include "sort.h" #include "string.h" #include "symbol.h" #include "thread.h" @@ -29,33 +31,50 @@ enum dso_origin { }; static void dsos__add(struct list_head *head, struct dso *dso); -static struct map *thread__find_map_by_name(struct thread *self, char *name); static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); -struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); static int dso__load_kernel_sym(struct dso *self, struct map *map, - struct thread *thread, symbol_filter_t filter); -unsigned int symbol__priv_size; + struct perf_session *session, symbol_filter_t filter); static int vmlinux_path__nr_entries; static char **vmlinux_path; -static struct symbol_conf symbol_conf__defaults = { +struct symbol_conf symbol_conf = { + .exclude_other = true, .use_modules = true, .try_vmlinux_path = true, }; -static struct thread kthread_mem; -struct thread *kthread = &kthread_mem; - bool dso__loaded(const struct dso *self, enum map_type type) { return self->loaded & (1 << type); } +bool dso__sorted_by_name(const struct dso *self, enum map_type type) +{ + return self->sorted_by_name & (1 << type); +} + static void dso__set_loaded(struct dso *self, enum map_type type) { self->loaded |= (1 << type); } +static void dso__set_sorted_by_name(struct dso *self, enum map_type type) +{ + self->sorted_by_name |= (1 << type); +} + +static bool symbol_type__is_a(char symbol_type, enum map_type map_type) +{ + switch (map_type) { + case MAP__FUNCTION: + return symbol_type == 'T' || symbol_type == 'W'; + case MAP__VARIABLE: + return symbol_type == 'D' || symbol_type == 'd'; + default: + return false; + } +} + static void symbols__fixup_end(struct rb_root *self) { struct rb_node *nd, *prevnd = rb_first(self); @@ -79,7 +98,7 @@ static void symbols__fixup_end(struct rb_root *self) curr->end = roundup(curr->start, 4096); } -static void __thread__fixup_maps_end(struct thread *self, enum map_type type) +static void __map_groups__fixup_end(struct map_groups *self, enum map_type type) { struct map *prev, *curr; struct rb_node *nd, *prevnd = rb_first(&self->maps[type]); @@ -102,23 +121,23 @@ static void __thread__fixup_maps_end(struct thread *self, enum map_type type) curr->end = ~0UL; } -static void thread__fixup_maps_end(struct thread *self) +static void map_groups__fixup_end(struct map_groups *self) { int i; for (i = 0; i < MAP__NR_TYPES; ++i) - __thread__fixup_maps_end(self, i); + __map_groups__fixup_end(self, i); } static struct symbol *symbol__new(u64 start, u64 len, const char *name) { size_t namelen = strlen(name) + 1; - struct symbol *self = zalloc(symbol__priv_size + + struct symbol *self = zalloc(symbol_conf.priv_size + sizeof(*self) + namelen); if (self == NULL) return NULL; - if (symbol__priv_size) - self = ((void *)self) + symbol__priv_size; + if (symbol_conf.priv_size) + self = ((void *)self) + symbol_conf.priv_size; self->start = start; self->end = len ? start + len - 1 : start; @@ -132,7 +151,7 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name) static void symbol__delete(struct symbol *self) { - free(((void *)self) - symbol__priv_size); + free(((void *)self) - symbol_conf.priv_size); } static size_t symbol__fprintf(struct symbol *self, FILE *fp) @@ -164,11 +183,11 @@ struct dso *dso__new(const char *name) dso__set_long_name(self, self->name); self->short_name = self->name; for (i = 0; i < MAP__NR_TYPES; ++i) - self->symbols[i] = RB_ROOT; - self->find_symbol = dso__find_symbol; + self->symbols[i] = self->symbol_names[i] = RB_ROOT; self->slen_calculated = 0; self->origin = DSO__ORIG_NOT_FOUND; self->loaded = 0; + self->sorted_by_name = 0; self->has_build_id = 0; } @@ -246,11 +265,85 @@ static struct symbol *symbols__find(struct rb_root *self, u64 ip) return NULL; } -struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr) +struct symbol_name_rb_node { + struct rb_node rb_node; + struct symbol sym; +}; + +static void symbols__insert_by_name(struct rb_root *self, struct symbol *sym) +{ + struct rb_node **p = &self->rb_node; + struct rb_node *parent = NULL; + struct symbol_name_rb_node *symn = ((void *)sym) - sizeof(*parent), *s; + + while (*p != NULL) { + parent = *p; + s = rb_entry(parent, struct symbol_name_rb_node, rb_node); + if (strcmp(sym->name, s->sym.name) < 0) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&symn->rb_node, parent, p); + rb_insert_color(&symn->rb_node, self); +} + +static void symbols__sort_by_name(struct rb_root *self, struct rb_root *source) +{ + struct rb_node *nd; + + for (nd = rb_first(source); nd; nd = rb_next(nd)) { + struct symbol *pos = rb_entry(nd, struct symbol, rb_node); + symbols__insert_by_name(self, pos); + } +} + +static struct symbol *symbols__find_by_name(struct rb_root *self, const char *name) +{ + struct rb_node *n; + + if (self == NULL) + return NULL; + + n = self->rb_node; + + while (n) { + struct symbol_name_rb_node *s; + int cmp; + + s = rb_entry(n, struct symbol_name_rb_node, rb_node); + cmp = strcmp(name, s->sym.name); + + if (cmp < 0) + n = n->rb_left; + else if (cmp > 0) + n = n->rb_right; + else + return &s->sym; + } + + return NULL; +} + +struct symbol *dso__find_symbol(struct dso *self, + enum map_type type, u64 addr) { return symbols__find(&self->symbols[type], addr); } +struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, + const char *name) +{ + return symbols__find_by_name(&self->symbol_names[type], name); +} + +void dso__sort_by_name(struct dso *self, enum map_type type) +{ + dso__set_sorted_by_name(self, type); + return symbols__sort_by_name(&self->symbol_names[type], + &self->symbols[type]); +} + int build_id__sprintf(u8 *self, int len, char *bf) { char *bid = bf; @@ -327,10 +420,7 @@ static int dso__load_all_kallsyms(struct dso *self, struct map *map) continue; symbol_type = toupper(line[len]); - /* - * We're interested only in code ('T'ext) - */ - if (symbol_type != 'T' && symbol_type != 'W') + if (!symbol_type__is_a(symbol_type, map->type)) continue; symbol_name = line + len + 2; @@ -364,8 +454,8 @@ out_failure: * kernel range is broken in several maps, named [kernel].N, as we don't have * the original ELF section names vmlinux have. */ -static int dso__split_kallsyms(struct dso *self, struct map *map, struct thread *thread, - symbol_filter_t filter) +static int dso__split_kallsyms(struct dso *self, struct map *map, + struct perf_session *session, symbol_filter_t filter) { struct map *curr_map = map; struct symbol *pos; @@ -382,13 +472,13 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, struct thread module = strchr(pos->name, '\t'); if (module) { - if (!thread->use_modules) + if (!symbol_conf.use_modules) goto discard_symbol; *module++ = '\0'; if (strcmp(self->name, module)) { - curr_map = thread__find_map_by_name(thread, module); + curr_map = map_groups__find_by_name(&session->kmaps, map->type, module); if (curr_map == NULL) { pr_debug("/proc/{kallsyms,modules} " "inconsistency!\n"); @@ -419,7 +509,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, struct thread } curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; - __thread__insert_map(thread, curr_map); + map_groups__insert(&session->kmaps, curr_map); ++kernel_range; } @@ -440,7 +530,7 @@ discard_symbol: rb_erase(&pos->rb_node, root); static int dso__load_kallsyms(struct dso *self, struct map *map, - struct thread *thread, symbol_filter_t filter) + struct perf_session *session, symbol_filter_t filter) { if (dso__load_all_kallsyms(self, map) < 0) return -1; @@ -448,14 +538,7 @@ static int dso__load_kallsyms(struct dso *self, struct map *map, symbols__fixup_end(&self->symbols[map->type]); self->origin = DSO__ORIG_KERNEL; - return dso__split_kallsyms(self, map, thread, filter); -} - -size_t kernel_maps__fprintf(FILE *fp) -{ - size_t printed = fprintf(fp, "Kernel maps:\n"); - printed += thread__fprintf_maps(kthread, fp); - return printed + fprintf(fp, "END kernel maps\n"); + return dso__split_kallsyms(self, map, session, filter); } static int dso__load_perf_map(struct dso *self, struct map *map, @@ -544,6 +627,13 @@ static inline int elf_sym__is_function(const GElf_Sym *sym) sym->st_shndx != SHN_UNDEF; } +static inline bool elf_sym__is_object(const GElf_Sym *sym) +{ + return elf_sym__type(sym) == STT_OBJECT && + sym->st_name != 0 && + sym->st_shndx != SHN_UNDEF; +} + static inline int elf_sym__is_label(const GElf_Sym *sym) { return elf_sym__type(sym) == STT_NOTYPE && @@ -564,6 +654,12 @@ static inline int elf_sec__is_text(const GElf_Shdr *shdr, return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; } +static inline bool elf_sec__is_data(const GElf_Shdr *shdr, + const Elf_Data *secstrs) +{ + return strstr(elf_sec__name(shdr, secstrs), "data") != NULL; +} + static inline const char *elf_sym__name(const GElf_Sym *sym, const Elf_Data *symstrs) { @@ -744,8 +840,32 @@ out: return 0; } +static bool elf_sym__is_a(GElf_Sym *self, enum map_type type) +{ + switch (type) { + case MAP__FUNCTION: + return elf_sym__is_function(self); + case MAP__VARIABLE: + return elf_sym__is_object(self); + default: + return false; + } +} + +static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type) +{ + switch (type) { + case MAP__FUNCTION: + return elf_sec__is_text(self, secstrs); + case MAP__VARIABLE: + return elf_sec__is_data(self, secstrs); + default: + return false; + } +} + static int dso__load_sym(struct dso *self, struct map *map, - struct thread *thread, const char *name, int fd, + struct perf_session *session, const char *name, int fd, symbol_filter_t filter, int kernel, int kmodule) { struct map *curr_map = map; @@ -818,7 +938,7 @@ static int dso__load_sym(struct dso *self, struct map *map, int is_label = elf_sym__is_label(&sym); const char *section_name; - if (!is_label && !elf_sym__is_function(&sym)) + if (!is_label && !elf_sym__is_a(&sym, map->type)) continue; sec = elf_getscn(elf, sym.st_shndx); @@ -827,7 +947,7 @@ static int dso__load_sym(struct dso *self, struct map *map, gelf_getshdr(sec, &shdr); - if (is_label && !elf_sec__is_text(&shdr, secstrs)) + if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) continue; elf_name = elf_sym__name(&sym, symstrs); @@ -849,7 +969,7 @@ static int dso__load_sym(struct dso *self, struct map *map, snprintf(dso_name, sizeof(dso_name), "%s%s", self->short_name, section_name); - curr_map = thread__find_map_by_name(thread, dso_name); + curr_map = map_groups__find_by_name(&session->kmaps, map->type, dso_name); if (curr_map == NULL) { u64 start = sym.st_value; @@ -868,7 +988,7 @@ static int dso__load_sym(struct dso *self, struct map *map, curr_map->map_ip = identity__map_ip; curr_map->unmap_ip = identity__map_ip; curr_dso->origin = DSO__ORIG_KERNEL; - __thread__insert_map(kthread, curr_map); + map_groups__insert(&session->kmaps, curr_map); dsos__add(&dsos__kernel, curr_dso); } else curr_dso = curr_map->dso; @@ -938,8 +1058,9 @@ static bool __dsos__read_build_ids(struct list_head *head) bool dsos__read_build_ids(void) { - return __dsos__read_build_ids(&dsos__kernel) || - __dsos__read_build_ids(&dsos__user); + bool kbuildids = __dsos__read_build_ids(&dsos__kernel), + ubuildids = __dsos__read_build_ids(&dsos__user); + return kbuildids || ubuildids; } /* @@ -1082,7 +1203,8 @@ char dso__symtab_origin(const struct dso *self) return origin[self->origin]; } -int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) +int dso__load(struct dso *self, struct map *map, struct perf_session *session, + symbol_filter_t filter) { int size = PATH_MAX; char *name; @@ -1093,7 +1215,7 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) dso__set_loaded(self, map->type); if (self->kernel) - return dso__load_kernel_sym(self, map, kthread, filter); + return dso__load_kernel_sym(self, map, session, filter); name = malloc(size); if (!name) @@ -1179,11 +1301,12 @@ out: return ret; } -static struct map *thread__find_map_by_name(struct thread *self, char *name) +struct map *map_groups__find_by_name(struct map_groups *self, + enum map_type type, const char *name) { struct rb_node *nd; - for (nd = rb_first(&self->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { + for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { struct map *map = rb_entry(nd, struct map, rb_node); if (map->dso && strcmp(map->dso->name, name) == 0) @@ -1193,7 +1316,7 @@ static struct map *thread__find_map_by_name(struct thread *self, char *name) return NULL; } -static int dsos__set_modules_path_dir(char *dirname) +static int perf_session__set_modules_path_dir(struct perf_session *self, char *dirname) { struct dirent *dent; DIR *dir = opendir(dirname); @@ -1213,7 +1336,7 @@ static int dsos__set_modules_path_dir(char *dirname) snprintf(path, sizeof(path), "%s/%s", dirname, dent->d_name); - if (dsos__set_modules_path_dir(path) < 0) + if (perf_session__set_modules_path_dir(self, path) < 0) goto failure; } else { char *dot = strrchr(dent->d_name, '.'), @@ -1227,7 +1350,7 @@ static int dsos__set_modules_path_dir(char *dirname) (int)(dot - dent->d_name), dent->d_name); strxfrchar(dso_name, '-', '_'); - map = thread__find_map_by_name(kthread, dso_name); + map = map_groups__find_by_name(&self->kmaps, MAP__FUNCTION, dso_name); if (map == NULL) continue; @@ -1247,7 +1370,7 @@ failure: return -1; } -static int dsos__set_modules_path(void) +static int perf_session__set_modules_path(struct perf_session *self) { struct utsname uts; char modules_path[PATH_MAX]; @@ -1258,7 +1381,7 @@ static int dsos__set_modules_path(void) snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel", uts.release); - return dsos__set_modules_path_dir(modules_path); + return perf_session__set_modules_path_dir(self, modules_path); } /* @@ -1280,7 +1403,7 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) return self; } -static int thread__create_module_maps(struct thread *self) +static int perf_session__create_module_maps(struct perf_session *self) { char *line = NULL; size_t n; @@ -1337,14 +1460,14 @@ static int thread__create_module_maps(struct thread *self) dso->has_build_id = true; dso->origin = DSO__ORIG_KMODULE; - __thread__insert_map(self, map); + map_groups__insert(&self->kmaps, map); dsos__add(&dsos__kernel, dso); } free(line); fclose(file); - return dsos__set_modules_path(); + return perf_session__set_modules_path(self); out_delete_line: free(line); @@ -1352,7 +1475,8 @@ out_failure: return -1; } -static int dso__load_vmlinux(struct dso *self, struct map *map, struct thread *thread, +static int dso__load_vmlinux(struct dso *self, struct map *map, + struct perf_session *session, const char *vmlinux, symbol_filter_t filter) { int err = -1, fd; @@ -1386,14 +1510,14 @@ static int dso__load_vmlinux(struct dso *self, struct map *map, struct thread *t return -1; dso__set_loaded(self, map->type); - err = dso__load_sym(self, map, thread, self->long_name, fd, filter, 1, 0); + err = dso__load_sym(self, map, session, self->long_name, fd, filter, 1, 0); close(fd); return err; } static int dso__load_kernel_sym(struct dso *self, struct map *map, - struct thread *thread, symbol_filter_t filter) + struct perf_session *session, symbol_filter_t filter) { int err; bool is_kallsyms; @@ -1403,7 +1527,7 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map, pr_debug("Looking at the vmlinux_path (%d entries long)\n", vmlinux_path__nr_entries); for (i = 0; i < vmlinux_path__nr_entries; ++i) { - err = dso__load_vmlinux(self, map, thread, + err = dso__load_vmlinux(self, map, session, vmlinux_path[i], filter); if (err > 0) { pr_debug("Using %s for symbols\n", @@ -1419,12 +1543,12 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map, if (is_kallsyms) goto do_kallsyms; - err = dso__load_vmlinux(self, map, thread, self->long_name, filter); + err = dso__load_vmlinux(self, map, session, self->long_name, filter); if (err <= 0) { pr_info("The file %s cannot be used, " "trying to use /proc/kallsyms...", self->long_name); do_kallsyms: - err = dso__load_kallsyms(self, map, thread, filter); + err = dso__load_kallsyms(self, map, session, filter); if (err > 0 && !is_kallsyms) dso__set_long_name(self, strdup("[kernel.kallsyms]")); } @@ -1507,42 +1631,59 @@ size_t dsos__fprintf_buildid(FILE *fp) __dsos__fprintf_buildid(&dsos__user, fp)); } -static int thread__create_kernel_map(struct thread *self, const char *vmlinux) +static struct dso *dsos__create_kernel( const char *vmlinux) { - struct map *kmap; struct dso *kernel = dso__new(vmlinux ?: "[kernel.kallsyms]"); if (kernel == NULL) - return -1; - - kmap = map__new2(0, kernel, MAP__FUNCTION); - if (kmap == NULL) - goto out_delete_kernel_dso; + return NULL; - kmap->map_ip = kmap->unmap_ip = identity__map_ip; kernel->short_name = "[kernel]"; kernel->kernel = 1; vdso = dso__new("[vdso]"); if (vdso == NULL) - goto out_delete_kernel_map; + goto out_delete_kernel_dso; dso__set_loaded(vdso, MAP__FUNCTION); if (sysfs__read_build_id("/sys/kernel/notes", kernel->build_id, sizeof(kernel->build_id)) == 0) kernel->has_build_id = true; - __thread__insert_map(self, kmap); dsos__add(&dsos__kernel, kernel); dsos__add(&dsos__user, vdso); - return 0; + return kernel; -out_delete_kernel_map: - map__delete(kmap); out_delete_kernel_dso: dso__delete(kernel); - return -1; + return NULL; +} + +static int map_groups__create_kernel_maps(struct map_groups *self, const char *vmlinux) +{ + struct map *functions, *variables; + struct dso *kernel = dsos__create_kernel(vmlinux); + + if (kernel == NULL) + return -1; + + functions = map__new2(0, kernel, MAP__FUNCTION); + if (functions == NULL) + return -1; + + variables = map__new2(0, kernel, MAP__VARIABLE); + if (variables == NULL) { + map__delete(functions); + return -1; + } + + functions->map_ip = functions->unmap_ip = + variables->map_ip = variables->unmap_ip = identity__map_ip; + map_groups__insert(self, functions); + map_groups__insert(self, variables); + + return 0; } static void vmlinux_path__exit(void) @@ -1600,29 +1741,69 @@ out_fail: return -1; } -int symbol__init(struct symbol_conf *conf) +static int setup_list(struct strlist **list, const char *list_str, + const char *list_name) { - const struct symbol_conf *pconf = conf ?: &symbol_conf__defaults; + if (list_str == NULL) + return 0; + *list = strlist__new(true, list_str); + if (!*list) { + pr_err("problems parsing %s list\n", list_name); + return -1; + } + return 0; +} + +int symbol__init(void) +{ elf_version(EV_CURRENT); - symbol__priv_size = pconf->priv_size; - thread__init(kthread, 0); + if (symbol_conf.sort_by_name) + symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - + sizeof(struct symbol)); - if (pconf->try_vmlinux_path && vmlinux_path__init() < 0) + if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0) return -1; - if (thread__create_kernel_map(kthread, pconf->vmlinux_name) < 0) { - vmlinux_path__exit(); + if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { + pr_err("'.' is the only non valid --field-separator argument\n"); return -1; } - kthread->use_modules = pconf->use_modules; - if (pconf->use_modules && thread__create_module_maps(kthread) < 0) - pr_debug("Failed to load list of modules in use, " - "continuing...\n"); + if (setup_list(&symbol_conf.dso_list, + symbol_conf.dso_list_str, "dso") < 0) + return -1; + + if (setup_list(&symbol_conf.comm_list, + symbol_conf.comm_list_str, "comm") < 0) + goto out_free_dso_list; + + if (setup_list(&symbol_conf.sym_list, + symbol_conf.sym_list_str, "symbol") < 0) + goto out_free_comm_list; + + return 0; + +out_free_dso_list: + strlist__delete(symbol_conf.dso_list); +out_free_comm_list: + strlist__delete(symbol_conf.comm_list); + return -1; +} + +int perf_session__create_kernel_maps(struct perf_session *self) +{ + if (map_groups__create_kernel_maps(&self->kmaps, + symbol_conf.vmlinux_name) < 0) + return -1; + + if (symbol_conf.use_modules && + perf_session__create_module_maps(self) < 0) + pr_debug("Failed to load list of modules for session %s, " + "continuing...\n", self->filename); /* * Now that we have all the maps created, just set the ->end of them: */ - thread__fixup_maps_end(kthread); + map_groups__fixup_end(&self->kmaps); return 0; } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 17003efa0b3..8aded2356f7 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -49,18 +49,32 @@ struct symbol { char name[0]; }; +struct strlist; + struct symbol_conf { unsigned short priv_size; bool try_vmlinux_path, - use_modules; - const char *vmlinux_name; + use_modules, + sort_by_name, + show_nr_samples, + use_callchain, + exclude_other; + const char *vmlinux_name, + *field_sep; + char *dso_list_str, + *comm_list_str, + *sym_list_str, + *col_width_list_str; + struct strlist *dso_list, + *comm_list, + *sym_list; }; -extern unsigned int symbol__priv_size; +extern struct symbol_conf symbol_conf; static inline void *symbol__priv(struct symbol *self) { - return ((void *)self) - symbol__priv_size; + return ((void *)self) - symbol_conf.priv_size; } struct addr_location { @@ -69,18 +83,19 @@ struct addr_location { struct symbol *sym; u64 addr; char level; + bool filtered; }; struct dso { struct list_head node; struct rb_root symbols[MAP__NR_TYPES]; - struct symbol *(*find_symbol)(struct dso *self, - enum map_type type, u64 addr); + struct rb_root symbol_names[MAP__NR_TYPES]; u8 adjust_symbols:1; u8 slen_calculated:1; u8 has_build_id:1; u8 kernel:1; unsigned char origin; + u8 sorted_by_name; u8 loaded; u8 build_id[BUILD_ID_SIZE]; u16 long_name_len; @@ -93,9 +108,15 @@ struct dso *dso__new(const char *name); void dso__delete(struct dso *self); bool dso__loaded(const struct dso *self, enum map_type type); +bool dso__sorted_by_name(const struct dso *self, enum map_type type); + +void dso__sort_by_name(struct dso *self, enum map_type type); + +struct perf_session; struct dso *dsos__findnew(const char *name); -int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); +int dso__load(struct dso *self, struct map *map, struct perf_session *session, + symbol_filter_t filter); void dsos__fprintf(FILE *fp); size_t dsos__fprintf_buildid(FILE *fp); @@ -103,18 +124,18 @@ size_t dso__fprintf_buildid(struct dso *self, FILE *fp); size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); char dso__symtab_origin(const struct dso *self); void dso__set_build_id(struct dso *self, void *build_id); +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); int filename__read_build_id(const char *filename, void *bf, size_t size); int sysfs__read_build_id(const char *filename, void *bf, size_t size); bool dsos__read_build_ids(void); int build_id__sprintf(u8 *self, int len, char *bf); -size_t kernel_maps__fprintf(FILE *fp); - -int symbol__init(struct symbol_conf *conf); +int symbol__init(void); +int perf_session__create_kernel_maps(struct perf_session *self); -struct thread; -struct thread *kthread; extern struct list_head dsos__user, dsos__kernel; extern struct dso *vdso; #endif /* __PERF_SYMBOL */ diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 603f5610861..4a08dcf50b6 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -2,18 +2,14 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> +#include "session.h" #include "thread.h" #include "util.h" #include "debug.h" -static struct rb_root threads; -static struct thread *last_match; - -void thread__init(struct thread *self, pid_t pid) +void map_groups__init(struct map_groups *self) { int i; - self->pid = pid; - self->comm = NULL; for (i = 0; i < MAP__NR_TYPES; ++i) { self->maps[i] = RB_ROOT; INIT_LIST_HEAD(&self->removed_maps[i]); @@ -25,7 +21,8 @@ static struct thread *thread__new(pid_t pid) struct thread *self = zalloc(sizeof(*self)); if (self != NULL) { - thread__init(self, pid); + map_groups__init(&self->mg); + self->pid = pid; self->comm = malloc(32); if (self->comm) snprintf(self->comm, 32, ":%d", self->pid); @@ -55,10 +52,11 @@ int thread__comm_len(struct thread *self) static const char *map_type__name[MAP__NR_TYPES] = { [MAP__FUNCTION] = "Functions", + [MAP__VARIABLE] = "Variables", }; -static size_t __thread__fprintf_maps(struct thread *self, - enum map_type type, FILE *fp) +static size_t __map_groups__fprintf_maps(struct map_groups *self, + enum map_type type, FILE *fp) { size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); struct rb_node *nd; @@ -76,16 +74,16 @@ static size_t __thread__fprintf_maps(struct thread *self, return printed; } -size_t thread__fprintf_maps(struct thread *self, FILE *fp) +size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp) { size_t printed = 0, i; for (i = 0; i < MAP__NR_TYPES; ++i) - printed += __thread__fprintf_maps(self, i, fp); + printed += __map_groups__fprintf_maps(self, i, fp); return printed; } -static size_t __thread__fprintf_removed_maps(struct thread *self, - enum map_type type, FILE *fp) +static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, + enum map_type type, FILE *fp) { struct map *pos; size_t printed = 0; @@ -101,25 +99,30 @@ static size_t __thread__fprintf_removed_maps(struct thread *self, return printed; } -static size_t thread__fprintf_removed_maps(struct thread *self, FILE *fp) +static size_t map_groups__fprintf_removed_maps(struct map_groups *self, FILE *fp) { size_t printed = 0, i; for (i = 0; i < MAP__NR_TYPES; ++i) - printed += __thread__fprintf_removed_maps(self, i, fp); + printed += __map_groups__fprintf_removed_maps(self, i, fp); return printed; } -static size_t thread__fprintf(struct thread *self, FILE *fp) +static size_t map_groups__fprintf(struct map_groups *self, FILE *fp) { - size_t printed = fprintf(fp, "Thread %d %s\n", self->pid, self->comm); - printed += thread__fprintf_removed_maps(self, fp); + size_t printed = map_groups__fprintf_maps(self, fp); printed += fprintf(fp, "Removed maps:\n"); - return printed + thread__fprintf_removed_maps(self, fp); + return printed + map_groups__fprintf_removed_maps(self, fp); +} + +static size_t thread__fprintf(struct thread *self, FILE *fp) +{ + return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) + + map_groups__fprintf(&self->mg, fp); } -struct thread *threads__findnew(pid_t pid) +struct thread *perf_session__findnew(struct perf_session *self, pid_t pid) { - struct rb_node **p = &threads.rb_node; + struct rb_node **p = &self->threads.rb_node; struct rb_node *parent = NULL; struct thread *th; @@ -128,15 +131,15 @@ struct thread *threads__findnew(pid_t pid) * so most of the time we dont have to look up * the full rbtree: */ - if (last_match && last_match->pid == pid) - return last_match; + if (self->last_match && self->last_match->pid == pid) + return self->last_match; while (*p != NULL) { parent = *p; th = rb_entry(parent, struct thread, rb_node); if (th->pid == pid) { - last_match = th; + self->last_match = th; return th; } @@ -149,26 +152,15 @@ struct thread *threads__findnew(pid_t pid) th = thread__new(pid); if (th != NULL) { rb_link_node(&th->rb_node, parent, p); - rb_insert_color(&th->rb_node, &threads); - last_match = th; + rb_insert_color(&th->rb_node, &self->threads); + self->last_match = th; } return th; } -struct thread *register_idle_thread(void) -{ - struct thread *thread = threads__findnew(0); - - if (!thread || thread__set_comm(thread, "swapper")) { - fprintf(stderr, "problem inserting idle task.\n"); - exit(-1); - } - - return thread; -} - -static void thread__remove_overlappings(struct thread *self, struct map *map) +static void map_groups__remove_overlappings(struct map_groups *self, + struct map *map) { struct rb_root *root = &self->maps[map->type]; struct rb_node *next = rb_first(root); @@ -238,12 +230,15 @@ struct map *maps__find(struct rb_root *maps, u64 ip) void thread__insert_map(struct thread *self, struct map *map) { - thread__remove_overlappings(self, map); - maps__insert(&self->maps[map->type], map); + map_groups__remove_overlappings(&self->mg, map); + map_groups__insert(&self->mg, map); } -static int thread__clone_maps(struct thread *self, struct thread *parent, - enum map_type type) +/* + * XXX This should not really _copy_ te maps, but refcount them. + */ +static int map_groups__clone(struct map_groups *self, + struct map_groups *parent, enum map_type type) { struct rb_node *nd; for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { @@ -251,7 +246,7 @@ static int thread__clone_maps(struct thread *self, struct thread *parent, struct map *new = map__clone(map); if (new == NULL) return -ENOMEM; - thread__insert_map(self, new); + map_groups__insert(self, new); } return 0; } @@ -267,17 +262,17 @@ int thread__fork(struct thread *self, struct thread *parent) return -ENOMEM; for (i = 0; i < MAP__NR_TYPES; ++i) - if (thread__clone_maps(self, parent, i) < 0) + if (map_groups__clone(&self->mg, &parent->mg, i) < 0) return -ENOMEM; return 0; } -size_t threads__fprintf(FILE *fp) +size_t perf_session__fprintf(struct perf_session *self, FILE *fp) { size_t ret = 0; struct rb_node *nd; - for (nd = rb_first(&threads); nd; nd = rb_next(nd)) { + for (nd = rb_first(&self->threads); nd; nd = rb_next(nd)) { struct thread *pos = rb_entry(nd, struct thread, rb_node); ret += thread__fprintf(pos, fp); @@ -286,14 +281,15 @@ size_t threads__fprintf(FILE *fp) return ret; } -struct symbol *thread__find_symbol(struct thread *self, - enum map_type type, u64 addr, - symbol_filter_t filter) +struct symbol *map_groups__find_symbol(struct map_groups *self, + struct perf_session *session, + enum map_type type, u64 addr, + symbol_filter_t filter) { - struct map *map = thread__find_map(self, type, addr); + struct map *map = map_groups__find(self, type, addr); if (map != NULL) - return map__find_symbol(map, map->map_ip(map, addr), filter); + return map__find_symbol(map, session, map->map_ip(map, addr), filter); return NULL; } diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 686d6e914d9..c206f72c888 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -5,52 +5,66 @@ #include <unistd.h> #include "symbol.h" -struct thread { - struct rb_node rb_node; +struct map_groups { struct rb_root maps[MAP__NR_TYPES]; struct list_head removed_maps[MAP__NR_TYPES]; +}; + +struct thread { + struct rb_node rb_node; + struct map_groups mg; pid_t pid; - bool use_modules; char shortname[3]; char *comm; int comm_len; }; -void thread__init(struct thread *self, pid_t pid); +void map_groups__init(struct map_groups *self); int thread__set_comm(struct thread *self, const char *comm); int thread__comm_len(struct thread *self); -struct thread *threads__findnew(pid_t pid); -struct thread *register_idle_thread(void); +struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); void thread__insert_map(struct thread *self, struct map *map); int thread__fork(struct thread *self, struct thread *parent); -size_t thread__fprintf_maps(struct thread *self, FILE *fp); -size_t threads__fprintf(FILE *fp); +size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp); +size_t perf_session__fprintf(struct perf_session *self, FILE *fp); void maps__insert(struct rb_root *maps, struct map *map); struct map *maps__find(struct rb_root *maps, u64 addr); -static inline struct map *thread__find_map(struct thread *self, +static inline void map_groups__insert(struct map_groups *self, struct map *map) +{ + maps__insert(&self->maps[map->type], map); +} + +static inline struct map *map_groups__find(struct map_groups *self, enum map_type type, u64 addr) { - return self ? maps__find(&self->maps[type], addr) : NULL; + return maps__find(&self->maps[type], addr); } -static inline void __thread__insert_map(struct thread *self, struct map *map) +static inline struct map *thread__find_map(struct thread *self, + enum map_type type, u64 addr) { - maps__insert(&self->maps[map->type], map); + return self ? map_groups__find(&self->mg, type, addr) : NULL; } -void thread__find_addr_location(struct thread *self, u8 cpumode, +void thread__find_addr_location(struct thread *self, + struct perf_session *session, u8 cpumode, enum map_type type, u64 addr, struct addr_location *al, symbol_filter_t filter); -struct symbol *thread__find_symbol(struct thread *self, - enum map_type type, u64 addr, - symbol_filter_t filter); +struct symbol *map_groups__find_symbol(struct map_groups *self, + struct perf_session *session, + enum map_type type, u64 addr, + symbol_filter_t filter); static inline struct symbol * -thread__find_function(struct thread *self, u64 addr, symbol_filter_t filter) +map_groups__find_function(struct map_groups *self, struct perf_session *session, + u64 addr, symbol_filter_t filter) { - return thread__find_symbol(self, MAP__FUNCTION, addr, filter); + return map_groups__find_symbol(self, session, MAP__FUNCTION, addr, filter); } + +struct map *map_groups__find_by_name(struct map_groups *self, + enum map_type type, const char *name); #endif /* __PERF_THREAD_H */ diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 0302405aa2c..c5c32be040b 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -177,7 +177,7 @@ void parse_proc_kallsyms(char *file, unsigned int size __unused) func_count++; } - func_list = malloc_or_die(sizeof(*func_list) * func_count + 1); + func_list = malloc_or_die(sizeof(*func_list) * (func_count + 1)); i = 0; while (list) { @@ -1477,7 +1477,7 @@ process_fields(struct event *event, struct print_flag_sym **list, char **tok) goto out_free; field = malloc_or_die(sizeof(*field)); - memset(field, 0, sizeof(field)); + memset(field, 0, sizeof(*field)); value = arg_eval(arg); field->value = strdup(value); diff --git a/tools/perf/util/trace-event-perl.c b/tools/perf/util/trace-event-perl.c index 51e833fd58c..6d6d76b8a21 100644 --- a/tools/perf/util/trace-event-perl.c +++ b/tools/perf/util/trace-event-perl.c @@ -32,9 +32,6 @@ void xs_init(pTHX); -void boot_Perf__Trace__Context(pTHX_ CV *cv); -void boot_DynaLoader(pTHX_ CV *cv); - void xs_init(pTHX) { const char *file = __FILE__; @@ -270,7 +267,7 @@ int common_lock_depth(struct scripting_context *context) } static void perl_process_event(int cpu, void *data, - int size __attribute((unused)), + int size __unused, unsigned long long nsecs, char *comm) { struct format_field *field; @@ -362,28 +359,46 @@ static void run_start_sub(void) /* * Start trace script */ -static int perl_start_script(const char *script) +static int perl_start_script(const char *script, int argc, const char **argv) { - const char *command_line[2] = { "", NULL }; + const char **command_line; + int i, err = 0; + command_line = malloc((argc + 2) * sizeof(const char *)); + command_line[0] = ""; command_line[1] = script; + for (i = 2; i < argc + 2; i++) + command_line[i] = argv[i - 2]; my_perl = perl_alloc(); perl_construct(my_perl); - if (perl_parse(my_perl, xs_init, 2, (char **)command_line, - (char **)NULL)) - return -1; + if (perl_parse(my_perl, xs_init, argc + 2, (char **)command_line, + (char **)NULL)) { + err = -1; + goto error; + } - perl_run(my_perl); - if (SvTRUE(ERRSV)) - return -1; + if (perl_run(my_perl)) { + err = -1; + goto error; + } + + if (SvTRUE(ERRSV)) { + err = -1; + goto error; + } 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); + free(command_line); + + return err; } /* @@ -573,26 +588,74 @@ struct scripting_ops perl_scripting_ops = { .generate_script = perl_generate_script, }; -#ifdef NO_LIBPERL -void setup_perl_scripting(void) +static void print_unsupported_msg(void) { fprintf(stderr, "Perl scripting not supported." - " Install libperl and rebuild perf to enable it. e.g. " - "apt-get install libperl-dev (ubuntu), yum install " - "perl-ExtUtils-Embed (Fedora), etc.\n"); + " Install libperl and rebuild perf to enable it.\n" + "For example:\n # apt-get install libperl-dev (ubuntu)" + "\n # yum install perl-ExtUtils-Embed (Fedora)" + "\n etc.\n"); } -#else -void setup_perl_scripting(void) + +static int perl_start_script_unsupported(const char *script __unused, + int argc __unused, + const char **argv __unused) +{ + print_unsupported_msg(); + + return -1; +} + +static int perl_stop_script_unsupported(void) +{ + return 0; +} + +static void perl_process_event_unsupported(int cpu __unused, + void *data __unused, + int size __unused, + unsigned long long nsecs __unused, + char *comm __unused) +{ +} + +static int perl_generate_script_unsupported(const char *outfile __unused) +{ + print_unsupported_msg(); + + return -1; +} + +struct scripting_ops perl_scripting_unsupported_ops = { + .name = "Perl", + .start_script = perl_start_script_unsupported, + .stop_script = perl_stop_script_unsupported, + .process_event = perl_process_event_unsupported, + .generate_script = perl_generate_script_unsupported, +}; + +static void register_perl_scripting(struct scripting_ops *scripting_ops) { int err; - err = script_spec_register("Perl", &perl_scripting_ops); + err = script_spec_register("Perl", scripting_ops); if (err) die("error registering Perl script extension"); - err = script_spec_register("pl", &perl_scripting_ops); + err = script_spec_register("pl", scripting_ops); if (err) die("error registering pl script extension"); scripting_context = malloc(sizeof(struct scripting_context)); } + +#ifdef NO_LIBPERL +void setup_perl_scripting(void) +{ + register_perl_scripting(&perl_scripting_unsupported_ops); +} +#else +void setup_perl_scripting(void) +{ + register_perl_scripting(&perl_scripting_ops); +} #endif diff --git a/tools/perf/util/trace-event-perl.h b/tools/perf/util/trace-event-perl.h index 8fe0d866fe1..e88fb26137b 100644 --- a/tools/perf/util/trace-event-perl.h +++ b/tools/perf/util/trace-event-perl.h @@ -34,9 +34,13 @@ typedef int INTERP; #define dXSUB_SYS #define pTHX_ static inline void newXS(const char *a, void *b, const char *c) {} +static void boot_Perf__Trace__Context(pTHX_ CV *cv) {} +static void boot_DynaLoader(pTHX_ CV *cv) {} #else #include <EXTERN.h> #include <perl.h> +void boot_Perf__Trace__Context(pTHX_ CV *cv); +void boot_DynaLoader(pTHX_ CV *cv); typedef PerlInterpreter * INTERP; #endif diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 342dfdd43f8..1744422cafc 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -145,8 +145,9 @@ static void read_proc_kallsyms(void) if (!size) return; - buf = malloc_or_die(size); + buf = malloc_or_die(size + 1); read_or_die(buf, size); + buf[size] = '\0'; parse_proc_kallsyms(buf, size); diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 81698d5e650..6ad405620c9 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -270,7 +270,7 @@ enum trace_flag_type { struct scripting_ops { const char *name; - int (*start_script) (const char *); + int (*start_script) (const char *script, int argc, const char **argv); int (*stop_script) (void); void (*process_event) (int cpu, void *data, int size, unsigned long long nsecs, char *comm); |