diff --git a/[refs] b/[refs] index 8b8da2695d75..206065819d2f 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: f92128193094c288bc315db1694fafeaeb7ee1d0 +refs/heads/master: 8dc58101f2c838355d44402aa77646649d10dbec diff --git a/trunk/include/linux/perf_event.h b/trunk/include/linux/perf_event.h index bf896d0b2e9c..6e96cc8225d4 100644 --- a/trunk/include/linux/perf_event.h +++ b/trunk/include/linux/perf_event.h @@ -589,14 +589,6 @@ enum perf_group_flag { PERF_GROUP_SOFTWARE = 0x1, }; -#define SWEVENT_HLIST_BITS 8 -#define SWEVENT_HLIST_SIZE (1 << SWEVENT_HLIST_BITS) - -struct swevent_hlist { - struct hlist_head heads[SWEVENT_HLIST_SIZE]; - struct rcu_head rcu_head; -}; - /** * struct perf_event - performance event kernel representation: */ @@ -605,7 +597,6 @@ struct perf_event { struct list_head group_entry; struct list_head event_entry; struct list_head sibling_list; - struct hlist_node hlist_entry; int nr_siblings; int group_flags; struct perf_event *group_leader; @@ -753,9 +744,6 @@ struct perf_cpu_context { int active_oncpu; int max_pertask; int exclusive; - struct swevent_hlist *swevent_hlist; - struct mutex hlist_mutex; - int hlist_refcount; /* * Recursion avoidance: diff --git a/trunk/kernel/perf_event.c b/trunk/kernel/perf_event.c index 07b7a435bf03..fcf42dcd6089 100644 --- a/trunk/kernel/perf_event.c +++ b/trunk/kernel/perf_event.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -3967,6 +3966,36 @@ static void perf_swevent_add(struct perf_event *event, u64 nr, perf_swevent_overflow(event, 0, nmi, data, regs); } +static int perf_swevent_is_counting(struct perf_event *event) +{ + /* + * The event is active, we're good! + */ + if (event->state == PERF_EVENT_STATE_ACTIVE) + return 1; + + /* + * The event is off/error, not counting. + */ + if (event->state != PERF_EVENT_STATE_INACTIVE) + return 0; + + /* + * The event is inactive, if the context is active + * we're part of a group that didn't make it on the 'pmu', + * not counting. + */ + if (event->ctx->is_active) + return 0; + + /* + * We're inactive and the context is too, this means the + * task is scheduled out, we're counting events that happen + * to us, like migration events. + */ + return 1; +} + static int perf_tp_event_match(struct perf_event *event, struct perf_sample_data *data); @@ -3990,6 +4019,12 @@ static int perf_swevent_match(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) { + if (event->cpu != -1 && event->cpu != smp_processor_id()) + return 0; + + if (!perf_swevent_is_counting(event)) + return 0; + if (event->attr.type != type) return 0; @@ -4006,53 +4041,18 @@ static int perf_swevent_match(struct perf_event *event, return 1; } -static inline u64 swevent_hash(u64 type, u32 event_id) +static void perf_swevent_ctx_event(struct perf_event_context *ctx, + enum perf_type_id type, + u32 event_id, u64 nr, int nmi, + struct perf_sample_data *data, + struct pt_regs *regs) { - u64 val = event_id | (type << 32); - - return hash_64(val, SWEVENT_HLIST_BITS); -} - -static struct hlist_head * -find_swevent_head(struct perf_cpu_context *ctx, u64 type, u32 event_id) -{ - u64 hash; - struct swevent_hlist *hlist; - - hash = swevent_hash(type, event_id); - - hlist = rcu_dereference(ctx->swevent_hlist); - if (!hlist) - return NULL; - - return &hlist->heads[hash]; -} - -static void do_perf_sw_event(enum perf_type_id type, u32 event_id, - u64 nr, int nmi, - struct perf_sample_data *data, - struct pt_regs *regs) -{ - struct perf_cpu_context *cpuctx; struct perf_event *event; - struct hlist_node *node; - struct hlist_head *head; - - cpuctx = &__get_cpu_var(perf_cpu_context); - - rcu_read_lock(); - head = find_swevent_head(cpuctx, type, event_id); - - if (!head) - goto end; - - hlist_for_each_entry_rcu(event, node, head, hlist_entry) { + list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { if (perf_swevent_match(event, type, event_id, data, regs)) perf_swevent_add(event, nr, nmi, data, regs); } -end: - rcu_read_unlock(); } int perf_swevent_get_recursion_context(void) @@ -4090,6 +4090,27 @@ void perf_swevent_put_recursion_context(int rctx) } EXPORT_SYMBOL_GPL(perf_swevent_put_recursion_context); +static void do_perf_sw_event(enum perf_type_id type, u32 event_id, + u64 nr, int nmi, + struct perf_sample_data *data, + struct pt_regs *regs) +{ + struct perf_cpu_context *cpuctx; + struct perf_event_context *ctx; + + cpuctx = &__get_cpu_var(perf_cpu_context); + rcu_read_lock(); + perf_swevent_ctx_event(&cpuctx->ctx, type, event_id, + nr, nmi, data, regs); + /* + * doesn't really matter which of the child contexts the + * events ends up in. + */ + ctx = rcu_dereference(current->perf_event_ctxp); + if (ctx) + perf_swevent_ctx_event(ctx, type, event_id, nr, nmi, data, regs); + rcu_read_unlock(); +} void __perf_sw_event(u32 event_id, u64 nr, int nmi, struct pt_regs *regs, u64 addr) @@ -4115,28 +4136,16 @@ static void perf_swevent_read(struct perf_event *event) static int perf_swevent_enable(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; - struct perf_cpu_context *cpuctx; - struct hlist_head *head; - - cpuctx = &__get_cpu_var(perf_cpu_context); if (hwc->sample_period) { hwc->last_period = hwc->sample_period; perf_swevent_set_period(event); } - - head = find_swevent_head(cpuctx, event->attr.type, event->attr.config); - if (WARN_ON_ONCE(!head)) - return -EINVAL; - - hlist_add_head_rcu(&event->hlist_entry, head); - return 0; } static void perf_swevent_disable(struct perf_event *event) { - hlist_del_rcu(&event->hlist_entry); } static const struct pmu perf_ops_generic = { @@ -4164,8 +4173,15 @@ static enum hrtimer_restart perf_swevent_hrtimer(struct hrtimer *hrtimer) perf_sample_data_init(&data, 0); data.period = event->hw.last_period; regs = get_irq_regs(); + /* + * In case we exclude kernel IPs or are somehow not in interrupt + * context, provide the next best thing, the user IP. + */ + if ((event->attr.exclude_kernel || !regs) && + !event->attr.exclude_user) + regs = task_pt_regs(current); - if (regs && !perf_exclude_event(event, regs)) { + if (regs) { if (!(event->attr.exclude_idle && current->pid == 0)) if (perf_event_overflow(event, 0, &data, regs)) ret = HRTIMER_NORESTART; @@ -4313,105 +4329,6 @@ static const struct pmu perf_ops_task_clock = { .read = task_clock_perf_event_read, }; -static void swevent_hlist_release_rcu(struct rcu_head *rcu_head) -{ - struct swevent_hlist *hlist; - - hlist = container_of(rcu_head, struct swevent_hlist, rcu_head); - kfree(hlist); -} - -static void swevent_hlist_release(struct perf_cpu_context *cpuctx) -{ - struct swevent_hlist *hlist; - - if (!cpuctx->swevent_hlist) - return; - - hlist = cpuctx->swevent_hlist; - rcu_assign_pointer(cpuctx->swevent_hlist, NULL); - call_rcu(&hlist->rcu_head, swevent_hlist_release_rcu); -} - -static void swevent_hlist_put_cpu(struct perf_event *event, int cpu) -{ - struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); - - mutex_lock(&cpuctx->hlist_mutex); - - if (!--cpuctx->hlist_refcount) - swevent_hlist_release(cpuctx); - - mutex_unlock(&cpuctx->hlist_mutex); -} - -static void swevent_hlist_put(struct perf_event *event) -{ - int cpu; - - if (event->cpu != -1) { - swevent_hlist_put_cpu(event, event->cpu); - return; - } - - for_each_possible_cpu(cpu) - swevent_hlist_put_cpu(event, cpu); -} - -static int swevent_hlist_get_cpu(struct perf_event *event, int cpu) -{ - struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); - int err = 0; - - mutex_lock(&cpuctx->hlist_mutex); - - if (!cpuctx->swevent_hlist && cpu_online(cpu)) { - struct swevent_hlist *hlist; - - hlist = kzalloc(sizeof(*hlist), GFP_KERNEL); - if (!hlist) { - err = -ENOMEM; - goto exit; - } - rcu_assign_pointer(cpuctx->swevent_hlist, hlist); - } - cpuctx->hlist_refcount++; - exit: - mutex_unlock(&cpuctx->hlist_mutex); - - return err; -} - -static int swevent_hlist_get(struct perf_event *event) -{ - int err; - int cpu, failed_cpu; - - if (event->cpu != -1) - return swevent_hlist_get_cpu(event, event->cpu); - - get_online_cpus(); - for_each_possible_cpu(cpu) { - err = swevent_hlist_get_cpu(event, cpu); - if (err) { - failed_cpu = cpu; - goto fail; - } - } - put_online_cpus(); - - return 0; - fail: - for_each_possible_cpu(cpu) { - if (cpu == failed_cpu) - break; - swevent_hlist_put_cpu(event, cpu); - } - - put_online_cpus(); - return err; -} - #ifdef CONFIG_EVENT_TRACING void perf_tp_event(int event_id, u64 addr, u64 count, void *record, @@ -4445,13 +4362,10 @@ static int perf_tp_event_match(struct perf_event *event, static void tp_perf_event_destroy(struct perf_event *event) { perf_trace_disable(event->attr.config); - swevent_hlist_put(event); } static const struct pmu *tp_perf_event_init(struct perf_event *event) { - int err; - /* * Raw tracepoint data is a severe data leak, only allow root to * have these. @@ -4465,11 +4379,6 @@ static const struct pmu *tp_perf_event_init(struct perf_event *event) return NULL; event->destroy = tp_perf_event_destroy; - err = swevent_hlist_get(event); - if (err) { - perf_trace_disable(event->attr.config); - return ERR_PTR(err); - } return &perf_ops_generic; } @@ -4570,7 +4479,6 @@ static void sw_perf_event_destroy(struct perf_event *event) WARN_ON(event->parent); atomic_dec(&perf_swevent_enabled[event_id]); - swevent_hlist_put(event); } static const struct pmu *sw_perf_event_init(struct perf_event *event) @@ -4609,12 +4517,6 @@ static const struct pmu *sw_perf_event_init(struct perf_event *event) case PERF_COUNT_SW_ALIGNMENT_FAULTS: case PERF_COUNT_SW_EMULATION_FAULTS: if (!event->parent) { - int err; - - err = swevent_hlist_get(event); - if (err) - return ERR_PTR(err); - atomic_inc(&perf_swevent_enabled[event_id]); event->destroy = sw_perf_event_destroy; } @@ -5487,7 +5389,6 @@ static void __init perf_event_init_all_cpus(void) for_each_possible_cpu(cpu) { cpuctx = &per_cpu(perf_cpu_context, cpu); - mutex_init(&cpuctx->hlist_mutex); __perf_event_init_context(&cpuctx->ctx, NULL); } } @@ -5501,16 +5402,6 @@ static void __cpuinit perf_event_init_cpu(int cpu) spin_lock(&perf_resource_lock); cpuctx->max_pertask = perf_max_events - perf_reserved_percpu; spin_unlock(&perf_resource_lock); - - mutex_lock(&cpuctx->hlist_mutex); - if (cpuctx->hlist_refcount > 0) { - struct swevent_hlist *hlist; - - hlist = kzalloc(sizeof(*hlist), GFP_KERNEL); - WARN_ON_ONCE(!hlist); - rcu_assign_pointer(cpuctx->swevent_hlist, hlist); - } - mutex_unlock(&cpuctx->hlist_mutex); } #ifdef CONFIG_HOTPLUG_CPU @@ -5530,10 +5421,6 @@ static void perf_event_exit_cpu(int cpu) struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); struct perf_event_context *ctx = &cpuctx->ctx; - mutex_lock(&cpuctx->hlist_mutex); - swevent_hlist_release(cpuctx); - mutex_unlock(&cpuctx->hlist_mutex); - mutex_lock(&ctx->mutex); smp_call_function_single(cpu, __perf_event_exit_cpu, NULL, 1); mutex_unlock(&ctx->mutex); diff --git a/trunk/tools/perf/Documentation/perf-record.txt b/trunk/tools/perf/Documentation/perf-record.txt index 020d871c7934..fc46c0b40f6e 100644 --- a/trunk/tools/perf/Documentation/perf-record.txt +++ b/trunk/tools/perf/Documentation/perf-record.txt @@ -58,7 +58,7 @@ OPTIONS -f:: --force:: - Overwrite existing data file. (deprecated) + Overwrite existing data file. -c:: --count=:: @@ -101,7 +101,7 @@ OPTIONS -R:: --raw-samples:: -Collect raw sample records from all opened counters (default for tracepoint counters). +Collect raw sample records from all opened counters (typically for tracepoint counters). SEE ALSO -------- diff --git a/trunk/tools/perf/builtin-record.c b/trunk/tools/perf/builtin-record.c index ca2affc9233f..d060fc50c8a4 100644 --- a/trunk/tools/perf/builtin-record.c +++ b/trunk/tools/perf/builtin-record.c @@ -26,20 +26,13 @@ #include #include -enum write_mode_t { - WRITE_FORCE, - WRITE_APPEND -}; - static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; -static unsigned int user_interval = UINT_MAX; static long default_interval = 0; static int nr_cpus = 0; static unsigned int page_size; static unsigned int mmap_pages = 128; -static unsigned int user_freq = UINT_MAX; static int freq = 1000; static int output; static const char *output_name = "perf.data"; @@ -54,7 +47,8 @@ static pid_t *all_tids = NULL; static int thread_num = 0; static pid_t child_pid = -1; static bool inherit = true; -static enum write_mode_t write_mode = WRITE_FORCE; +static bool force = false; +static bool append_file = false; static bool call_graph = false; static bool inherit_stat = false; static bool no_samples = false; @@ -257,19 +251,10 @@ static void create_counter(int counter, int cpu) if (nr_counters > 1) attr->sample_type |= PERF_SAMPLE_ID; - /* - * We default some events to a 1 default interval. But keep - * it a weak assumption overridable by the user. - */ - if (!attr->sample_period || (user_freq != UINT_MAX && - user_interval != UINT_MAX)) { - if (freq) { - attr->sample_type |= PERF_SAMPLE_PERIOD; - attr->freq = 1; - attr->sample_freq = freq; - } else { - attr->sample_period = default_interval; - } + if (freq) { + attr->sample_type |= PERF_SAMPLE_PERIOD; + attr->freq = 1; + attr->sample_freq = freq; } if (no_samples) @@ -465,19 +450,26 @@ static int __cmd_record(int argc, const char **argv) } if (!stat(output_name, &st) && st.st_size) { - if (write_mode == WRITE_FORCE) { + if (!force) { + if (!append_file) { + pr_err("Error, output file %s exists, use -A " + "to append or -f to overwrite.\n", + output_name); + exit(-1); + } + } else { char oldname[PATH_MAX]; snprintf(oldname, sizeof(oldname), "%s.old", output_name); unlink(oldname); rename(output_name, oldname); } - } else if (write_mode == WRITE_APPEND) { - write_mode = WRITE_FORCE; + } else { + append_file = false; } flags = O_CREAT|O_RDWR; - if (write_mode == WRITE_APPEND) + if (append_file) file_new = 0; else flags |= O_TRUNC; @@ -488,15 +480,14 @@ static int __cmd_record(int argc, const char **argv) exit(-1); } - session = perf_session__new(output_name, O_WRONLY, - write_mode == WRITE_FORCE); + session = perf_session__new(output_name, O_WRONLY, force); if (session == NULL) { pr_err("Not enough memory for reading perf file header\n"); return -1; } if (!file_new) { - err = perf_header__read(&session->header, output); + err = perf_header__read(session, output); if (err < 0) return err; } @@ -676,8 +667,6 @@ static const char * const record_usage[] = { NULL }; -static bool force, append_file; - static const struct option options[] = { OPT_CALLBACK('e', "event", NULL, "event", "event selector. use 'perf list' to list available events", @@ -699,14 +688,14 @@ static const struct option options[] = { OPT_INTEGER('C', "profile_cpu", &profile_cpu, "CPU to profile on"), OPT_BOOLEAN('f', "force", &force, - "overwrite existing data file (deprecated)"), - OPT_LONG('c', "count", &user_interval, + "overwrite existing data file"), + OPT_LONG('c', "count", &default_interval, "event period to sample"), OPT_STRING('o', "output", &output_name, "file", "output file name"), OPT_BOOLEAN('i', "inherit", &inherit, "child tasks inherit counters"), - OPT_INTEGER('F', "freq", &user_freq, + OPT_INTEGER('F', "freq", &freq, "profile at this frequency"), OPT_INTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), @@ -727,6 +716,7 @@ static const struct option options[] = { int cmd_record(int argc, const char **argv, const char *prefix __used) { + int counter; int i,j; argc = parse_options(argc, argv, options, record_usage, @@ -735,16 +725,6 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) !system_wide && profile_cpu == -1) usage_with_options(record_usage, options); - if (force && append_file) { - fprintf(stderr, "Can't overwrite and append at the same time." - " You need to choose between -f and -A"); - usage_with_options(record_usage, options); - } else if (append_file) { - write_mode = WRITE_APPEND; - } else { - write_mode = WRITE_FORCE; - } - symbol__init(); if (!nr_counters) { @@ -784,11 +764,6 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) if (!event_array) return -ENOMEM; - if (user_interval != UINT_MAX) - default_interval = user_interval; - if (user_freq != UINT_MAX) - freq = user_freq; - /* * User specified count overrides default frequency. */ @@ -801,5 +776,12 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) exit(EXIT_FAILURE); } + for (counter = 0; counter < nr_counters; counter++) { + if (attrs[counter].sample_period) + continue; + + attrs[counter].sample_period = default_interval; + } + return __cmd_record(argc, argv); } diff --git a/trunk/tools/perf/util/event.h b/trunk/tools/perf/util/event.h index 7f7cf8539cfe..5c1eba671305 100644 --- a/trunk/tools/perf/util/event.h +++ b/trunk/tools/perf/util/event.h @@ -83,6 +83,10 @@ struct build_id_event { char filename[]; }; +enum perf_header_event_type { /* above any possible kernel type */ + PERF_RECORD_HEADER_MAX = 64, +}; + typedef union event_union { struct perf_event_header header; struct ip_event ip; diff --git a/trunk/tools/perf/util/header.c b/trunk/tools/perf/util/header.c index 6c9aa16ee51f..8d05337d1a59 100644 --- a/trunk/tools/perf/util/header.c +++ b/trunk/tools/perf/util/header.c @@ -427,6 +427,25 @@ static int perf_header__adds_write(struct perf_header *self, int fd) return err; } +int perf_header__write_pipe(int fd) +{ + struct perf_pipe_file_header f_header; + int err; + + f_header = (struct perf_pipe_file_header){ + .magic = PERF_MAGIC, + .size = sizeof(f_header), + }; + + err = do_write(fd, &f_header, sizeof(f_header)); + if (err < 0) { + pr_debug("failed to write perf pipe header\n"); + return err; + } + + return 0; +} + int perf_header__write(struct perf_header *self, int fd, bool at_exit) { struct perf_file_header f_header; @@ -518,25 +537,10 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) return 0; } -static int do_read(int fd, void *buf, size_t size) -{ - while (size) { - int ret = read(fd, buf, size); - - if (ret <= 0) - return -1; - - size -= ret; - buf += ret; - } - - return 0; -} - static int perf_header__getbuffer64(struct perf_header *self, int fd, void *buf, size_t size) { - if (do_read(fd, buf, size)) + if (do_read(fd, buf, size) <= 0) return -1; if (self->needs_swap) @@ -592,7 +596,7 @@ int perf_file_header__read(struct perf_file_header *self, { lseek(fd, 0, SEEK_SET); - if (do_read(fd, self, sizeof(*self)) || + if (do_read(fd, self, sizeof(*self)) <= 0 || memcmp(&self->magic, __perf_magic, sizeof(self->magic))) return -1; @@ -662,13 +666,51 @@ static int perf_file_section__process(struct perf_file_section *self, return 0; } -int perf_header__read(struct perf_header *self, int fd) +static int perf_file_header__read_pipe(struct perf_pipe_file_header *self, + struct perf_header *ph, int fd) { + if (do_read(fd, self, sizeof(*self)) <= 0 || + memcmp(&self->magic, __perf_magic, sizeof(self->magic))) + return -1; + + if (self->size != sizeof(*self)) { + u64 size = bswap_64(self->size); + + if (size != sizeof(*self)) + return -1; + + ph->needs_swap = true; + } + + return 0; +} + +static int perf_header__read_pipe(struct perf_session *session, int fd) +{ + struct perf_header *self = &session->header; + struct perf_pipe_file_header f_header; + + if (perf_file_header__read_pipe(&f_header, self, fd) < 0) { + pr_debug("incompatible file format\n"); + return -EINVAL; + } + + session->fd = fd; + + return 0; +} + +int perf_header__read(struct perf_session *session, int fd) +{ + struct perf_header *self = &session->header; struct perf_file_header f_header; struct perf_file_attr f_attr; u64 f_id; int nr_attrs, nr_ids, i, j; + if (session->fd_pipe) + return perf_header__read_pipe(session, fd); + if (perf_file_header__read(&f_header, self, fd) < 0) { pr_debug("incompatible file format\n"); return -EINVAL; diff --git a/trunk/tools/perf/util/header.h b/trunk/tools/perf/util/header.h index c059f08cf877..6562ece67064 100644 --- a/trunk/tools/perf/util/header.h +++ b/trunk/tools/perf/util/header.h @@ -39,6 +39,11 @@ struct perf_file_header { DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); }; +struct perf_pipe_file_header { + u64 magic; + u64 size; +}; + struct perf_header; int perf_file_header__read(struct perf_file_header *self, @@ -60,8 +65,9 @@ struct perf_header { 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__read(struct perf_session *session, int fd); int perf_header__write(struct perf_header *self, int fd, bool at_exit); +int perf_header__write_pipe(int fd); int perf_header__add_attr(struct perf_header *self, struct perf_header_attr *attr); diff --git a/trunk/tools/perf/util/parse-events.c b/trunk/tools/perf/util/parse-events.c index 3b4ec6797565..435781e0c205 100644 --- a/trunk/tools/perf/util/parse-events.c +++ b/trunk/tools/perf/util/parse-events.c @@ -410,6 +410,7 @@ static enum event_result parse_single_tracepoint_event(char *sys_name, const char *evt_name, unsigned int evt_length, + char *flags, struct perf_event_attr *attr, const char **strp) { @@ -418,11 +419,13 @@ parse_single_tracepoint_event(char *sys_name, u64 id; int fd; - attr->sample_type |= PERF_SAMPLE_RAW; - attr->sample_type |= PERF_SAMPLE_TIME; - attr->sample_type |= PERF_SAMPLE_CPU; - - attr->sample_period = 1; + if (flags) { + if (!strncmp(flags, "record", strlen(flags))) { + attr->sample_type |= PERF_SAMPLE_RAW; + attr->sample_type |= PERF_SAMPLE_TIME; + attr->sample_type |= PERF_SAMPLE_CPU; + } + } snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, sys_name, evt_name); @@ -530,7 +533,8 @@ static enum event_result parse_tracepoint_event(const char **strp, flags); } else return parse_single_tracepoint_event(sys_name, evt_name, - evt_length, attr, strp); + evt_length, flags, + attr, strp); } static enum event_result diff --git a/trunk/tools/perf/util/session.c b/trunk/tools/perf/util/session.c index ddf288fca3eb..2c1277cb4ae4 100644 --- a/trunk/tools/perf/util/session.c +++ b/trunk/tools/perf/util/session.c @@ -14,6 +14,16 @@ static int perf_session__open(struct perf_session *self, bool force) { struct stat input_stat; + if (!strcmp(self->filename, "-")) { + self->fd_pipe = true; + self->fd = STDIN_FILENO; + + if (perf_header__read(self, self->fd) < 0) + pr_err("incompatible file format"); + + return 0; + } + self->fd = open(self->filename, O_RDONLY); if (self->fd < 0) { pr_err("failed to open file: %s", self->filename); @@ -38,7 +48,7 @@ static int perf_session__open(struct perf_session *self, bool force) goto out_close; } - if (perf_header__read(&self->header, self->fd) < 0) { + if (perf_header__read(self, self->fd) < 0) { pr_err("incompatible file format"); goto out_close; } @@ -52,6 +62,11 @@ static int perf_session__open(struct perf_session *self, bool force) return -1; } +void perf_session__update_sample_type(struct perf_session *self) +{ + self->sample_type = perf_header__sample_type(&self->header); +} + struct perf_session *perf_session__new(const char *filename, int mode, bool force) { size_t len = filename ? strlen(filename) + 1 : 0; @@ -85,7 +100,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc goto out_delete; } - self->sample_type = perf_header__sample_type(&self->header); + perf_session__update_sample_type(self); out: return self; out_free: @@ -200,14 +215,17 @@ static const char *event__name[] = { [PERF_RECORD_SAMPLE] = "SAMPLE", }; -unsigned long event__total[PERF_RECORD_MAX]; +unsigned long event__total[PERF_RECORD_HEADER_MAX]; void event__print_totals(void) { int i; - for (i = 0; i < PERF_RECORD_MAX; ++i) + for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { + if (!event__name[i]) + continue; pr_info("%10s events: %10ld\n", event__name[i], event__total[i]); + } } void mem_bswap_64(void *src, int byte_size) @@ -271,7 +289,7 @@ static event__swap_op event__swap_ops[] = { [PERF_RECORD_LOST] = event__all64_swap, [PERF_RECORD_READ] = event__read_swap, [PERF_RECORD_SAMPLE] = event__all64_swap, - [PERF_RECORD_MAX] = NULL, + [PERF_RECORD_HEADER_MAX] = NULL, }; static int perf_session__process_event(struct perf_session *self, @@ -281,7 +299,7 @@ static int perf_session__process_event(struct perf_session *self, { trace_event(event); - if (event->header.type < PERF_RECORD_MAX) { + if (event->header.type < PERF_RECORD_HEADER_MAX) { dump_printf("%#Lx [%#x]: PERF_RECORD_%s", offset + head, event->header.size, event__name[event->header.type]); @@ -376,6 +394,101 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se return thread; } +int do_read(int fd, void *buf, size_t size) +{ + void *buf_start = buf; + + while (size) { + int ret = read(fd, buf, size); + + if (ret <= 0) + return ret; + + size -= ret; + buf += ret; + } + + return buf - buf_start; +} + +#define session_done() (*(volatile int *)(&session_done)) +volatile int session_done; + +static int __perf_session__process_pipe_events(struct perf_session *self, + struct perf_event_ops *ops) +{ + event_t event; + uint32_t size; + int skip = 0; + u64 head; + int err; + void *p; + + perf_event_ops__fill_defaults(ops); + + head = 0; +more: + err = do_read(self->fd, &event, sizeof(struct perf_event_header)); + if (err <= 0) { + if (err == 0) + goto done; + + pr_err("failed to read event header\n"); + goto out_err; + } + + if (self->header.needs_swap) + perf_event_header__bswap(&event.header); + + size = event.header.size; + if (size == 0) + size = 8; + + p = &event; + p += sizeof(struct perf_event_header); + + err = do_read(self->fd, p, size - sizeof(struct perf_event_header)); + if (err <= 0) { + if (err == 0) { + pr_err("unexpected end of event stream\n"); + goto done; + } + + pr_err("failed to read event data\n"); + goto out_err; + } + + if (size == 0 || + (skip = perf_session__process_event(self, &event, ops, + 0, head)) < 0) { + dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", + head, event.header.size, event.header.type); + /* + * assume we lost track of the stream, check alignment, and + * increment a single u64 in the hope to catch on again 'soon'. + */ + if (unlikely(head & 7)) + head &= ~7ULL; + + size = 8; + } + + head += size; + + dump_printf("\n%#Lx [%#x]: event: %d\n", + head, event.header.size, event.header.type); + + if (skip > 0) + head += skip; + + if (!session_done()) + goto more; +done: + err = 0; +out_err: + return err; +} + int __perf_session__process_events(struct perf_session *self, u64 data_offset, u64 data_size, u64 file_size, struct perf_event_ops *ops) @@ -499,9 +612,13 @@ int perf_session__process_events(struct perf_session *self, self->cwdlen = strlen(self->cwd); } - err = __perf_session__process_events(self, self->header.data_offset, - self->header.data_size, - self->size, ops); + if (!self->fd_pipe) + err = __perf_session__process_events(self, + self->header.data_offset, + self->header.data_size, + self->size, ops); + else + err = __perf_session__process_pipe_events(self, ops); out_err: return err; } diff --git a/trunk/tools/perf/util/session.h b/trunk/tools/perf/util/session.h index 27f4c2dc715b..5f7891136655 100644 --- a/trunk/tools/perf/util/session.h +++ b/trunk/tools/perf/util/session.h @@ -27,6 +27,7 @@ struct perf_session { u64 sample_type; struct ref_reloc_sym ref_reloc_sym; int fd; + bool fd_pipe; int cwdlen; char *cwd; char filename[0]; @@ -92,6 +93,9 @@ static inline struct map * return map_groups__new_module(&self->kmaps, start, filename); } +int do_read(int fd, void *buf, size_t size); +void perf_session__update_sample_type(struct perf_session *self); + #ifdef NO_NEWT_SUPPORT static inline int perf_session__browse_hists(struct rb_root *hists __used, u64 nr_hists __used, diff --git a/trunk/tools/perf/util/trace-event-parse.c b/trunk/tools/perf/util/trace-event-parse.c index d6ef414075a6..17d6d66ed766 100644 --- a/trunk/tools/perf/util/trace-event-parse.c +++ b/trunk/tools/perf/util/trace-event-parse.c @@ -761,7 +761,7 @@ static int field_is_string(struct format_field *field) static int field_is_dynamic(struct format_field *field) { - if (!strncmp(field->type, "__data_loc", 10)) + if (!strcmp(field->type, "__data_loc")) return 1; return 0;