From 37982ba0a0630066a6a0844a66aedaf91c66db84 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 26 Nov 2010 18:31:54 -0200 Subject: perf events: Default to using event__process_lost Tool developers have to fill in a 'perf_event_ops' method table to specify how to handle each event, so far the ones that were not explicitely especified would get a stub that would just discard the event. Change that so that tool developers can get the lost event details and the total number of such events at the end of 'perf report -D' output. Suggested-by: Thomas Gleixner Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian CC: Thomas Gleixner Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index fa9d652c2dc3..3d5604739493 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -262,7 +262,7 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) if (handler->exit == NULL) handler->exit = process_event_stub; if (handler->lost == NULL) - handler->lost = process_event_stub; + handler->lost = event__process_lost; if (handler->read == NULL) handler->read = process_event_stub; if (handler->throttle == NULL) -- cgit v1.2.3 From 068ffaa8bfb67c2ddb3ecaf38cc90f94a1a92fe3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 27 Nov 2010 02:41:01 -0200 Subject: perf tools: Fix lost and unknown events handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix it by explaining what can be happening and giving the number of processed and lost events. Also holler if unknown events were found, that can be due to processing a perf.data file collected using a newer tool where newer events got added on reporting using an older perf tool, that or a bug, so ask for a report to be made. Works on both --tui and --stdio. Suggested-by: Thomas Gleixner Cc: Frédéric Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Stephane Eranian Cc: Thomas Gleixner LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/debug.c | 11 +++++++++++ tools/perf/util/debug.h | 2 ++ tools/perf/util/session.c | 18 ++++++++++++++++++ tools/perf/util/ui/util.c | 16 +++++++++++++++- 4 files changed, 46 insertions(+), 1 deletion(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index c8d81b00089d..e5161e8f9915 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -46,6 +46,17 @@ int dump_printf(const char *fmt, ...) return ret; } +#ifdef NO_NEWT_SUPPORT +void ui__warning(const char *format, ...) +{ + va_list args; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} +#endif + static int dump_printf_color(const char *fmt, const char *color, ...) { va_list args; diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 7b514082bbaf..ca35fd66b5df 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -35,4 +35,6 @@ int ui_helpline__show_help(const char *format, va_list ap); #include "ui/progress.h" #endif +void ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); + #endif /* __PERF_DEBUG_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 3d5604739493..3ae69550fa0b 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -852,6 +852,24 @@ done: flush_sample_queue(self, ops); out_err: ui_progress__delete(progress); + + if (ops->lost == event__process_lost && + self->hists.stats.total_lost != 0) { + ui__warning("Processed %Lu events and LOST %Lu!\n\n" + "Check IO/CPU overload!\n\n", + self->hists.stats.total_period, + self->hists.stats.total_lost); + } + + if (self->hists.stats.nr_unknown_events != 0) { + ui__warning("Found %u unknown events!\n\n" + "Is this an older tool processing a perf.data " + "file generated by a more recent tool?\n\n" + "If that is not the case, consider " + "reporting to linux-kernel@vger.kernel.org.\n\n", + self->hists.stats.nr_unknown_events); + } + return err; } diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c index 056c69521a38..7b5a8926624e 100644 --- a/tools/perf/util/ui/util.c +++ b/tools/perf/util/ui/util.c @@ -104,10 +104,24 @@ out_destroy_form: return rc; } -static const char yes[] = "Yes", no[] = "No"; +static const char yes[] = "Yes", no[] = "No", + warning_str[] = "Warning!", ok[] = "Ok"; bool ui__dialog_yesno(const char *msg) { /* newtWinChoice should really be accepting const char pointers... */ return newtWinChoice(NULL, (char *)yes, (char *)no, (char *)msg) == 1; } + +void ui__warning(const char *format, ...) +{ + va_list args; + + va_start(args, format); + if (use_browser > 0) + newtWinMessagev((char *)warning_str, (char *)ok, + (char *)format, args); + else + vfprintf(stderr, format, args); + va_end(args); +} -- cgit v1.2.3 From a1225decc43849a73f7e4c333c3fdbbb8a9c1e65 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 30 Nov 2010 17:49:33 +0000 Subject: perf session: Fix list sort algorithm The homebrewn sort algorithm fails to sort in time order. One of the problem spots is that it fails to deal with equal timestamps correctly. My first gut reaction was to replace the fancy list with an rbtree, but the performance is 3 times worse. Rewrite it so it works. Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Frederic Weisbecker LKML-Reference: <20101130163819.908482530@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 113 +++++++++++++++++++--------------------------- tools/perf/util/session.h | 4 +- 2 files changed, 49 insertions(+), 68 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 3ae69550fa0b..daca557b9d28 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -104,7 +104,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc self->mmap_window = 32; self->machines = RB_ROOT; self->repipe = repipe; - INIT_LIST_HEAD(&self->ordered_samples.samples_head); + INIT_LIST_HEAD(&self->ordered_samples.samples); machine__init(&self->host_machine, "", HOST_KERNEL_ID); if (mode == O_RDONLY) { @@ -393,27 +393,33 @@ struct sample_queue { static void flush_sample_queue(struct perf_session *s, struct perf_event_ops *ops) { - struct list_head *head = &s->ordered_samples.samples_head; - u64 limit = s->ordered_samples.next_flush; + struct ordered_samples *os = &s->ordered_samples; + struct list_head *head = &os->samples; struct sample_queue *tmp, *iter; + u64 limit = os->next_flush; + u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; if (!ops->ordered_samples || !limit) return; list_for_each_entry_safe(iter, tmp, head, list) { if (iter->timestamp > limit) - return; - - if (iter == s->ordered_samples.last_inserted) - s->ordered_samples.last_inserted = NULL; + break; ops->sample((event_t *)iter->event, s); - s->ordered_samples.last_flush = iter->timestamp; + os->last_flush = iter->timestamp; list_del(&iter->list); free(iter->event); free(iter); } + + if (list_empty(head)) { + os->last_sample = NULL; + } else if (last_ts <= limit) { + os->last_sample = + list_entry(head->prev, struct sample_queue, list); + } } /* @@ -465,71 +471,50 @@ static int process_finished_round(event_t *event __used, return 0; } -static void __queue_sample_end(struct sample_queue *new, struct list_head *head) -{ - struct sample_queue *iter; - - list_for_each_entry_reverse(iter, head, list) { - if (iter->timestamp < new->timestamp) { - list_add(&new->list, &iter->list); - return; - } - } - - list_add(&new->list, head); -} - -static void __queue_sample_before(struct sample_queue *new, - struct sample_queue *iter, - struct list_head *head) -{ - list_for_each_entry_continue_reverse(iter, head, list) { - if (iter->timestamp < new->timestamp) { - list_add(&new->list, &iter->list); - return; - } - } - - list_add(&new->list, head); -} - -static void __queue_sample_after(struct sample_queue *new, - struct sample_queue *iter, - struct list_head *head) -{ - list_for_each_entry_continue(iter, head, list) { - if (iter->timestamp > new->timestamp) { - list_add_tail(&new->list, &iter->list); - return; - } - } - list_add_tail(&new->list, head); -} - /* The queue is ordered by time */ static void __queue_sample_event(struct sample_queue *new, struct perf_session *s) { - struct sample_queue *last_inserted = s->ordered_samples.last_inserted; - struct list_head *head = &s->ordered_samples.samples_head; + struct ordered_samples *os = &s->ordered_samples; + struct sample_queue *sample = os->last_sample; + u64 timestamp = new->timestamp; + struct list_head *p; + os->last_sample = new; - if (!last_inserted) { - __queue_sample_end(new, head); + if (!sample) { + list_add(&new->list, &os->samples); + os->max_timestamp = timestamp; return; } /* - * Most of the time the current event has a timestamp - * very close to the last event inserted, unless we just switched - * to another event buffer. Having a sorting based on a list and - * on the last inserted event that is close to the current one is - * probably more efficient than an rbtree based sorting. + * last_sample might point to some random place in the list as it's + * the last queued event. We expect that the new event is close to + * this. */ - if (last_inserted->timestamp >= new->timestamp) - __queue_sample_before(new, last_inserted, head); - else - __queue_sample_after(new, last_inserted, head); + if (sample->timestamp <= timestamp) { + while (sample->timestamp <= timestamp) { + p = sample->list.next; + if (p == &os->samples) { + list_add_tail(&new->list, &os->samples); + os->max_timestamp = timestamp; + return; + } + sample = list_entry(p, struct sample_queue, list); + } + list_add_tail(&new->list, &sample->list); + } else { + while (sample->timestamp > timestamp) { + p = sample->list.prev; + if (p == &os->samples) { + list_add(&new->list, &os->samples); + return; + } + sample = list_entry(p, struct sample_queue, list); + } + list_add(&new->list, &sample->list); + } } static int queue_sample_event(event_t *event, struct sample_data *data, @@ -559,10 +544,6 @@ static int queue_sample_event(event_t *event, struct sample_data *data, memcpy(new->event, event, event->header.size); __queue_sample_event(new, s); - s->ordered_samples.last_inserted = new; - - if (new->timestamp > s->ordered_samples.max_timestamp) - s->ordered_samples.max_timestamp = new->timestamp; return 0; } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 9fa0fc2a863f..a00f32ed1c79 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -17,8 +17,8 @@ struct ordered_samples { u64 last_flush; u64 next_flush; u64 max_timestamp; - struct list_head samples_head; - struct sample_queue *last_inserted; + struct list_head samples; + struct sample_queue *last_sample; }; struct perf_session { -- cgit v1.2.3 From 28990f75e66b36faf6ce56747890009d4e250243 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 30 Nov 2010 17:49:35 +0000 Subject: perf session: Use appropriate pointer type instead of silly typecasting There is no reason to use a struct sample_event pointer in struct sample_queue and type cast it when flushing the queue. Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Frederic Weisbecker LKML-Reference: <20101130163819.969462809@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index daca557b9d28..525bcf6adc21 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -386,7 +386,7 @@ static event__swap_op event__swap_ops[] = { struct sample_queue { u64 timestamp; - struct sample_event *event; + event_t *event; struct list_head list; }; @@ -406,7 +406,7 @@ static void flush_sample_queue(struct perf_session *s, if (iter->timestamp > limit) break; - ops->sample((event_t *)iter->event, s); + ops->sample(iter->event, s); os->last_flush = iter->timestamp; list_del(&iter->list); -- cgit v1.2.3 From 0331ee0cf4187dcdc2b184cf701d8b58bf9ff637 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 30 Nov 2010 17:49:38 +0000 Subject: perf session: Cleanup __perf_session__process_events() Replace the pseudo C++ self argument with session and give the mmap related variables a sensible name. shift is a complete misnomer - it took me several rounds of cursing to figure out that it's not a shift value. Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Frederic Weisbecker LKML-Reference: <20101130163820.029687218@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 77 +++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 40 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 525bcf6adc21..2fdbccf10770 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -567,13 +567,13 @@ static int perf_session__process_sample(event_t *event, struct perf_session *s, static int perf_session__process_event(struct perf_session *self, event_t *event, struct perf_event_ops *ops, - u64 offset, u64 head) + u64 file_offset) { trace_event(event); if (event->header.type < PERF_RECORD_HEADER_MAX) { dump_printf("%#Lx [%#x]: PERF_RECORD_%s", - offset + head, event->header.size, + file_offset, event->header.size, event__name[event->header.type]); hists__inc_nr_events(&self->hists, event->header.type); } @@ -606,7 +606,7 @@ static int perf_session__process_event(struct perf_session *self, return ops->event_type(event, self); case PERF_RECORD_HEADER_TRACING_DATA: /* setup for reading amidst mmap */ - lseek(self->fd, offset + head, SEEK_SET); + lseek(self->fd, file_offset, SEEK_SET); return ops->tracing_data(event, self); case PERF_RECORD_HEADER_BUILD_ID: return ops->build_id(event, self); @@ -705,8 +705,7 @@ more: } if (size == 0 || - (skip = perf_session__process_event(self, &event, ops, - 0, head)) < 0) { + (skip = perf_session__process_event(self, &event, ops, head)) < 0) { dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", head, event.header.size, event.header.type); /* @@ -735,19 +734,19 @@ out_err: return err; } -int __perf_session__process_events(struct perf_session *self, +int __perf_session__process_events(struct perf_session *session, u64 data_offset, u64 data_size, u64 file_size, struct perf_event_ops *ops) { + u64 head, page_offset, file_offset; int err, mmap_prot, mmap_flags; - u64 head, shift; - u64 offset = 0; + struct ui_progress *progress; size_t page_size; event_t *event; uint32_t size; char *buf; - struct ui_progress *progress = ui_progress__new("Processing events...", - self->size); + + progress = ui_progress__new("Processing events...", session->size); if (progress == NULL) return -1; @@ -755,21 +754,20 @@ int __perf_session__process_events(struct perf_session *self, page_size = sysconf(_SC_PAGESIZE); - head = data_offset; - shift = page_size * (head / page_size); - offset += shift; - head -= shift; + page_offset = page_size * (data_offset / page_size); + file_offset = page_offset; + head = data_offset - page_offset; mmap_prot = PROT_READ; mmap_flags = MAP_SHARED; - if (self->header.needs_swap) { + if (session->header.needs_swap) { mmap_prot |= PROT_WRITE; mmap_flags = MAP_PRIVATE; } remap: - buf = mmap(NULL, page_size * self->mmap_window, mmap_prot, - mmap_flags, self->fd, offset); + buf = mmap(NULL, page_size * session->mmap_window, mmap_prot, + mmap_flags, session->fd, file_offset); if (buf == MAP_FAILED) { pr_err("failed to mmap file\n"); err = -errno; @@ -778,36 +776,35 @@ remap: more: event = (event_t *)(buf + head); - ui_progress__update(progress, offset); + ui_progress__update(progress, file_offset); - if (self->header.needs_swap) + if (session->header.needs_swap) perf_event_header__bswap(&event->header); size = event->header.size; if (size == 0) size = 8; - if (head + event->header.size >= page_size * self->mmap_window) { + if (head + event->header.size >= page_size * session->mmap_window) { int munmap_ret; - shift = page_size * (head / page_size); - - munmap_ret = munmap(buf, page_size * self->mmap_window); + munmap_ret = munmap(buf, page_size * session->mmap_window); assert(munmap_ret == 0); - offset += shift; - head -= shift; + page_offset = page_size * (head / page_size); + file_offset += page_offset; + head -= page_offset; goto remap; } size = event->header.size; dump_printf("\n%#Lx [%#x]: event: %d\n", - offset + head, event->header.size, event->header.type); + file_offset + head, event->header.size, event->header.type); - if (size == 0 || - perf_session__process_event(self, event, ops, offset, head) < 0) { + if (size == 0 || perf_session__process_event(session, event, ops, + file_offset + head) < 0) { dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", - offset + head, event->header.size, + file_offset + head, event->header.size, event->header.type); /* * assume we lost track of the stream, check alignment, and @@ -821,36 +818,36 @@ more: head += size; - if (offset + head >= data_offset + data_size) + if (file_offset + head >= data_offset + data_size) goto done; - if (offset + head < file_size) + if (file_offset + head < file_size) goto more; done: err = 0; /* do the final flush for ordered samples */ - self->ordered_samples.next_flush = ULLONG_MAX; - flush_sample_queue(self, ops); + session->ordered_samples.next_flush = ULLONG_MAX; + flush_sample_queue(session, ops); out_err: ui_progress__delete(progress); if (ops->lost == event__process_lost && - self->hists.stats.total_lost != 0) { + session->hists.stats.total_lost != 0) { ui__warning("Processed %Lu events and LOST %Lu!\n\n" "Check IO/CPU overload!\n\n", - self->hists.stats.total_period, - self->hists.stats.total_lost); + session->hists.stats.total_period, + session->hists.stats.total_lost); } - - if (self->hists.stats.nr_unknown_events != 0) { + + if (session->hists.stats.nr_unknown_events != 0) { ui__warning("Found %u unknown events!\n\n" "Is this an older tool processing a perf.data " "file generated by a more recent tool?\n\n" "If that is not the case, consider " "reporting to linux-kernel@vger.kernel.org.\n\n", - self->hists.stats.nr_unknown_events); + session->hists.stats.nr_unknown_events); } - + return err; } -- cgit v1.2.3 From 85b99952ccd3d84707661d8ae103c710daca1c8a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 30 Nov 2010 17:49:41 +0000 Subject: perf session: Move ui_progress_update in __perf_session__process_events() The progress bar is changed when the file offset changes. This happens only when the next mmap is done. No need to call ui_progress_update() for every event. Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Frederic Weisbecker LKML-Reference: <20101130163820.094836523@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 2fdbccf10770..3c140daba63a 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -773,10 +773,10 @@ remap: err = -errno; goto out_err; } + ui_progress__update(progress, file_offset); more: event = (event_t *)(buf + head); - ui_progress__update(progress, file_offset); if (session->header.needs_swap) perf_event_header__bswap(&event->header); -- cgit v1.2.3 From d6513281c5f728d138ba895d600b9788e51508b1 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 30 Nov 2010 17:49:44 +0000 Subject: perf session: Simplify termination checks No need to check twice. Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Frederic Weisbecker LKML-Reference: <20101130163820.152886642@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 3c140daba63a..9c806ab565ea 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -738,7 +738,7 @@ int __perf_session__process_events(struct perf_session *session, u64 data_offset, u64 data_size, u64 file_size, struct perf_event_ops *ops) { - u64 head, page_offset, file_offset; + u64 head, page_offset, file_offset, file_pos; int err, mmap_prot, mmap_flags; struct ui_progress *progress; size_t page_size; @@ -758,6 +758,9 @@ int __perf_session__process_events(struct perf_session *session, file_offset = page_offset; head = data_offset - page_offset; + if (data_offset + data_size < file_size) + file_size = data_offset + data_size; + mmap_prot = PROT_READ; mmap_flags = MAP_SHARED; @@ -773,6 +776,7 @@ remap: err = -errno; goto out_err; } + file_pos = file_offset + head; ui_progress__update(progress, file_offset); more: @@ -799,10 +803,10 @@ more: size = event->header.size; dump_printf("\n%#Lx [%#x]: event: %d\n", - file_offset + head, event->header.size, event->header.type); + file_pos, event->header.size, event->header.type); - if (size == 0 || perf_session__process_event(session, event, ops, - file_offset + head) < 0) { + if (size == 0 || + perf_session__process_event(session, event, ops, file_pos) < 0) { dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", file_offset + head, event->header.size, event->header.type); @@ -817,13 +821,11 @@ more: } head += size; + file_pos += size; - if (file_offset + head >= data_offset + data_size) - goto done; - - if (file_offset + head < file_size) + if (file_pos < file_size) goto more; -done: + err = 0; /* do the final flush for ordered samples */ session->ordered_samples.next_flush = ULLONG_MAX; -- cgit v1.2.3 From 55b44629f599a2305265ae9c77f9d9bcfd6ddc17 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 30 Nov 2010 17:49:46 +0000 Subject: perf session: Use sensible mmap size On 64bit we can map the whole file in one go, on 32bit we can at least map 32MB and not map/unmap tiny chunks of the file. Base the progress bar on 1/16 of the data size. Preparatory patch to get rid of the malloc/memcpy/free of trace data. Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Frederic Weisbecker LKML-Reference: <20101130163820.213687773@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 9c806ab565ea..752577fdb16e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -101,7 +101,15 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc INIT_LIST_HEAD(&self->dead_threads); self->hists_tree = RB_ROOT; self->last_match = NULL; - self->mmap_window = 32; + /* + * On 64bit we can mmap the data file in one go. No need for tiny mmap + * slices. On 32bit we use 32MB. + */ +#if BITS_PER_LONG == 64 + self->mmap_window = ULLONG_MAX; +#else + self->mmap_window = 32 * 1024 * 1024ULL; +#endif self->machines = RB_ROOT; self->repipe = repipe; INIT_LIST_HEAD(&self->ordered_samples.samples); @@ -738,18 +746,14 @@ int __perf_session__process_events(struct perf_session *session, u64 data_offset, u64 data_size, u64 file_size, struct perf_event_ops *ops) { - u64 head, page_offset, file_offset, file_pos; + u64 head, page_offset, file_offset, file_pos, progress_next; int err, mmap_prot, mmap_flags; struct ui_progress *progress; - size_t page_size; + size_t page_size, mmap_size; event_t *event; uint32_t size; char *buf; - progress = ui_progress__new("Processing events...", session->size); - if (progress == NULL) - return -1; - perf_event_ops__fill_defaults(ops); page_size = sysconf(_SC_PAGESIZE); @@ -761,6 +765,15 @@ int __perf_session__process_events(struct perf_session *session, if (data_offset + data_size < file_size) file_size = data_offset + data_size; + progress_next = file_size / 16; + progress = ui_progress__new("Processing events...", file_size); + if (progress == NULL) + return -1; + + mmap_size = session->mmap_window; + if (mmap_size > file_size) + mmap_size = file_size; + mmap_prot = PROT_READ; mmap_flags = MAP_SHARED; @@ -769,15 +782,14 @@ int __perf_session__process_events(struct perf_session *session, mmap_flags = MAP_PRIVATE; } remap: - buf = mmap(NULL, page_size * session->mmap_window, mmap_prot, - mmap_flags, session->fd, file_offset); + buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, session->fd, + file_offset); if (buf == MAP_FAILED) { pr_err("failed to mmap file\n"); err = -errno; goto out_err; } file_pos = file_offset + head; - ui_progress__update(progress, file_offset); more: event = (event_t *)(buf + head); @@ -788,10 +800,10 @@ more: if (size == 0) size = 8; - if (head + event->header.size >= page_size * session->mmap_window) { + if (head + event->header.size >= mmap_size) { int munmap_ret; - munmap_ret = munmap(buf, page_size * session->mmap_window); + munmap_ret = munmap(buf, mmap_size); assert(munmap_ret == 0); page_offset = page_size * (head / page_size); @@ -823,6 +835,11 @@ more: head += size; file_pos += size; + if (file_pos >= progress_next) { + progress_next += file_size / 16; + ui_progress__update(progress, file_pos); + } + if (file_pos < file_size) goto more; -- cgit v1.2.3 From fe17420784a6d3602e98f798731369fa05936cbe Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 30 Nov 2010 17:49:49 +0000 Subject: perf session: Keep file mmaped instead of malloc/memcpy Profiling perf with perf revealed that a large part of the processing time is spent in malloc/memcpy/free in the sample ordering code. That code copies the data from the mmap into malloc'ed memory. That's silly. We can keep the mmap and just store the pointer in the queuing data structure. For 64 bit this is not a problem as we map the whole file anyway. On 32bit we keep 8 maps around and unmap the oldest before mmaping the next chunk of the file. Performance gain: 2.95s -> 1.23s (Faktor 2.4) Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Frederic Weisbecker LKML-Reference: <20101130163820.278787719@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 752577fdb16e..c98958314102 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -418,7 +418,6 @@ static void flush_sample_queue(struct perf_session *s, os->last_flush = iter->timestamp; list_del(&iter->list); - free(iter->event); free(iter); } @@ -531,7 +530,6 @@ static int queue_sample_event(event_t *event, struct sample_data *data, u64 timestamp = data->time; struct sample_queue *new; - if (timestamp < s->ordered_samples.last_flush) { printf("Warning: Timestamp below last timeslice flush\n"); return -EINVAL; @@ -542,14 +540,7 @@ static int queue_sample_event(event_t *event, struct sample_data *data, return -ENOMEM; new->timestamp = timestamp; - - new->event = malloc(event->header.size); - if (!new->event) { - free(new); - return -ENOMEM; - } - - memcpy(new->event, event, event->header.size); + new->event = event; __queue_sample_event(new, s); @@ -747,12 +738,12 @@ int __perf_session__process_events(struct perf_session *session, u64 file_size, struct perf_event_ops *ops) { u64 head, page_offset, file_offset, file_pos, progress_next; - int err, mmap_prot, mmap_flags; + int err, mmap_prot, mmap_flags, map_idx = 0; struct ui_progress *progress; size_t page_size, mmap_size; + char *buf, *mmaps[8]; event_t *event; uint32_t size; - char *buf; perf_event_ops__fill_defaults(ops); @@ -774,6 +765,8 @@ int __perf_session__process_events(struct perf_session *session, if (mmap_size > file_size) mmap_size = file_size; + memset(mmaps, 0, sizeof(mmaps)); + mmap_prot = PROT_READ; mmap_flags = MAP_SHARED; @@ -789,6 +782,8 @@ remap: err = -errno; goto out_err; } + mmaps[map_idx] = buf; + map_idx = (map_idx + 1) & (ARRAY_SIZE(mmaps) - 1); file_pos = file_offset + head; more: @@ -801,10 +796,10 @@ more: size = 8; if (head + event->header.size >= mmap_size) { - int munmap_ret; - - munmap_ret = munmap(buf, mmap_size); - assert(munmap_ret == 0); + if (mmaps[map_idx]) { + munmap(mmaps[map_idx], mmap_size); + mmaps[map_idx] = NULL; + } page_offset = page_size * (head / page_size); file_offset += page_offset; -- cgit v1.2.3 From 020bb75a6deeca5ebeae531dc7378c157affc8fd Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 30 Nov 2010 17:49:53 +0000 Subject: perf session: Cache sample objects When the sample queue is flushed we free the sample reference objects. Though we need to malloc new objects when we process further. Stop the malloc/free orgy and cache the already allocated object for resuage. Only allocate when the cache is empty. Performance gain: ~ 10% Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Frederic Weisbecker LKML-Reference: <20101130163820.338488630@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 30 ++++++++++++++++++++++++++---- tools/perf/util/session.h | 1 + 2 files changed, 27 insertions(+), 4 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index c98958314102..9fef587ff11f 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -113,6 +113,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc self->machines = RB_ROOT; self->repipe = repipe; INIT_LIST_HEAD(&self->ordered_samples.samples); + INIT_LIST_HEAD(&self->ordered_samples.sample_cache); machine__init(&self->host_machine, "", HOST_KERNEL_ID); if (mode == O_RDONLY) { @@ -398,6 +399,19 @@ struct sample_queue { struct list_head list; }; +static void perf_session_free_sample_buffers(struct perf_session *session) +{ + struct ordered_samples *os = &session->ordered_samples; + + while (!list_empty(&os->sample_cache)) { + struct sample_queue *sq; + + sq = list_entry(os->sample_cache.next, struct sample_queue, list); + list_del(&sq->list); + free(sq); + } +} + static void flush_sample_queue(struct perf_session *s, struct perf_event_ops *ops) { @@ -418,7 +432,7 @@ static void flush_sample_queue(struct perf_session *s, os->last_flush = iter->timestamp; list_del(&iter->list); - free(iter); + list_add(&iter->list, &os->sample_cache); } if (list_empty(head)) { @@ -527,6 +541,7 @@ static void __queue_sample_event(struct sample_queue *new, static int queue_sample_event(event_t *event, struct sample_data *data, struct perf_session *s) { + struct list_head *sc = &s->ordered_samples.sample_cache; u64 timestamp = data->time; struct sample_queue *new; @@ -535,9 +550,14 @@ static int queue_sample_event(event_t *event, struct sample_data *data, return -EINVAL; } - new = malloc(sizeof(*new)); - if (!new) - return -ENOMEM; + if (!list_empty(sc)) { + new = list_entry(sc->next, struct sample_queue, list); + list_del(&new->list); + } else { + new = malloc(sizeof(*new)); + if (!new) + return -ENOMEM; + } new->timestamp = timestamp; new->event = event; @@ -730,6 +750,7 @@ more: done: err = 0; out_err: + perf_session_free_sample_buffers(self); return err; } @@ -862,6 +883,7 @@ out_err: session->hists.stats.nr_unknown_events); } + perf_session_free_sample_buffers(session); return err; } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index a00f32ed1c79..e4a7ff2ba8d5 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -18,6 +18,7 @@ struct ordered_samples { u64 next_flush; u64 max_timestamp; struct list_head samples; + struct list_head sample_cache; struct sample_queue *last_sample; }; -- cgit v1.2.3 From 5c891f3840a7a330c96d7203d4bb5be6fa033724 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 30 Nov 2010 17:49:55 +0000 Subject: perf session: Allocate chunks of sample objects The ordered sample code allocates singular reference objects struct sample_queue which have 48byte size on 64bit and 20 bytes on 32bit. That's silly. Allocate ~64k sized chunks and hand them out. Performance gain: ~ 15% Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Frederic Weisbecker LKML-Reference: <20101130163820.398713983@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 21 ++++++++++++++++----- tools/perf/util/session.h | 3 +++ 2 files changed, 19 insertions(+), 5 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 9fef587ff11f..52672dad1fe9 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -114,6 +114,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc self->repipe = repipe; INIT_LIST_HEAD(&self->ordered_samples.samples); INIT_LIST_HEAD(&self->ordered_samples.sample_cache); + INIT_LIST_HEAD(&self->ordered_samples.to_free); machine__init(&self->host_machine, "", HOST_KERNEL_ID); if (mode == O_RDONLY) { @@ -403,10 +404,10 @@ static void perf_session_free_sample_buffers(struct perf_session *session) { struct ordered_samples *os = &session->ordered_samples; - while (!list_empty(&os->sample_cache)) { + while (!list_empty(&os->to_free)) { struct sample_queue *sq; - sq = list_entry(os->sample_cache.next, struct sample_queue, list); + sq = list_entry(os->to_free.next, struct sample_queue, list); list_del(&sq->list); free(sq); } @@ -538,10 +539,13 @@ static void __queue_sample_event(struct sample_queue *new, } } +#define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) + static int queue_sample_event(event_t *event, struct sample_data *data, struct perf_session *s) { - struct list_head *sc = &s->ordered_samples.sample_cache; + struct ordered_samples *os = &s->ordered_samples; + struct list_head *sc = &os->sample_cache; u64 timestamp = data->time; struct sample_queue *new; @@ -553,10 +557,17 @@ static int queue_sample_event(event_t *event, struct sample_data *data, if (!list_empty(sc)) { new = list_entry(sc->next, struct sample_queue, list); list_del(&new->list); + } else if (os->sample_buffer) { + new = os->sample_buffer + os->sample_buffer_idx; + if (++os->sample_buffer_idx == MAX_SAMPLE_BUFFER) + os->sample_buffer = NULL; } else { - new = malloc(sizeof(*new)); - if (!new) + os->sample_buffer = malloc(MAX_SAMPLE_BUFFER * sizeof(*new)); + if (!os->sample_buffer) return -ENOMEM; + list_add(&os->sample_buffer->list, &os->to_free); + os->sample_buffer_idx = 2; + new = os->sample_buffer + 1; } new->timestamp = timestamp; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index e4a7ff2ba8d5..5bf6efa3788a 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -19,7 +19,10 @@ struct ordered_samples { u64 max_timestamp; struct list_head samples; struct list_head sample_cache; + struct list_head to_free; + struct sample_queue *sample_buffer; struct sample_queue *last_sample; + int sample_buffer_idx; }; struct perf_session { -- cgit v1.2.3 From 640c03ce837fe8d4b56342aba376ea0da3960459 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 2 Dec 2010 14:10:21 -0200 Subject: perf session: Parse sample earlier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At perf_session__process_event, so that we reduce the number of lines in eache tool sample processing routine that now receives a sample_data pointer already parsed. This will also be useful in the next patch, where we'll allow sample the identity fields in MMAP, FORK, EXIT, etc, when it will be possible to see (cpu, timestamp) just after before every event. Also validate callchains in perf_session__process_event, i.e. as early as possible, and keep a counter of the number of events discarded due to invalid callchains, warning the user about it if it happens. There is an assumption that was kept that all events have the same sample_type, that will be dealt with in the future, when this preexisting limitation will be removed. Tested-by: Thomas Gleixner Reviewed-by: Thomas Gleixner Acked-by: Ian Munsie Acked-by: Thomas Gleixner Cc: Frédéric Weisbecker Cc: Ian Munsie Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Stephane Eranian LKML-Reference: <1291318772-30880-4-git-send-email-acme@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 6 +-- tools/perf/builtin-diff.c | 11 +++-- tools/perf/builtin-inject.c | 39 ++++++++++------ tools/perf/builtin-kmem.c | 21 ++------- tools/perf/builtin-lock.c | 12 ++--- tools/perf/builtin-record.c | 7 +++ tools/perf/builtin-report.c | 15 +++--- tools/perf/builtin-sched.c | 21 +++------ tools/perf/builtin-script.c | 34 +++++--------- tools/perf/builtin-timechart.c | 40 ++++++++-------- tools/perf/builtin-top.c | 4 +- tools/perf/util/build-id.c | 7 ++- tools/perf/util/event.c | 66 +++++++++++--------------- tools/perf/util/event.h | 20 +++++--- tools/perf/util/header.c | 11 ++--- tools/perf/util/hist.h | 2 + tools/perf/util/session.c | 103 +++++++++++++++++++++++++++++++---------- tools/perf/util/session.h | 9 ++-- 18 files changed, 236 insertions(+), 192 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 6d5604d8df95..569a2761b90a 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -58,12 +58,12 @@ static int hists__add_entry(struct hists *self, struct addr_location *al) return hist_entry__inc_addr_samples(he, al->addr); } -static int process_sample_event(event_t *event, struct perf_session *session) +static int process_sample_event(event_t *event, struct sample_data *sample, + struct perf_session *session) { struct addr_location al; - struct sample_data data; - if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) { + if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) { pr_warning("problem processing %d event, skipping it.\n", event->header.type); return -1; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 221b823bc26f..5e1a043aae03 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -30,12 +30,13 @@ static int hists__add_entry(struct hists *self, return -ENOMEM; } -static int diff__process_sample_event(event_t *event, struct perf_session *session) +static int diff__process_sample_event(event_t *event, + struct sample_data *sample, + struct perf_session *session) { struct addr_location al; - struct sample_data data = { .period = 1, }; - if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) { + if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) { pr_warning("problem processing %d event, skipping it.\n", event->header.type); return -1; @@ -44,12 +45,12 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi if (al.filtered || al.sym == NULL) return 0; - if (hists__add_entry(&session->hists, &al, data.period)) { + if (hists__add_entry(&session->hists, &al, sample->period)) { pr_warning("problem incrementing symbol period, skipping event\n"); return -1; } - session->hists.stats.total_period += data.period; + session->hists.stats.total_period += sample->period; return 0; } diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 8e3e47b064ce..4b66b8579410 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -16,8 +16,8 @@ static char const *input_name = "-"; static bool inject_build_ids; -static int event__repipe(event_t *event __used, - struct perf_session *session __used) +static int event__repipe_synth(event_t *event, + struct perf_session *session __used) { uint32_t size; void *buf = event; @@ -36,22 +36,30 @@ static int event__repipe(event_t *event __used, return 0; } -static int event__repipe_mmap(event_t *self, struct perf_session *session) +static int event__repipe(event_t *event, struct sample_data *sample __used, + struct perf_session *session) +{ + return event__repipe_synth(event, session); +} + +static int event__repipe_mmap(event_t *self, struct sample_data *sample, + struct perf_session *session) { int err; - err = event__process_mmap(self, session); - event__repipe(self, session); + err = event__process_mmap(self, sample, session); + event__repipe(self, sample, session); return err; } -static int event__repipe_task(event_t *self, struct perf_session *session) +static int event__repipe_task(event_t *self, struct sample_data *sample, + struct perf_session *session) { int err; - err = event__process_task(self, session); - event__repipe(self, session); + err = event__process_task(self, sample, session); + event__repipe(self, sample, session); return err; } @@ -61,7 +69,7 @@ static int event__repipe_tracing_data(event_t *self, { int err; - event__repipe(self, session); + event__repipe_synth(self, session); err = event__process_tracing_data(self, session); return err; @@ -111,7 +119,8 @@ static int dso__inject_build_id(struct dso *self, struct perf_session *session) return 0; } -static int event__inject_buildid(event_t *event, struct perf_session *session) +static int event__inject_buildid(event_t *event, struct sample_data *sample, + struct perf_session *session) { struct addr_location al; struct thread *thread; @@ -146,7 +155,7 @@ static int event__inject_buildid(event_t *event, struct perf_session *session) } repipe: - event__repipe(event, session); + event__repipe(event, sample, session); return 0; } @@ -160,10 +169,10 @@ struct perf_event_ops inject_ops = { .read = event__repipe, .throttle = event__repipe, .unthrottle = event__repipe, - .attr = event__repipe, - .event_type = event__repipe, - .tracing_data = event__repipe, - .build_id = event__repipe, + .attr = event__repipe_synth, + .event_type = event__repipe_synth, + .tracing_data = event__repipe_synth, + .build_id = event__repipe_synth, }; extern volatile int session_done; diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 31f60a2535e0..d0a652e9c708 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -304,22 +304,11 @@ process_raw_event(event_t *raw_event __used, void *data, } } -static int process_sample_event(event_t *event, struct perf_session *session) +static int process_sample_event(event_t *event, struct sample_data *sample, + struct perf_session *session) { - struct sample_data data; - struct thread *thread; + struct thread *thread = perf_session__findnew(session, event->ip.pid); - memset(&data, 0, sizeof(data)); - data.time = -1; - data.cpu = -1; - data.period = 1; - - event__parse_sample(event, session->sample_type, &data); - - dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, - data.pid, data.tid, data.ip, data.period); - - thread = perf_session__findnew(session, event->ip.pid); if (thread == NULL) { pr_debug("problem processing %d event, skipping it.\n", event->header.type); @@ -328,8 +317,8 @@ static int process_sample_event(event_t *event, struct perf_session *session) dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); - process_raw_event(event, data.raw_data, data.cpu, - data.time, thread); + process_raw_event(event, sample->raw_data, sample->cpu, + sample->time, thread); return 0; } diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 8452a2ae2191..92d3da52a5cf 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -834,22 +834,18 @@ static void dump_info(void) die("Unknown type of information\n"); } -static int process_sample_event(event_t *self, struct perf_session *s) +static int process_sample_event(event_t *self, struct sample_data *sample, + struct perf_session *s) { - struct sample_data data; - struct thread *thread; + struct thread *thread = perf_session__findnew(s, sample->tid); - bzero(&data, sizeof(data)); - event__parse_sample(self, s->sample_type, &data); - - thread = perf_session__findnew(s, data.tid); if (thread == NULL) { pr_debug("problem processing %d event, skipping it.\n", self->header.type); return -1; } - process_raw_event(data.raw_data, data.cpu, data.time, thread); + process_raw_event(sample->raw_data, sample->cpu, sample->time, thread); return 0; } diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 024e1441d76b..b34de9291c27 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -36,6 +36,7 @@ static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; static u64 user_interval = ULLONG_MAX; static u64 default_interval = 0; +static u64 sample_type; static int nr_cpus = 0; static unsigned int page_size; @@ -129,6 +130,7 @@ static void write_output(void *buf, size_t size) } static int process_synthesized_event(event_t *event, + struct sample_data *sample __used, struct perf_session *self __used) { write_output(event, event->header.size); @@ -287,6 +289,9 @@ static void create_counter(int counter, int cpu) attr->sample_type |= PERF_SAMPLE_CPU; } + if (!sample_type) + sample_type = attr->sample_type; + attr->mmap = track; attr->comm = track; attr->inherit = !no_inherit; @@ -642,6 +647,8 @@ static int __cmd_record(int argc, const char **argv) open_counters(cpumap[i]); } + perf_session__set_sample_type(session, sample_type); + if (pipe_output) { err = perf_header__write_pipe(output); if (err < 0) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 5de405d45230..904519fba434 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -150,13 +150,13 @@ static int add_event_total(struct perf_session *session, return 0; } -static int process_sample_event(event_t *event, struct perf_session *session) +static int process_sample_event(event_t *event, struct sample_data *sample, + struct perf_session *session) { - struct sample_data data = { .period = 1, }; struct addr_location al; struct perf_event_attr *attr; - if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) { + if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) { fprintf(stderr, "problem processing %d event, skipping it.\n", event->header.type); return -1; @@ -165,14 +165,14 @@ static int process_sample_event(event_t *event, struct perf_session *session) if (al.filtered || (hide_unresolved && al.sym == NULL)) return 0; - if (perf_session__add_hist_entry(session, &al, &data)) { + if (perf_session__add_hist_entry(session, &al, sample)) { pr_debug("problem incrementing symbol period, skipping event\n"); return -1; } - attr = perf_header__find_attr(data.id, &session->header); + attr = perf_header__find_attr(sample->id, &session->header); - if (add_event_total(session, &data, attr)) { + if (add_event_total(session, sample, attr)) { pr_debug("problem adding event period\n"); return -1; } @@ -180,7 +180,8 @@ static int process_sample_event(event_t *event, struct perf_session *session) return 0; } -static int process_read_event(event_t *event, struct perf_session *session __used) +static int process_read_event(event_t *event, struct sample_data *sample __used, + struct perf_session *session __used) { struct perf_event_attr *attr; diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 26523c939791..73d1e30b0fed 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1606,25 +1606,15 @@ process_raw_event(event_t *raw_event __used, struct perf_session *session, process_sched_migrate_task_event(data, session, event, cpu, timestamp, thread); } -static int process_sample_event(event_t *event, struct perf_session *session) +static int process_sample_event(event_t *event, struct sample_data *sample, + struct perf_session *session) { - struct sample_data data; struct thread *thread; if (!(session->sample_type & PERF_SAMPLE_RAW)) return 0; - memset(&data, 0, sizeof(data)); - data.time = -1; - data.cpu = -1; - data.period = -1; - - event__parse_sample(event, session->sample_type, &data); - - dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, - data.pid, data.tid, data.ip, data.period); - - thread = perf_session__findnew(session, data.pid); + thread = perf_session__findnew(session, sample->pid); if (thread == NULL) { pr_debug("problem processing %d event, skipping it.\n", event->header.type); @@ -1633,10 +1623,11 @@ static int process_sample_event(event_t *event, struct perf_session *session) dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); - if (profile_cpu != -1 && profile_cpu != (int)data.cpu) + if (profile_cpu != -1 && profile_cpu != (int)sample->cpu) return 0; - process_raw_event(event, session, data.raw_data, data.cpu, data.time, thread); + process_raw_event(event, session, sample->raw_data, sample->cpu, + sample->time, thread); return 0; } diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 4539551ab40e..683a30572cc5 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -63,22 +63,11 @@ static int cleanup_scripting(void) static char const *input_name = "perf.data"; -static int process_sample_event(event_t *event, struct perf_session *session) +static int process_sample_event(event_t *event, struct sample_data *sample, + struct perf_session *session) { - struct sample_data data; - struct thread *thread; + struct thread *thread = perf_session__findnew(session, event->ip.pid); - memset(&data, 0, sizeof(data)); - data.time = -1; - data.cpu = -1; - data.period = 1; - - event__parse_sample(event, session->sample_type, &data); - - dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, - data.pid, data.tid, data.ip, data.period); - - thread = perf_session__findnew(session, event->ip.pid); if (thread == NULL) { pr_debug("problem processing %d event, skipping it.\n", event->header.type); @@ -87,13 +76,13 @@ static int process_sample_event(event_t *event, struct perf_session *session) if (session->sample_type & PERF_SAMPLE_RAW) { if (debug_mode) { - if (data.time < last_timestamp) { + if (sample->time < last_timestamp) { pr_err("Samples misordered, previous: %llu " "this: %llu\n", last_timestamp, - data.time); + sample->time); nr_unordered++; } - last_timestamp = data.time; + last_timestamp = sample->time; return 0; } /* @@ -101,18 +90,19 @@ static int process_sample_event(event_t *event, struct perf_session *session) * field, although it should be the same than this perf * event pid */ - scripting_ops->process_event(data.cpu, data.raw_data, - data.raw_size, - data.time, thread->comm); + scripting_ops->process_event(sample->cpu, sample->raw_data, + sample->raw_size, + sample->time, thread->comm); } - session->hists.stats.total_period += data.period; + session->hists.stats.total_period += sample->period; return 0; } static u64 nr_lost; -static int process_lost_event(event_t *event, struct perf_session *session __used) +static int process_lost_event(event_t *event, struct sample_data *sample __used, + struct perf_session *session __used) { nr_lost += event->lost.lost; diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 9bcc38f0b706..1f158dc03349 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -272,19 +272,22 @@ static int cpus_cstate_state[MAX_CPUS]; static u64 cpus_pstate_start_times[MAX_CPUS]; static u64 cpus_pstate_state[MAX_CPUS]; -static int process_comm_event(event_t *event, struct perf_session *session __used) +static int process_comm_event(event_t *event, struct sample_data *sample __used, + struct perf_session *session __used) { pid_set_comm(event->comm.tid, event->comm.comm); return 0; } -static int process_fork_event(event_t *event, struct perf_session *session __used) +static int process_fork_event(event_t *event, struct sample_data *sample __used, + struct perf_session *session __used) { pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); return 0; } -static int process_exit_event(event_t *event, struct perf_session *session __used) +static int process_exit_event(event_t *event, struct sample_data *sample __used, + struct perf_session *session __used) { pid_exit(event->fork.pid, event->fork.time); return 0; @@ -470,24 +473,21 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) } -static int process_sample_event(event_t *event, struct perf_session *session) +static int process_sample_event(event_t *event __used, + struct sample_data *sample, + struct perf_session *session) { - struct sample_data data; struct trace_entry *te; - memset(&data, 0, sizeof(data)); - - event__parse_sample(event, session->sample_type, &data); - if (session->sample_type & PERF_SAMPLE_TIME) { - if (!first_time || first_time > data.time) - first_time = data.time; - if (last_time < data.time) - last_time = data.time; + if (!first_time || first_time > sample->time) + first_time = sample->time; + if (last_time < sample->time) + last_time = sample->time; } - te = (void *)data.raw_data; - if (session->sample_type & PERF_SAMPLE_RAW && data.raw_size > 0) { + te = (void *)sample->raw_data; + if (session->sample_type & PERF_SAMPLE_RAW && sample->raw_size > 0) { char *event_str; struct power_entry *pe; @@ -499,19 +499,19 @@ static int process_sample_event(event_t *event, struct perf_session *session) return 0; if (strcmp(event_str, "power:power_start") == 0) - c_state_start(pe->cpu_id, data.time, pe->value); + c_state_start(pe->cpu_id, sample->time, pe->value); if (strcmp(event_str, "power:power_end") == 0) - c_state_end(pe->cpu_id, data.time); + c_state_end(pe->cpu_id, sample->time); if (strcmp(event_str, "power:power_frequency") == 0) - p_state_change(pe->cpu_id, data.time, pe->value); + p_state_change(pe->cpu_id, sample->time, pe->value); if (strcmp(event_str, "sched:sched_wakeup") == 0) - sched_wakeup(data.cpu, data.time, data.pid, te); + sched_wakeup(sample->cpu, sample->time, sample->pid, te); if (strcmp(event_str, "sched:sched_switch") == 0) - sched_switch(data.cpu, data.time, te); + sched_switch(sample->cpu, sample->time, te); } return 0; } diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 3d2b47d5121a..5aa29e1e855a 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1025,6 +1025,8 @@ static void event__process_sample(const event_t *self, if (self->header.misc & PERF_RECORD_MISC_EXACT_IP) exact_samples++; + event__parse_sample(self, session->sample_type, &data); + if (event__preprocess_sample(self, session, &al, &data, symbol_filter) < 0 || al.filtered) @@ -1155,7 +1157,7 @@ static void perf_session__mmap_read_counter(struct perf_session *self, if (event->header.type == PERF_RECORD_SAMPLE) event__process_sample(event, self, md->counter); else - event__process(event, self); + event__process(event, NULL, self); old += size; } diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index e437edb72417..deffb8c96071 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -14,7 +14,9 @@ #include #include "debug.h" -static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) +static int build_id__mark_dso_hit(event_t *event, + struct sample_data *sample __used, + struct perf_session *session) { struct addr_location al; u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; @@ -35,7 +37,8 @@ static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) return 0; } -static int event__exit_del_thread(event_t *self, struct perf_session *session) +static int event__exit_del_thread(event_t *self, struct sample_data *sample __used, + struct perf_session *session) { struct thread *thread = perf_session__findnew(session, self->fork.tid); diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 7260db75b93d..34510f441975 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -24,6 +24,15 @@ const char *event__name[] = { [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", }; +static struct sample_data synth_sample = { + .pid = -1, + .tid = -1, + .time = -1, + .stream_id = -1, + .cpu = -1, + .period = 1, +}; + static pid_t event__synthesize_comm(pid_t pid, int full, event__handler_t process, struct perf_session *session) @@ -75,7 +84,7 @@ out_race: if (!full) { ev.comm.tid = pid; - process(&ev, session); + process(&ev, &synth_sample, session); goto out_fclose; } @@ -93,7 +102,7 @@ out_race: ev.comm.tid = pid; - process(&ev, session); + process(&ev, &synth_sample, session); } closedir(tasks); @@ -173,7 +182,7 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, ev.mmap.pid = tgid; ev.mmap.tid = pid; - process(&ev, session); + process(&ev, &synth_sample, session); } } @@ -219,7 +228,7 @@ int event__synthesize_modules(event__handler_t process, memcpy(ev.mmap.filename, pos->dso->long_name, pos->dso->long_name_len + 1); - process(&ev, session); + process(&ev, &synth_sample, session); } return 0; @@ -331,7 +340,7 @@ int event__synthesize_kernel_mmap(event__handler_t process, ev.mmap.len = map->end - ev.mmap.start; ev.mmap.pid = machine->pid; - return process(&ev, session); + return process(&ev, &synth_sample, session); } static void thread__comm_adjust(struct thread *self, struct hists *hists) @@ -361,7 +370,8 @@ static int thread__set_comm_adjust(struct thread *self, const char *comm, return 0; } -int event__process_comm(event_t *self, struct perf_session *session) +int event__process_comm(event_t *self, struct sample_data *sample __used, + struct perf_session *session) { struct thread *thread = perf_session__findnew(session, self->comm.tid); @@ -376,7 +386,8 @@ int event__process_comm(event_t *self, struct perf_session *session) return 0; } -int event__process_lost(event_t *self, struct perf_session *session) +int event__process_lost(event_t *self, struct sample_data *sample __used, + struct perf_session *session) { dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); session->hists.stats.total_lost += self->lost.lost; @@ -485,7 +496,8 @@ out_problem: return -1; } -int event__process_mmap(event_t *self, struct perf_session *session) +int event__process_mmap(event_t *self, struct sample_data *sample __used, + struct perf_session *session) { struct machine *machine; struct thread *thread; @@ -526,7 +538,8 @@ out_problem: return 0; } -int event__process_task(event_t *self, struct perf_session *session) +int event__process_task(event_t *self, struct sample_data *sample __used, + struct perf_session *session) { struct thread *thread = perf_session__findnew(session, self->fork.tid); struct thread *parent = perf_session__findnew(session, self->fork.ptid); @@ -548,18 +561,19 @@ int event__process_task(event_t *self, struct perf_session *session) return 0; } -int event__process(event_t *event, struct perf_session *session) +int event__process(event_t *event, struct sample_data *sample, + struct perf_session *session) { switch (event->header.type) { case PERF_RECORD_COMM: - event__process_comm(event, session); + event__process_comm(event, sample, session); break; case PERF_RECORD_MMAP: - event__process_mmap(event, session); + event__process_mmap(event, sample, session); break; case PERF_RECORD_FORK: case PERF_RECORD_EXIT: - event__process_task(event, session); + event__process_task(event, sample, session); break; default: break; @@ -674,32 +688,8 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, symbol_filter_t filter) { u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - struct thread *thread; - - event__parse_sample(self, session->sample_type, data); + struct thread *thread = perf_session__findnew(session, self->ip.pid); - dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld cpu:%d\n", - self->header.misc, data->pid, data->tid, data->ip, - data->period, data->cpu); - - if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { - unsigned int i; - - dump_printf("... chain: nr:%Lu\n", data->callchain->nr); - - if (!ip_callchain__valid(data->callchain, self)) { - pr_debug("call-chain problem with event, " - "skipping it.\n"); - goto out_filtered; - } - - if (dump_trace) { - for (i = 0; i < data->callchain->nr; i++) - dump_printf("..... %2d: %016Lx\n", - i, data->callchain->ips[i]); - } - } - thread = perf_session__findnew(session, self->ip.pid); if (thread == NULL) return -1; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 8e790dae7026..08c400b83d57 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -135,7 +135,10 @@ void event__print_totals(void); struct perf_session; -typedef int (*event__handler_t)(event_t *event, struct perf_session *session); +typedef int (*event__handler_synth_t)(event_t *event, + struct perf_session *session); +typedef int (*event__handler_t)(event_t *event, struct sample_data *sample, + struct perf_session *session); int event__synthesize_thread(pid_t pid, event__handler_t process, struct perf_session *session); @@ -150,11 +153,16 @@ int event__synthesize_modules(event__handler_t process, struct perf_session *session, struct machine *machine); -int event__process_comm(event_t *self, struct perf_session *session); -int event__process_lost(event_t *self, struct perf_session *session); -int event__process_mmap(event_t *self, struct perf_session *session); -int event__process_task(event_t *self, struct perf_session *session); -int event__process(event_t *event, struct perf_session *session); +int event__process_comm(event_t *self, struct sample_data *sample, + struct perf_session *session); +int event__process_lost(event_t *self, struct sample_data *sample, + struct perf_session *session); +int event__process_mmap(event_t *self, struct sample_data *sample, + struct perf_session *session); +int event__process_task(event_t *self, struct sample_data *sample, + struct perf_session *session); +int event__process(event_t *event, struct sample_data *sample, + struct perf_session *session); struct addr_location; int event__preprocess_sample(const event_t *self, struct perf_session *session, diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f65d7dc127b6..fe652f3b0aa7 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -993,15 +993,14 @@ int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, ev->attr.header.type = PERF_RECORD_HEADER_ATTR; ev->attr.header.size = size; - err = process(ev, session); + err = process(ev, NULL, session); free(ev); return err; } -int event__synthesize_attrs(struct perf_header *self, - event__handler_t process, +int event__synthesize_attrs(struct perf_header *self, event__handler_t process, struct perf_session *session) { struct perf_header_attr *attr; @@ -1071,7 +1070,7 @@ int event__synthesize_event_type(u64 event_id, char *name, ev.event_type.header.size = sizeof(ev.event_type) - (sizeof(ev.event_type.event_type.name) - size); - err = process(&ev, session); + err = process(&ev, NULL, session); return err; } @@ -1126,7 +1125,7 @@ int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs, ev.tracing_data.header.size = sizeof(ev.tracing_data); ev.tracing_data.size = aligned_size; - process(&ev, session); + process(&ev, NULL, session); err = read_tracing_data(fd, pattrs, nb_events); write_padded(fd, NULL, 0, padding); @@ -1186,7 +1185,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, ev.build_id.header.size = sizeof(ev.build_id) + len; memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len); - err = process(&ev, session); + err = process(&ev, NULL, session); return err; } diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 587d375d3430..ee789856a8c9 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -52,8 +52,10 @@ struct sym_priv { struct events_stats { u64 total_period; u64 total_lost; + u64 total_invalid_chains; u32 nr_events[PERF_RECORD_HEADER_MAX]; u32 nr_unknown_events; + u32 nr_invalid_chains; }; enum hist_column { diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 52672dad1fe9..08ec018966a8 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -70,6 +70,11 @@ void perf_session__update_sample_type(struct perf_session *self) self->sample_type = perf_header__sample_type(&self->header); } +void perf_session__set_sample_type(struct perf_session *session, u64 type) +{ + session->sample_type = type; +} + int perf_session__create_kernel_maps(struct perf_session *self) { int ret = machine__create_kernel_maps(&self->host_machine); @@ -240,7 +245,15 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, return syms; } +static int process_event_synth_stub(event_t *event __used, + struct perf_session *session __used) +{ + dump_printf(": unhandled!\n"); + return 0; +} + static int process_event_stub(event_t *event __used, + struct sample_data *sample __used, struct perf_session *session __used) { dump_printf(": unhandled!\n"); @@ -280,13 +293,13 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) if (handler->unthrottle == NULL) handler->unthrottle = process_event_stub; if (handler->attr == NULL) - handler->attr = process_event_stub; + handler->attr = process_event_synth_stub; if (handler->event_type == NULL) - handler->event_type = process_event_stub; + handler->event_type = process_event_synth_stub; if (handler->tracing_data == NULL) - handler->tracing_data = process_event_stub; + handler->tracing_data = process_event_synth_stub; if (handler->build_id == NULL) - handler->build_id = process_event_stub; + handler->build_id = process_event_synth_stub; if (handler->finished_round == NULL) { if (handler->ordered_samples) handler->finished_round = process_finished_round; @@ -419,6 +432,7 @@ static void flush_sample_queue(struct perf_session *s, struct ordered_samples *os = &s->ordered_samples; struct list_head *head = &os->samples; struct sample_queue *tmp, *iter; + struct sample_data sample; u64 limit = os->next_flush; u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; @@ -429,7 +443,8 @@ static void flush_sample_queue(struct perf_session *s, if (iter->timestamp > limit) break; - ops->sample(iter->event, s); + event__parse_sample(iter->event, s->sample_type, &sample); + ops->sample(iter->event, &sample, s); os->last_flush = iter->timestamp; list_del(&iter->list); @@ -578,20 +593,29 @@ static int queue_sample_event(event_t *event, struct sample_data *data, return 0; } -static int perf_session__process_sample(event_t *event, struct perf_session *s, +static int perf_session__process_sample(event_t *event, + struct sample_data *sample, + struct perf_session *s, struct perf_event_ops *ops) { - struct sample_data data; - if (!ops->ordered_samples) - return ops->sample(event, s); + return ops->sample(event, sample, s); + + queue_sample_event(event, sample, s); + return 0; +} - bzero(&data, sizeof(struct sample_data)); - event__parse_sample(event, s->sample_type, &data); +static void callchain__dump(struct sample_data *sample) +{ + unsigned int i; - queue_sample_event(event, &data, s); + if (!dump_trace) + return; - return 0; + printf("... chain: nr:%Lu\n", sample->callchain->nr); + + for (i = 0; i < sample->callchain->nr; i++) + printf("..... %2d: %016Lx\n", i, sample->callchain->ips[i]); } static int perf_session__process_event(struct perf_session *self, @@ -599,8 +623,16 @@ static int perf_session__process_event(struct perf_session *self, struct perf_event_ops *ops, u64 file_offset) { + struct sample_data sample; + trace_event(event); + if (self->header.needs_swap && event__swap_ops[event->header.type]) + event__swap_ops[event->header.type](event); + + if (event->header.type == PERF_RECORD_SAMPLE) + event__parse_sample(event, self->sample_type, &sample); + if (event->header.type < PERF_RECORD_HEADER_MAX) { dump_printf("%#Lx [%#x]: PERF_RECORD_%s", file_offset, event->header.size, @@ -608,28 +640,41 @@ static int perf_session__process_event(struct perf_session *self, hists__inc_nr_events(&self->hists, event->header.type); } - if (self->header.needs_swap && event__swap_ops[event->header.type]) - event__swap_ops[event->header.type](event); - switch (event->header.type) { case PERF_RECORD_SAMPLE: - return perf_session__process_sample(event, self, ops); + dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, + sample.pid, sample.tid, sample.ip, sample.period); + + if (self->sample_type & PERF_SAMPLE_CALLCHAIN) { + if (!ip_callchain__valid(sample.callchain, event)) { + pr_debug("call-chain problem with event, " + "skipping it.\n"); + ++self->hists.stats.nr_invalid_chains; + self->hists.stats.total_invalid_chains += sample.period; + return 0; + } + + callchain__dump(&sample); + } + + return perf_session__process_sample(event, &sample, self, ops); + case PERF_RECORD_MMAP: - return ops->mmap(event, self); + return ops->mmap(event, &sample, self); case PERF_RECORD_COMM: - return ops->comm(event, self); + return ops->comm(event, &sample, self); case PERF_RECORD_FORK: - return ops->fork(event, self); + return ops->fork(event, &sample, self); case PERF_RECORD_EXIT: - return ops->exit(event, self); + return ops->exit(event, &sample, self); case PERF_RECORD_LOST: - return ops->lost(event, self); + return ops->lost(event, &sample, self); case PERF_RECORD_READ: - return ops->read(event, self); + return ops->read(event, &sample, self); case PERF_RECORD_THROTTLE: - return ops->throttle(event, self); + return ops->throttle(event, &sample, self); case PERF_RECORD_UNTHROTTLE: - return ops->unthrottle(event, self); + return ops->unthrottle(event, &sample, self); case PERF_RECORD_HEADER_ATTR: return ops->attr(event, self); case PERF_RECORD_HEADER_EVENT_TYPE: @@ -894,6 +939,14 @@ out_err: session->hists.stats.nr_unknown_events); } + if (session->hists.stats.nr_invalid_chains != 0) { + ui__warning("Found invalid callchains!\n\n" + "%u out of %u events were discarded for this reason.\n\n" + "Consider reporting to linux-kernel@vger.kernel.org.\n\n", + session->hists.stats.nr_invalid_chains, + session->hists.stats.nr_events[PERF_RECORD_SAMPLE]); + } + perf_session_free_sample_buffers(session); return err; } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 5bf6efa3788a..4578f86a6209 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -54,7 +54,9 @@ struct perf_session { struct perf_event_ops; -typedef int (*event_op)(event_t *self, struct perf_session *session); +typedef int (*event_op)(event_t *self, struct sample_data *sample, + struct perf_session *session); +typedef int (*event_synth_op)(event_t *self, struct perf_session *session); typedef int (*event_op2)(event_t *self, struct perf_session *session, struct perf_event_ops *ops); @@ -67,8 +69,8 @@ struct perf_event_ops { lost, read, throttle, - unthrottle, - attr, + unthrottle; + event_synth_op attr, event_type, tracing_data, build_id; @@ -104,6 +106,7 @@ int perf_session__create_kernel_maps(struct perf_session *self); int do_read(int fd, void *buf, size_t size); void perf_session__update_sample_type(struct perf_session *self); +void perf_session__set_sample_type(struct perf_session *session, u64 type); void perf_session__remove_thread(struct perf_session *self, struct thread *th); static inline -- cgit v1.2.3 From 9c90a61c7e4286aa5a38b314a2d8f5a1e70b5135 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 2 Dec 2010 10:25:28 -0200 Subject: perf tools: Ask for ID PERF_SAMPLE_ info on all PERF_RECORD_ events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So that we can use -T == --timestamp, asking for PERF_SAMPLE_TIME: $ perf record -aT $ perf report -D | grep PERF_RECORD_ 3 5951915425 0x47530 [0x58]: PERF_RECORD_SAMPLE(IP, 1): 16811/16811: 0xffffffff8138c1a2 period: 215979 cpu:3 3 5952026879 0x47588 [0x90]: PERF_RECORD_SAMPLE(IP, 1): 16811/16811: 0xffffffff810cb480 period: 215979 cpu:3 3 5952059959 0x47618 [0x38]: PERF_RECORD_FORK(6853:6853):(16811:16811) 3 5952138878 0x47650 [0x78]: PERF_RECORD_SAMPLE(IP, 1): 16811/16811: 0xffffffff811bac35 period: 431478 cpu:3 3 5952375068 0x476c8 [0x30]: PERF_RECORD_COMM: find:6853 3 5952395923 0x476f8 [0x50]: PERF_RECORD_MMAP 6853/6853: [0x400000(0x25000) @ 0]: /usr/bin/find 3 5952413756 0x47748 [0xa0]: PERF_RECORD_SAMPLE(IP, 1): 6853/6853: 0xffffffff810d080f period: 859332 cpu:3 3 5952419837 0x477e8 [0x58]: PERF_RECORD_MMAP 6853/6853: [0x3f44600000(0x21d000) @ 0]: /lib64/ld-2.5.so 3 5952437929 0x47840 [0x48]: PERF_RECORD_MMAP 6853/6853: [0x7fff7e1c9000(0x1000) @ 0x7fff7e1c9000]: [vdso] 3 5952570127 0x47888 [0x58]: PERF_RECORD_MMAP 6853/6853: [0x3f46200000(0x218000) @ 0]: /lib64/libselinux.so.1 3 5952623637 0x478e0 [0x58]: PERF_RECORD_MMAP 6853/6853: [0x3f44a00000(0x356000) @ 0]: /lib64/libc-2.5.so 3 5952675720 0x47938 [0x58]: PERF_RECORD_MMAP 6853/6853: [0x3f44e00000(0x204000) @ 0]: /lib64/libdl-2.5.so 3 5952710080 0x47990 [0x58]: PERF_RECORD_MMAP 6853/6853: [0x3f45a00000(0x246000) @ 0]: /lib64/libsepol.so.1 3 5952847802 0x479e8 [0x58]: PERF_RECORD_SAMPLE(IP, 1): 6853/6853: 0xffffffff813897f0 period: 1142536 cpu:3 First column is the cpu and the second the timestamp. That way we can investigate problems in the event stream. If the new perf binary is run on an older kernel, it will disable this feature automatically. Tested-by: Thomas Gleixner Reviewed-by: Thomas Gleixner Acked-by: Ian Munsie Acked-by: Thomas Gleixner Cc: Frédéric Weisbecker Cc: Ian Munsie Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Stephane Eranian LKML-Reference: <1291318772-30880-5-git-send-email-acme@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-record.txt | 5 + tools/perf/builtin-record.c | 16 ++ tools/perf/builtin-top.c | 14 +- tools/perf/util/event.c | 285 ++++++++++++++++++++++--------- tools/perf/util/event.h | 7 +- tools/perf/util/header.c | 18 ++ tools/perf/util/header.h | 1 + tools/perf/util/session.c | 66 ++++++- tools/perf/util/session.h | 3 + 9 files changed, 315 insertions(+), 100 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 0ad1bc75ab49..52462ae26455 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -108,6 +108,11 @@ OPTIONS --data:: Sample addresses. +-T:: +--timestamp:: + Sample timestamps. Use it with 'perf report -D' to see the timestamps, + for instance. + -n:: --no-samples:: Don't sample. diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index b34de9291c27..699dd2149c4b 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -49,6 +49,7 @@ static const char *output_name = "perf.data"; static int group = 0; static int realtime_prio = 0; static bool raw_samples = false; +static bool sample_id_all_avail = true; static bool system_wide = false; static pid_t target_pid = -1; static pid_t target_tid = -1; @@ -61,6 +62,7 @@ static bool call_graph = false; static bool inherit_stat = false; static bool no_samples = false; static bool sample_address = false; +static bool sample_time = false; static bool no_buildid = false; static bool no_buildid_cache = false; @@ -283,6 +285,9 @@ static void create_counter(int counter, int cpu) if (system_wide) attr->sample_type |= PERF_SAMPLE_CPU; + if (sample_time) + attr->sample_type |= PERF_SAMPLE_TIME; + if (raw_samples) { attr->sample_type |= PERF_SAMPLE_TIME; attr->sample_type |= PERF_SAMPLE_RAW; @@ -299,6 +304,8 @@ static void create_counter(int counter, int cpu) attr->disabled = 1; attr->enable_on_exec = 1; } +retry_sample_id: + attr->sample_id_all = sample_id_all_avail ? 1 : 0; for (thread_index = 0; thread_index < thread_num; thread_index++) { try_again: @@ -315,6 +322,12 @@ try_again: else if (err == ENODEV && cpu_list) { die("No such device - did you specify" " an out-of-range profile CPU?\n"); + } else if (err == EINVAL && sample_id_all_avail) { + /* + * Old kernel, no attr->sample_id_type_all field + */ + sample_id_all_avail = false; + goto retry_sample_id; } /* @@ -661,6 +674,8 @@ static int __cmd_record(int argc, const char **argv) post_processing_offset = lseek(output, 0, SEEK_CUR); + perf_session__set_sample_id_all(session, sample_id_all_avail); + if (pipe_output) { err = event__synthesize_attrs(&session->header, process_synthesized_event, @@ -841,6 +856,7 @@ const struct option record_options[] = { "per thread counts"), OPT_BOOLEAN('d', "data", &sample_address, "Sample addresses"), + OPT_BOOLEAN('T', "timestamp", &sample_time, "Sample timestamps"), OPT_BOOLEAN('n', "no-samples", &no_samples, "don't sample"), OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid_cache, diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 5aa29e1e855a..0515ce9d3d3e 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -977,12 +977,12 @@ static int symbol_filter(struct map *map, struct symbol *sym) } static void event__process_sample(const event_t *self, - struct perf_session *session, int counter) + struct sample_data *sample, + struct perf_session *session, int counter) { u64 ip = self->ip.ip; struct sym_entry *syme; struct addr_location al; - struct sample_data data; struct machine *machine; u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; @@ -1025,9 +1025,7 @@ static void event__process_sample(const event_t *self, if (self->header.misc & PERF_RECORD_MISC_EXACT_IP) exact_samples++; - event__parse_sample(self, session->sample_type, &data); - - if (event__preprocess_sample(self, session, &al, &data, + if (event__preprocess_sample(self, session, &al, sample, symbol_filter) < 0 || al.filtered) return; @@ -1107,6 +1105,7 @@ static void perf_session__mmap_read_counter(struct perf_session *self, unsigned int head = mmap_read_head(md); unsigned int old = md->prev; unsigned char *data = md->base + page_size; + struct sample_data sample; int diff; /* @@ -1154,10 +1153,11 @@ static void perf_session__mmap_read_counter(struct perf_session *self, event = &event_copy; } + event__parse_sample(event, self, &sample); if (event->header.type == PERF_RECORD_SAMPLE) - event__process_sample(event, self, md->counter); + event__process_sample(event, &sample, self, md->counter); else - event__process(event, NULL, self); + event__process(event, &sample, self); old += size; } diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 34510f441975..e4cdc1ebe0fb 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -33,11 +33,10 @@ static struct sample_data synth_sample = { .period = 1, }; -static pid_t event__synthesize_comm(pid_t pid, int full, +static pid_t event__synthesize_comm(event_t *event, pid_t pid, int full, event__handler_t process, struct perf_session *session) { - event_t ev; char filename[PATH_MAX]; char bf[BUFSIZ]; FILE *fp; @@ -58,34 +57,39 @@ out_race: return 0; } - memset(&ev.comm, 0, sizeof(ev.comm)); - while (!ev.comm.comm[0] || !ev.comm.pid) { - if (fgets(bf, sizeof(bf), fp) == NULL) - goto out_failure; + memset(&event->comm, 0, sizeof(event->comm)); + + while (!event->comm.comm[0] || !event->comm.pid) { + if (fgets(bf, sizeof(bf), fp) == NULL) { + pr_warning("couldn't get COMM and pgid, malformed %s\n", filename); + goto out; + } if (memcmp(bf, "Name:", 5) == 0) { char *name = bf + 5; while (*name && isspace(*name)) ++name; size = strlen(name) - 1; - memcpy(ev.comm.comm, name, size++); + memcpy(event->comm.comm, name, size++); } else if (memcmp(bf, "Tgid:", 5) == 0) { char *tgids = bf + 5; while (*tgids && isspace(*tgids)) ++tgids; - tgid = ev.comm.pid = atoi(tgids); + tgid = event->comm.pid = atoi(tgids); } } - ev.comm.header.type = PERF_RECORD_COMM; + event->comm.header.type = PERF_RECORD_COMM; size = ALIGN(size, sizeof(u64)); - ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size); - + memset(event->comm.comm + size, 0, session->id_hdr_size); + event->comm.header.size = (sizeof(event->comm) - + (sizeof(event->comm.comm) - size) + + session->id_hdr_size); if (!full) { - ev.comm.tid = pid; + event->comm.tid = pid; - process(&ev, &synth_sample, session); - goto out_fclose; + process(event, &synth_sample, session); + goto out; } snprintf(filename, sizeof(filename), "/proc/%d/task", pid); @@ -100,22 +104,19 @@ out_race: if (*end) continue; - ev.comm.tid = pid; + event->comm.tid = pid; - process(&ev, &synth_sample, session); + process(event, &synth_sample, session); } - closedir(tasks); -out_fclose: + closedir(tasks); +out: fclose(fp); - return tgid; -out_failure: - pr_warning("couldn't get COMM and pgid, malformed %s\n", filename); - return -1; + return tgid; } -static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, +static int event__synthesize_mmap_events(event_t *event, pid_t pid, pid_t tgid, event__handler_t process, struct perf_session *session) { @@ -133,29 +134,25 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, return -1; } + event->header.type = PERF_RECORD_MMAP; + /* + * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c + */ + event->header.misc = PERF_RECORD_MISC_USER; + while (1) { char bf[BUFSIZ], *pbf = bf; - event_t ev = { - .header = { - .type = PERF_RECORD_MMAP, - /* - * Just like the kernel, see __perf_event_mmap - * in kernel/perf_event.c - */ - .misc = PERF_RECORD_MISC_USER, - }, - }; int n; size_t size; if (fgets(bf, sizeof(bf), fp) == NULL) break; /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ - n = hex2u64(pbf, &ev.mmap.start); + n = hex2u64(pbf, &event->mmap.start); if (n < 0) continue; pbf += n + 1; - n = hex2u64(pbf, &ev.mmap.len); + n = hex2u64(pbf, &event->mmap.len); if (n < 0) continue; pbf += n + 3; @@ -170,19 +167,21 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, continue; pbf += 3; - n = hex2u64(pbf, &ev.mmap.pgoff); + n = hex2u64(pbf, &event->mmap.pgoff); size = strlen(execname); execname[size - 1] = '\0'; /* Remove \n */ - memcpy(ev.mmap.filename, execname, size); + memcpy(event->mmap.filename, execname, size); size = ALIGN(size, sizeof(u64)); - ev.mmap.len -= ev.mmap.start; - ev.mmap.header.size = (sizeof(ev.mmap) - - (sizeof(ev.mmap.filename) - size)); - ev.mmap.pid = tgid; - ev.mmap.tid = pid; - - process(&ev, &synth_sample, session); + event->mmap.len -= event->mmap.start; + event->mmap.header.size = (sizeof(event->mmap) - + (sizeof(event->mmap.filename) - size)); + memset(event->mmap.filename + size, 0, session->id_hdr_size); + event->mmap.header.size += session->id_hdr_size; + event->mmap.pid = tgid; + event->mmap.tid = pid; + + process(event, &synth_sample, session); } } @@ -196,20 +195,27 @@ int event__synthesize_modules(event__handler_t process, { struct rb_node *nd; struct map_groups *kmaps = &machine->kmaps; - u16 misc; + event_t *event = zalloc(sizeof(event->mmap) + session->id_hdr_size); + + if (event == NULL) { + pr_debug("Not enough memory synthesizing mmap event " + "for kernel modules\n"); + return -1; + } + + event->header.type = PERF_RECORD_MMAP; /* * kernel uses 0 for user space maps, see kernel/perf_event.c * __perf_event_mmap */ if (machine__is_host(machine)) - misc = PERF_RECORD_MISC_KERNEL; + event->header.misc = PERF_RECORD_MISC_KERNEL; else - misc = PERF_RECORD_MISC_GUEST_KERNEL; + event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL; for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { - event_t ev; size_t size; struct map *pos = rb_entry(nd, struct map, rb_node); @@ -217,39 +223,78 @@ int event__synthesize_modules(event__handler_t process, continue; size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); - memset(&ev, 0, sizeof(ev)); - ev.mmap.header.misc = misc; - ev.mmap.header.type = PERF_RECORD_MMAP; - ev.mmap.header.size = (sizeof(ev.mmap) - - (sizeof(ev.mmap.filename) - size)); - ev.mmap.start = pos->start; - ev.mmap.len = pos->end - pos->start; - ev.mmap.pid = machine->pid; - - memcpy(ev.mmap.filename, pos->dso->long_name, + event->mmap.header.type = PERF_RECORD_MMAP; + event->mmap.header.size = (sizeof(event->mmap) - + (sizeof(event->mmap.filename) - size)); + memset(event->mmap.filename + size, 0, session->id_hdr_size); + event->mmap.header.size += session->id_hdr_size; + event->mmap.start = pos->start; + event->mmap.len = pos->end - pos->start; + event->mmap.pid = machine->pid; + + memcpy(event->mmap.filename, pos->dso->long_name, pos->dso->long_name_len + 1); - process(&ev, &synth_sample, session); + process(event, &synth_sample, session); } + free(event); return 0; } -int event__synthesize_thread(pid_t pid, event__handler_t process, - struct perf_session *session) +static int __event__synthesize_thread(event_t *comm_event, event_t *mmap_event, + pid_t pid, event__handler_t process, + struct perf_session *session) { - pid_t tgid = event__synthesize_comm(pid, 1, process, session); + pid_t tgid = event__synthesize_comm(comm_event, pid, 1, process, + session); if (tgid == -1) return -1; - return event__synthesize_mmap_events(pid, tgid, process, session); + return event__synthesize_mmap_events(mmap_event, pid, tgid, + process, session); } -void event__synthesize_threads(event__handler_t process, - struct perf_session *session) +int event__synthesize_thread(pid_t pid, event__handler_t process, + struct perf_session *session) +{ + event_t *comm_event, *mmap_event; + int err = -1; + + comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size); + if (comm_event == NULL) + goto out; + + mmap_event = malloc(sizeof(mmap_event->mmap) + session->id_hdr_size); + if (mmap_event == NULL) + goto out_free_comm; + + err = __event__synthesize_thread(comm_event, mmap_event, pid, + process, session); + free(mmap_event); +out_free_comm: + free(comm_event); +out: + return err; +} + +int event__synthesize_threads(event__handler_t process, + struct perf_session *session) { DIR *proc; struct dirent dirent, *next; + event_t *comm_event, *mmap_event; + int err = -1; + + comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size); + if (comm_event == NULL) + goto out; + + mmap_event = malloc(sizeof(mmap_event->mmap) + session->id_hdr_size); + if (mmap_event == NULL) + goto out_free_comm; proc = opendir("/proc"); + if (proc == NULL) + goto out_free_mmap; while (!readdir_r(proc, &dirent, &next) && next) { char *end; @@ -258,10 +303,18 @@ void event__synthesize_threads(event__handler_t process, if (*end) /* only interested in proper numerical dirents */ continue; - event__synthesize_thread(pid, process, session); + __event__synthesize_thread(comm_event, mmap_event, pid, + process, session); } closedir(proc); + err = 0; +out_free_mmap: + free(mmap_event); +out_free_comm: + free(comm_event); +out: + return err; } struct process_symbol_args { @@ -295,18 +348,20 @@ int event__synthesize_kernel_mmap(event__handler_t process, char path[PATH_MAX]; char name_buff[PATH_MAX]; struct map *map; - - event_t ev = { - .header = { - .type = PERF_RECORD_MMAP, - }, - }; + int err; /* * We should get this from /sys/kernel/sections/.text, but till that is * available use this, and after it is use this as a fallback for older * kernels. */ struct process_symbol_args args = { .name = symbol_name, }; + event_t *event = zalloc(sizeof(event->mmap) + session->id_hdr_size); + + if (event == NULL) { + pr_debug("Not enough memory synthesizing mmap event " + "for kernel modules\n"); + return -1; + } mmap_name = machine__mmap_name(machine, name_buff, sizeof(name_buff)); if (machine__is_host(machine)) { @@ -314,10 +369,10 @@ int event__synthesize_kernel_mmap(event__handler_t process, * kernel uses PERF_RECORD_MISC_USER for user space maps, * see kernel/perf_event.c __perf_event_mmap */ - ev.header.misc = PERF_RECORD_MISC_KERNEL; + event->header.misc = PERF_RECORD_MISC_KERNEL; filename = "/proc/kallsyms"; } else { - ev.header.misc = PERF_RECORD_MISC_GUEST_KERNEL; + event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL; if (machine__is_default_guest(machine)) filename = (char *) symbol_conf.default_guest_kallsyms; else { @@ -330,17 +385,21 @@ int event__synthesize_kernel_mmap(event__handler_t process, return -ENOENT; map = machine->vmlinux_maps[MAP__FUNCTION]; - size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), + size = snprintf(event->mmap.filename, sizeof(event->mmap.filename), "%s%s", mmap_name, symbol_name) + 1; size = ALIGN(size, sizeof(u64)); - ev.mmap.header.size = (sizeof(ev.mmap) - - (sizeof(ev.mmap.filename) - size)); - ev.mmap.pgoff = args.start; - ev.mmap.start = map->start; - ev.mmap.len = map->end - ev.mmap.start; - ev.mmap.pid = machine->pid; - - return process(&ev, &synth_sample, session); + event->mmap.header.type = PERF_RECORD_MMAP; + event->mmap.header.size = (sizeof(event->mmap) - + (sizeof(event->mmap.filename) - size) + session->id_hdr_size); + event->mmap.pgoff = args.start; + event->mmap.start = map->start; + event->mmap.len = map->end - event->mmap.start; + event->mmap.pid = machine->pid; + + err = process(event, &synth_sample, session); + free(event); + + return err; } static void thread__comm_adjust(struct thread *self, struct hists *hists) @@ -756,9 +815,65 @@ out_filtered: return 0; } -int event__parse_sample(const event_t *event, u64 type, struct sample_data *data) +static int event__parse_id_sample(const event_t *event, + struct perf_session *session, + struct sample_data *sample) { - const u64 *array = event->sample.array; + const u64 *array; + u64 type; + + sample->cpu = sample->pid = sample->tid = -1; + sample->stream_id = sample->id = sample->time = -1ULL; + + if (!session->sample_id_all) + return 0; + + array = event->sample.array; + array += ((event->header.size - + sizeof(event->header)) / sizeof(u64)) - 1; + type = session->sample_type; + + if (type & PERF_SAMPLE_CPU) { + u32 *p = (u32 *)array; + sample->cpu = *p; + array--; + } + + if (type & PERF_SAMPLE_STREAM_ID) { + sample->stream_id = *array; + array--; + } + + if (type & PERF_SAMPLE_ID) { + sample->id = *array; + array--; + } + + if (type & PERF_SAMPLE_TIME) { + sample->time = *array; + array--; + } + + if (type & PERF_SAMPLE_TID) { + u32 *p = (u32 *)array; + sample->pid = p[0]; + sample->tid = p[1]; + } + + return 0; +} + +int event__parse_sample(const event_t *event, struct perf_session *session, + struct sample_data *data) +{ + const u64 *array; + u64 type; + + if (event->header.type != PERF_RECORD_SAMPLE) + return event__parse_id_sample(event, session, data); + + array = event->sample.array; + type = session->sample_type; if (type & PERF_SAMPLE_IP) { data->ip = event->ip.ip; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 08c400b83d57..a95ab18575ce 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -142,8 +142,8 @@ typedef int (*event__handler_t)(event_t *event, struct sample_data *sample, int event__synthesize_thread(pid_t pid, event__handler_t process, struct perf_session *session); -void event__synthesize_threads(event__handler_t process, - struct perf_session *session); +int event__synthesize_threads(event__handler_t process, + struct perf_session *session); int event__synthesize_kernel_mmap(event__handler_t process, struct perf_session *session, struct machine *machine, @@ -168,7 +168,8 @@ struct addr_location; int event__preprocess_sample(const event_t *self, struct perf_session *session, struct addr_location *al, struct sample_data *data, symbol_filter_t filter); -int event__parse_sample(const event_t *event, u64 type, struct sample_data *data); +int event__parse_sample(const event_t *event, struct perf_session *session, + struct sample_data *sample); extern const char *event__name[]; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index fe652f3b0aa7..073f0e1c7123 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -946,6 +946,24 @@ u64 perf_header__sample_type(struct perf_header *header) return type; } +bool perf_header__sample_id_all(const struct perf_header *header) +{ + bool value = false, first = true; + int i; + + for (i = 0; i < header->attrs; i++) { + struct perf_header_attr *attr = header->attr[i]; + + if (first) { + value = attr->attr.sample_id_all; + first = false; + } else if (value != attr->attr.sample_id_all) + die("non matching sample_id_all"); + } + + return value; +} + struct perf_event_attr * perf_header__find_attr(u64 id, struct perf_header *header) { diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index ed550bffd655..6335965e1f93 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -81,6 +81,7 @@ void perf_header_attr__delete(struct perf_header_attr *self); int perf_header_attr__add_id(struct perf_header_attr *self, u64 id); u64 perf_header__sample_type(struct perf_header *header); +bool perf_header__sample_id_all(const struct perf_header *header); struct perf_event_attr * perf_header__find_attr(u64 id, struct perf_header *header); void perf_header__set_feat(struct perf_header *self, int feat); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 08ec018966a8..5c756609104e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -65,9 +65,37 @@ out_close: return -1; } -void perf_session__update_sample_type(struct perf_session *self) +static void perf_session__id_header_size(struct perf_session *session) { - self->sample_type = perf_header__sample_type(&self->header); + struct sample_data *data; + u64 sample_type = session->sample_type; + u16 size = 0; + + if (!session->sample_id_all) + goto out; + + if (sample_type & PERF_SAMPLE_TID) + size += sizeof(data->tid) * 2; + + if (sample_type & PERF_SAMPLE_TIME) + size += sizeof(data->time); + + if (sample_type & PERF_SAMPLE_ID) + size += sizeof(data->id); + + if (sample_type & PERF_SAMPLE_STREAM_ID) + size += sizeof(data->stream_id); + + if (sample_type & PERF_SAMPLE_CPU) + size += sizeof(data->cpu) * 2; +out: + session->id_hdr_size = size; +} + +void perf_session__set_sample_id_all(struct perf_session *session, bool value) +{ + session->sample_id_all = value; + perf_session__id_header_size(session); } void perf_session__set_sample_type(struct perf_session *session, u64 type) @@ -75,6 +103,13 @@ void perf_session__set_sample_type(struct perf_session *session, u64 type) session->sample_type = type; } +void perf_session__update_sample_type(struct perf_session *self) +{ + self->sample_type = perf_header__sample_type(&self->header); + self->sample_id_all = perf_header__sample_id_all(&self->header); + perf_session__id_header_size(self); +} + int perf_session__create_kernel_maps(struct perf_session *self) { int ret = machine__create_kernel_maps(&self->host_machine); @@ -443,7 +478,7 @@ static void flush_sample_queue(struct perf_session *s, if (iter->timestamp > limit) break; - event__parse_sample(iter->event, s->sample_type, &sample); + event__parse_sample(iter->event, s, &sample); ops->sample(iter->event, &sample, s); os->last_flush = iter->timestamp; @@ -618,6 +653,23 @@ static void callchain__dump(struct sample_data *sample) printf("..... %2d: %016Lx\n", i, sample->callchain->ips[i]); } +static void perf_session__print_tstamp(struct perf_session *session, + event_t *event, + struct sample_data *sample) +{ + if (event->header.type != PERF_RECORD_SAMPLE && + !session->sample_id_all) { + fputs("-1 -1 ", stdout); + return; + } + + if ((session->sample_type & PERF_SAMPLE_CPU)) + printf("%u ", sample->cpu); + + if (session->sample_type & PERF_SAMPLE_TIME) + printf("%Lu ", sample->time); +} + static int perf_session__process_event(struct perf_session *self, event_t *event, struct perf_event_ops *ops, @@ -630,8 +682,12 @@ static int perf_session__process_event(struct perf_session *self, if (self->header.needs_swap && event__swap_ops[event->header.type]) event__swap_ops[event->header.type](event); - if (event->header.type == PERF_RECORD_SAMPLE) - event__parse_sample(event, self->sample_type, &sample); + if (event->header.type >= PERF_RECORD_MMAP && + event->header.type <= PERF_RECORD_SAMPLE) { + event__parse_sample(event, self, &sample); + if (dump_trace) + perf_session__print_tstamp(self, event, &sample); + } if (event->header.type < PERF_RECORD_HEADER_MAX) { dump_printf("%#Lx [%#x]: PERF_RECORD_%s", diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 4578f86a6209..ac36f99f14af 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -46,6 +46,8 @@ struct perf_session { int fd; bool fd_pipe; bool repipe; + bool sample_id_all; + u16 id_hdr_size; int cwdlen; char *cwd; struct ordered_samples ordered_samples; @@ -106,6 +108,7 @@ int perf_session__create_kernel_maps(struct perf_session *self); int do_read(int fd, void *buf, size_t size); void perf_session__update_sample_type(struct perf_session *self); +void perf_session__set_sample_id_all(struct perf_session *session, bool value); void perf_session__set_sample_type(struct perf_session *session, u64 type); void perf_session__remove_thread(struct perf_session *self, struct thread *th); -- cgit v1.2.3 From cbf41645f35224798cb61641766e6a16e141ffe4 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 5 Dec 2010 14:32:55 +0100 Subject: perf session: Sort all events if ordered_samples=true Now that we have timestamps on FORK, EXIT, COMM, MMAP events we can sort everything in time order. This fixes the following observed problem: mmap(file1) -> pagefault() -> munmap(file1) mmap(file2) -> pagefault() -> munmap(file2) Resulted in decoding both pagefaults in file2 because the file1 map was already replaced by the file2 map when the map address was identical. With all events sorted we decode both pagefaults correctly. Cc: Frederic Weisbecker Cc: Ian Munsie Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian LKML-Reference: Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 125 ++++++++++++++++++++++++++-------------------- 1 file changed, 72 insertions(+), 53 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 5c756609104e..3074d38897e6 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -461,6 +461,11 @@ static void perf_session_free_sample_buffers(struct perf_session *session) } } +static int perf_session_deliver_event(struct perf_session *session, + event_t *event, + struct sample_data *sample, + struct perf_event_ops *ops); + static void flush_sample_queue(struct perf_session *s, struct perf_event_ops *ops) { @@ -479,7 +484,7 @@ static void flush_sample_queue(struct perf_session *s, break; event__parse_sample(iter->event, s, &sample); - ops->sample(iter->event, &sample, s); + perf_session_deliver_event(s, iter->event, &sample, ops); os->last_flush = iter->timestamp; list_del(&iter->list); @@ -544,8 +549,7 @@ static int process_finished_round(event_t *event __used, } /* The queue is ordered by time */ -static void __queue_sample_event(struct sample_queue *new, - struct perf_session *s) +static void __queue_event(struct sample_queue *new, struct perf_session *s) { struct ordered_samples *os = &s->ordered_samples; struct sample_queue *sample = os->last_sample; @@ -591,14 +595,17 @@ static void __queue_sample_event(struct sample_queue *new, #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) -static int queue_sample_event(event_t *event, struct sample_data *data, - struct perf_session *s) +static int perf_session_queue_event(struct perf_session *s, event_t *event, + struct sample_data *data) { struct ordered_samples *os = &s->ordered_samples; struct list_head *sc = &os->sample_cache; u64 timestamp = data->time; struct sample_queue *new; + if (!timestamp) + return -ETIME; + if (timestamp < s->ordered_samples.last_flush) { printf("Warning: Timestamp below last timeslice flush\n"); return -EINVAL; @@ -623,20 +630,8 @@ static int queue_sample_event(event_t *event, struct sample_data *data, new->timestamp = timestamp; new->event = event; - __queue_sample_event(new, s); - - return 0; -} - -static int perf_session__process_sample(event_t *event, - struct sample_data *sample, - struct perf_session *s, - struct perf_event_ops *ops) -{ - if (!ops->ordered_samples) - return ops->sample(event, sample, s); + __queue_event(new, s); - queue_sample_event(event, sample, s); return 0; } @@ -670,83 +665,107 @@ static void perf_session__print_tstamp(struct perf_session *session, printf("%Lu ", sample->time); } -static int perf_session__process_event(struct perf_session *self, +static int perf_session_deliver_event(struct perf_session *session, + event_t *event, + struct sample_data *sample, + struct perf_event_ops *ops) +{ + switch (event->header.type) { + case PERF_RECORD_SAMPLE: + return ops->sample(event, sample, session); + case PERF_RECORD_MMAP: + return ops->mmap(event, sample, session); + case PERF_RECORD_COMM: + return ops->comm(event, sample, session); + case PERF_RECORD_FORK: + return ops->fork(event, sample, session); + case PERF_RECORD_EXIT: + return ops->exit(event, sample, session); + case PERF_RECORD_LOST: + return ops->lost(event, sample, session); + case PERF_RECORD_READ: + return ops->read(event, sample, session); + case PERF_RECORD_THROTTLE: + return ops->throttle(event, sample, session); + case PERF_RECORD_UNTHROTTLE: + return ops->unthrottle(event, sample, session); + default: + ++session->hists.stats.nr_unknown_events; + return -1; + } +} + +static int perf_session__process_event(struct perf_session *session, event_t *event, struct perf_event_ops *ops, u64 file_offset) { struct sample_data sample; + int ret; trace_event(event); - if (self->header.needs_swap && event__swap_ops[event->header.type]) + if (session->header.needs_swap && event__swap_ops[event->header.type]) event__swap_ops[event->header.type](event); if (event->header.type >= PERF_RECORD_MMAP && event->header.type <= PERF_RECORD_SAMPLE) { - event__parse_sample(event, self, &sample); + event__parse_sample(event, session, &sample); if (dump_trace) - perf_session__print_tstamp(self, event, &sample); + perf_session__print_tstamp(session, event, &sample); } if (event->header.type < PERF_RECORD_HEADER_MAX) { dump_printf("%#Lx [%#x]: PERF_RECORD_%s", file_offset, event->header.size, event__name[event->header.type]); - hists__inc_nr_events(&self->hists, event->header.type); + hists__inc_nr_events(&session->hists, event->header.type); } + /* These events are processed right away */ switch (event->header.type) { case PERF_RECORD_SAMPLE: - dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, + dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", + event->header.misc, sample.pid, sample.tid, sample.ip, sample.period); - if (self->sample_type & PERF_SAMPLE_CALLCHAIN) { + if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { if (!ip_callchain__valid(sample.callchain, event)) { pr_debug("call-chain problem with event, " "skipping it.\n"); - ++self->hists.stats.nr_invalid_chains; - self->hists.stats.total_invalid_chains += sample.period; + ++session->hists.stats.nr_invalid_chains; + session->hists.stats.total_invalid_chains += + sample.period; return 0; } callchain__dump(&sample); } + break; - return perf_session__process_sample(event, &sample, self, ops); - - case PERF_RECORD_MMAP: - return ops->mmap(event, &sample, self); - case PERF_RECORD_COMM: - return ops->comm(event, &sample, self); - case PERF_RECORD_FORK: - return ops->fork(event, &sample, self); - case PERF_RECORD_EXIT: - return ops->exit(event, &sample, self); - case PERF_RECORD_LOST: - return ops->lost(event, &sample, self); - case PERF_RECORD_READ: - return ops->read(event, &sample, self); - case PERF_RECORD_THROTTLE: - return ops->throttle(event, &sample, self); - case PERF_RECORD_UNTHROTTLE: - return ops->unthrottle(event, &sample, self); case PERF_RECORD_HEADER_ATTR: - return ops->attr(event, self); + return ops->attr(event, session); case PERF_RECORD_HEADER_EVENT_TYPE: - return ops->event_type(event, self); + return ops->event_type(event, session); case PERF_RECORD_HEADER_TRACING_DATA: /* setup for reading amidst mmap */ - lseek(self->fd, file_offset, SEEK_SET); - return ops->tracing_data(event, self); + lseek(session->fd, file_offset, SEEK_SET); + return ops->tracing_data(event, session); case PERF_RECORD_HEADER_BUILD_ID: - return ops->build_id(event, self); + return ops->build_id(event, session); case PERF_RECORD_FINISHED_ROUND: - return ops->finished_round(event, self, ops); + return ops->finished_round(event, session, ops); default: - ++self->hists.stats.nr_unknown_events; - return -1; + break; } + + if (ops->ordered_samples) { + ret = perf_session_queue_event(session, event, &sample); + if (ret != -ETIME) + return ret; + } + + return perf_session_deliver_event(session, event, &sample, ops); } void perf_event_header__bswap(struct perf_event_header *self) -- cgit v1.2.3 From 3835bc00c5b2d8e337a6e9d7b44f47e02760dba3 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 7 Dec 2010 12:48:42 +0000 Subject: perf event: Prevent unbound event__name array access event__name[] is missing an entry for PERF_RECORD_FINISHED_ROUND, but we happily access the array from the dump code. Make event__name[] static and provide an accessor function, fix up all callers and add the missing string. Cc: Frederic Weisbecker Cc: Ian Munsie Cc: Ingo Molnar Cc: Peter Zijlstra LKML-Reference: <20101207124550.432593943@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 12 +++++++++++- tools/perf/util/event.h | 2 +- tools/perf/util/hist.c | 9 ++++++--- tools/perf/util/session.c | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index e4cdc1ebe0fb..183aedd4db83 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -7,7 +7,7 @@ #include "strlist.h" #include "thread.h" -const char *event__name[] = { +static const char *event__name[] = { [0] = "TOTAL", [PERF_RECORD_MMAP] = "MMAP", [PERF_RECORD_LOST] = "LOST", @@ -22,8 +22,18 @@ const char *event__name[] = { [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", + [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", }; +const char *event__get_event_name(unsigned int id) +{ + if (id >= ARRAY_SIZE(event__name)) + return "INVALID"; + if (!event__name[id]) + return "UNKNOWN"; + return event__name[id]; +} + static struct sample_data synth_sample = { .pid = -1, .tid = -1, diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index a95ab18575ce..4716a8f6d3eb 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -171,6 +171,6 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, int event__parse_sample(const event_t *event, struct perf_session *session, struct sample_data *sample); -extern const char *event__name[]; +const char *event__get_event_name(unsigned int id); #endif /* __PERF_RECORD_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 2022e8740994..a3b84160c42e 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1168,10 +1168,13 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) size_t ret = 0; for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { - if (!event__name[i]) + const char *name = event__get_event_name(i); + + if (!strcmp(name, "UNKNOWN")) continue; - ret += fprintf(fp, "%10s events: %10d\n", - event__name[i], self->stats.nr_events[i]); + + ret += fprintf(fp, "%16s events: %10d\n", name, + self->stats.nr_events[i]); } return ret; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 3074d38897e6..b3b145a8ff7c 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -718,7 +718,7 @@ static int perf_session__process_event(struct perf_session *session, if (event->header.type < PERF_RECORD_HEADER_MAX) { dump_printf("%#Lx [%#x]: PERF_RECORD_%s", file_offset, event->header.size, - event__name[event->header.type]); + event__get_event_name(event->header.type)); hists__inc_nr_events(&session->hists, event->header.type); } -- cgit v1.2.3 From 79a14c1f458d598642bf11f09512c83d33a114e6 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 7 Dec 2010 12:48:44 +0000 Subject: perf session: Dont queue events w/o timestamps If the event has no timestamp assigned then the parse code sets it to ~0ULL which causes the ordering code to enqueue it at the end. Process it right away. Reported-by: Ian Munsie Cc: Frederic Weisbecker Cc: Ian Munsie Cc: Ingo Molnar Cc: Peter Zijlstra LKML-Reference: <20101207124550.528788441@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index b3b145a8ff7c..e5d0ed9603ee 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -603,7 +603,7 @@ static int perf_session_queue_event(struct perf_session *s, event_t *event, u64 timestamp = data->time; struct sample_queue *new; - if (!timestamp) + if (!timestamp || timestamp == ~0ULL) return -ETIME; if (timestamp < s->ordered_samples.last_flush) { -- cgit v1.2.3 From 9aefcab0de472ee2b3ab195a6827ddd4b170e3a7 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 7 Dec 2010 12:48:47 +0000 Subject: perf session: Consolidate the dump code The dump code used by perf report -D is scattered all over the place. Move it to separate functions. Acked-by: Ian Munsie Cc: Frederic Weisbecker Cc: Ian Munsie Cc: Ingo Molnar Cc: Peter Zijlstra LKML-Reference: <20101207124550.625434869@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.h | 1 + tools/perf/util/session.c | 64 +++++++++++++++++++++++++++++------------------ 2 files changed, 40 insertions(+), 25 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 4716a8f6d3eb..2b7e91902f10 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -85,6 +85,7 @@ struct build_id_event { }; enum perf_user_event_type { /* above any possible kernel type */ + PERF_RECORD_USER_TYPE_START = 64, PERF_RECORD_HEADER_ATTR = 64, PERF_RECORD_HEADER_EVENT_TYPE = 65, PERF_RECORD_HEADER_TRACING_DATA = 66, diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index e5d0ed9603ee..12761d5bc92a 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -665,6 +665,35 @@ static void perf_session__print_tstamp(struct perf_session *session, printf("%Lu ", sample->time); } +static void dump_event(struct perf_session *session, event_t *event, + u64 file_offset, struct sample_data *sample) +{ + if (!dump_trace) + return; + + dump_printf("\n%#Lx [%#x]: event: %d\n", file_offset, + event->header.size, event->header.type); + + trace_event(event); + + if (sample) + perf_session__print_tstamp(session, event, sample); + + dump_printf("%#Lx [%#x]: PERF_RECORD_%s", + file_offset, event->header.size, + event__get_event_name(event->header.type)); +} + +static void dump_sample(struct perf_session *session, event_t *event, + struct sample_data *sample) +{ + dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, + sample->pid, sample->tid, sample->ip, sample->period); + + if (session->sample_type & PERF_SAMPLE_CALLCHAIN) + callchain__dump(sample); +} + static int perf_session_deliver_event(struct perf_session *session, event_t *event, struct sample_data *sample, @@ -703,32 +732,24 @@ static int perf_session__process_event(struct perf_session *session, struct sample_data sample; int ret; - trace_event(event); - if (session->header.needs_swap && event__swap_ops[event->header.type]) event__swap_ops[event->header.type](event); - if (event->header.type >= PERF_RECORD_MMAP && - event->header.type <= PERF_RECORD_SAMPLE) { - event__parse_sample(event, session, &sample); - if (dump_trace) - perf_session__print_tstamp(session, event, &sample); - } + if (event->header.type >= PERF_RECORD_HEADER_MAX) + return -EINVAL; - if (event->header.type < PERF_RECORD_HEADER_MAX) { - dump_printf("%#Lx [%#x]: PERF_RECORD_%s", - file_offset, event->header.size, - event__get_event_name(event->header.type)); - hists__inc_nr_events(&session->hists, event->header.type); + hists__inc_nr_events(&session->hists, event->header.type); + + if (event->header.type >= PERF_RECORD_USER_TYPE_START) + dump_event(session, event, file_offset, NULL); + else { + event__parse_sample(event, session, &sample); + dump_event(session, event, file_offset, &sample); } /* These events are processed right away */ switch (event->header.type) { case PERF_RECORD_SAMPLE: - dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", - event->header.misc, - sample.pid, sample.tid, sample.ip, sample.period); - if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { if (!ip_callchain__valid(sample.callchain, event)) { pr_debug("call-chain problem with event, " @@ -738,9 +759,8 @@ static int perf_session__process_event(struct perf_session *session, sample.period; return 0; } - - callchain__dump(&sample); } + dump_sample(session, event, &sample); break; case PERF_RECORD_HEADER_ATTR: @@ -870,9 +890,6 @@ more: head += size; - dump_printf("\n%#Lx [%#x]: event: %d\n", - head, event.header.size, event.header.type); - if (skip > 0) head += skip; @@ -961,9 +978,6 @@ more: size = event->header.size; - dump_printf("\n%#Lx [%#x]: event: %d\n", - file_pos, event->header.size, event->header.type); - if (size == 0 || perf_session__process_event(session, event, ops, file_pos) < 0) { dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", -- cgit v1.2.3 From e4c2df132fef60a28b851abc1859a531e64f350c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 7 Dec 2010 12:48:50 +0000 Subject: perf session: Store file offset in sample_queue Preparatory patch for ordered output of perf report -D. Acked-by: Ian Munsie Cc: Frederic Weisbecker Cc: Ian Munsie Cc: Ingo Molnar Cc: Peter Zijlstra LKML-Reference: <20101207124550.725128545@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 12761d5bc92a..a4339548de67 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -444,6 +444,7 @@ static event__swap_op event__swap_ops[] = { struct sample_queue { u64 timestamp; + u64 file_offset; event_t *event; struct list_head list; }; @@ -596,7 +597,7 @@ static void __queue_event(struct sample_queue *new, struct perf_session *s) #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) static int perf_session_queue_event(struct perf_session *s, event_t *event, - struct sample_data *data) + struct sample_data *data, u64 file_offset) { struct ordered_samples *os = &s->ordered_samples; struct list_head *sc = &os->sample_cache; @@ -628,6 +629,7 @@ static int perf_session_queue_event(struct perf_session *s, event_t *event, } new->timestamp = timestamp; + new->file_offset = file_offset; new->event = event; __queue_event(new, s); @@ -780,7 +782,8 @@ static int perf_session__process_event(struct perf_session *session, } if (ops->ordered_samples) { - ret = perf_session_queue_event(session, event, &sample); + ret = perf_session_queue_event(session, event, &sample, + file_offset); if (ret != -ETIME) return ret; } -- cgit v1.2.3 From f74725dcf2f6931c26bc65e77e34e693eeb8441c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 7 Dec 2010 12:48:53 +0000 Subject: perf session: Add file_offset to event delivery function Preparatory patch for ordered output of perf report -D Acked-by: Ian Munsie Cc: Frederic Weisbecker Cc: Ian Munsie Cc: Ingo Molnar Cc: Peter Zijlstra LKML-Reference: <20101207124550.818568607@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index a4339548de67..d43e56cd6f28 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -465,7 +465,8 @@ static void perf_session_free_sample_buffers(struct perf_session *session) static int perf_session_deliver_event(struct perf_session *session, event_t *event, struct sample_data *sample, - struct perf_event_ops *ops); + struct perf_event_ops *ops, + u64 file_offset); static void flush_sample_queue(struct perf_session *s, struct perf_event_ops *ops) @@ -485,7 +486,8 @@ static void flush_sample_queue(struct perf_session *s, break; event__parse_sample(iter->event, s, &sample); - perf_session_deliver_event(s, iter->event, &sample, ops); + perf_session_deliver_event(s, iter->event, &sample, ops, + iter->file_offset); os->last_flush = iter->timestamp; list_del(&iter->list); @@ -699,7 +701,8 @@ static void dump_sample(struct perf_session *session, event_t *event, static int perf_session_deliver_event(struct perf_session *session, event_t *event, struct sample_data *sample, - struct perf_event_ops *ops) + struct perf_event_ops *ops, + u64 file_offset __used) { switch (event->header.type) { case PERF_RECORD_SAMPLE: @@ -788,7 +791,8 @@ static int perf_session__process_event(struct perf_session *session, return ret; } - return perf_session_deliver_event(session, event, &sample, ops); + return perf_session_deliver_event(session, event, &sample, ops, + file_offset); } void perf_event_header__bswap(struct perf_event_header *self) -- cgit v1.2.3 From 532e7269c01098f0be6e08113c6947ec6ed11bfa Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 7 Dec 2010 12:48:55 +0000 Subject: perf session: Move dump code to event delivery path Preparatory patch for ordered perf report -D Acked-by: Ian Munsie Cc: Frederic Weisbecker Cc: Ian Munsie Cc: Ingo Molnar Cc: Peter Zijlstra LKML-Reference: <20101207124550.918655066@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index d43e56cd6f28..7c5cc129207a 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -702,10 +702,13 @@ static int perf_session_deliver_event(struct perf_session *session, event_t *event, struct sample_data *sample, struct perf_event_ops *ops, - u64 file_offset __used) + u64 file_offset) { + dump_event(session, event, file_offset, sample); + switch (event->header.type) { case PERF_RECORD_SAMPLE: + dump_sample(session, event, sample); return ops->sample(event, sample, session); case PERF_RECORD_MMAP: return ops->mmap(event, sample, session); @@ -747,10 +750,8 @@ static int perf_session__process_event(struct perf_session *session, if (event->header.type >= PERF_RECORD_USER_TYPE_START) dump_event(session, event, file_offset, NULL); - else { + else event__parse_sample(event, session, &sample); - dump_event(session, event, file_offset, &sample); - } /* These events are processed right away */ switch (event->header.type) { @@ -765,7 +766,6 @@ static int perf_session__process_event(struct perf_session *session, return 0; } } - dump_sample(session, event, &sample); break; case PERF_RECORD_HEADER_ATTR: -- cgit v1.2.3 From 3dfc2c0aee789843d18f6e4675658e6879465a56 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 7 Dec 2010 12:48:58 +0000 Subject: perf session: Split out sample preprocessing Simplify the code a bit. Cc: Frederic Weisbecker Cc: Ian Munsie Cc: Ingo Molnar Cc: Peter Zijlstra LKML-Reference: <20101207124551.014649793@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 7c5cc129207a..a765b274390e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -732,6 +732,22 @@ static int perf_session_deliver_event(struct perf_session *session, } } +static int perf_session__preprocess_sample(struct perf_session *session, + event_t *event, struct sample_data *sample) +{ + if (event->header.type != PERF_RECORD_SAMPLE || + !(session->sample_type & PERF_SAMPLE_CALLCHAIN)) + return 0; + + if (!ip_callchain__valid(sample->callchain, event)) { + pr_debug("call-chain problem with event, skipping it.\n"); + ++session->hists.stats.nr_invalid_chains; + session->hists.stats.total_invalid_chains += sample->period; + return -EINVAL; + } + return 0; +} + static int perf_session__process_event(struct perf_session *session, event_t *event, struct perf_event_ops *ops, @@ -750,24 +766,9 @@ static int perf_session__process_event(struct perf_session *session, if (event->header.type >= PERF_RECORD_USER_TYPE_START) dump_event(session, event, file_offset, NULL); - else - event__parse_sample(event, session, &sample); /* These events are processed right away */ switch (event->header.type) { - case PERF_RECORD_SAMPLE: - if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { - if (!ip_callchain__valid(sample.callchain, event)) { - pr_debug("call-chain problem with event, " - "skipping it.\n"); - ++session->hists.stats.nr_invalid_chains; - session->hists.stats.total_invalid_chains += - sample.period; - return 0; - } - } - break; - case PERF_RECORD_HEADER_ATTR: return ops->attr(event, session); case PERF_RECORD_HEADER_EVENT_TYPE: @@ -784,6 +785,15 @@ static int perf_session__process_event(struct perf_session *session, break; } + /* + * For all kernel events we get the sample data + */ + event__parse_sample(event, session, &sample); + + /* Preprocess sample records - precheck callchains */ + if (perf_session__preprocess_sample(session, event, &sample)) + return 0; + if (ops->ordered_samples) { ret = perf_session_queue_event(session, event, &sample, file_offset); -- cgit v1.2.3 From ba74f0640d963ccc914ac533cb0ba133ee07bcf2 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 7 Dec 2010 12:49:01 +0000 Subject: perf session: Split out user event processing Simplify further. Cc: Frederic Weisbecker Cc: Ian Munsie Cc: Ingo Molnar Cc: Peter Zijlstra LKML-Reference: <20101207124551.110956235@linutronix.de> Signed-off-by: Thomas Gleixner Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index a765b274390e..69760cd4cc44 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -748,24 +748,10 @@ static int perf_session__preprocess_sample(struct perf_session *session, return 0; } -static int perf_session__process_event(struct perf_session *session, - event_t *event, - struct perf_event_ops *ops, - u64 file_offset) +static int perf_session__process_user_event(struct perf_session *session, event_t *event, + struct perf_event_ops *ops, u64 file_offset) { - struct sample_data sample; - int ret; - - if (session->header.needs_swap && event__swap_ops[event->header.type]) - event__swap_ops[event->header.type](event); - - if (event->header.type >= PERF_RECORD_HEADER_MAX) - return -EINVAL; - - hists__inc_nr_events(&session->hists, event->header.type); - - if (event->header.type >= PERF_RECORD_USER_TYPE_START) - dump_event(session, event, file_offset, NULL); + dump_event(session, event, file_offset, NULL); /* These events are processed right away */ switch (event->header.type) { @@ -782,8 +768,28 @@ static int perf_session__process_event(struct perf_session *session, case PERF_RECORD_FINISHED_ROUND: return ops->finished_round(event, session, ops); default: - break; + return -EINVAL; } +} + +static int perf_session__process_event(struct perf_session *session, + event_t *event, + struct perf_event_ops *ops, + u64 file_offset) +{ + struct sample_data sample; + int ret; + + if (session->header.needs_swap && event__swap_ops[event->header.type]) + event__swap_ops[event->header.type](event); + + if (event->header.type >= PERF_RECORD_HEADER_MAX) + return -EINVAL; + + hists__inc_nr_events(&session->hists, event->header.type); + + if (event->header.type >= PERF_RECORD_USER_TYPE_START) + return perf_session__process_user_event(session, event, ops, file_offset); /* * For all kernel events we get the sample data -- cgit v1.2.3 From ddbc24b72c2c3f3f0182bbc2cb70b31c52a6f45b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 9 Dec 2010 12:20:20 -0200 Subject: perf session: Remove unneeded dump_printf calls Since we check at the beginning of the callers, no need to ask if dump_trace is set multiple times. Cc: Frederic Weisbecker Cc: Ian Munsie Cc: Ingo Molnar Cc: Thomas Gleixner LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 69760cd4cc44..b59abf5aba36 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -639,13 +639,10 @@ static int perf_session_queue_event(struct perf_session *s, event_t *event, return 0; } -static void callchain__dump(struct sample_data *sample) +static void callchain__printf(struct sample_data *sample) { unsigned int i; - if (!dump_trace) - return; - printf("... chain: nr:%Lu\n", sample->callchain->nr); for (i = 0; i < sample->callchain->nr; i++) @@ -675,27 +672,29 @@ static void dump_event(struct perf_session *session, event_t *event, if (!dump_trace) return; - dump_printf("\n%#Lx [%#x]: event: %d\n", file_offset, - event->header.size, event->header.type); + printf("\n%#Lx [%#x]: event: %d\n", file_offset, event->header.size, + event->header.type); trace_event(event); if (sample) perf_session__print_tstamp(session, event, sample); - dump_printf("%#Lx [%#x]: PERF_RECORD_%s", - file_offset, event->header.size, - event__get_event_name(event->header.type)); + printf("%#Lx [%#x]: PERF_RECORD_%s", file_offset, event->header.size, + event__get_event_name(event->header.type)); } static void dump_sample(struct perf_session *session, event_t *event, struct sample_data *sample) { - dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, - sample->pid, sample->tid, sample->ip, sample->period); + if (!dump_trace) + return; + + printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, + sample->pid, sample->tid, sample->ip, sample->period); if (session->sample_type & PERF_SAMPLE_CALLCHAIN) - callchain__dump(sample); + callchain__printf(sample); } static int perf_session_deliver_event(struct perf_session *session, -- cgit v1.2.3 From 21ef97f05a7da5bc23b26cb34d6746f83ca9bf20 Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Fri, 10 Dec 2010 14:09:16 +1100 Subject: perf session: Fallback to unordered processing if no sample_id_all If we are running the new perf on an old kernel without support for sample_id_all, we should fall back to the old unordered processing of events. If we didn't than we would *always* process events without timestamps out of order, whether or not we hit a reordering race. In other words, instead of there being a chance of not attributing samples correctly, we would guarantee that samples would not be attributed. While processing all events without timestamps before events with timestamps may seem like an intuitive solution, it falls down as PERF_RECORD_EXIT events would also be processed before any samples. Even with a workaround for that case, samples before/after an exec would not be attributed correctly. This patch allows commands to indicate whether they need to fall back to unordered processing, so that commands that do not care about timestamps on every event will not be affected. If we do fallback, this will print out a warning if report -D was invoked. This patch adds the test in perf_session__new so that we only need to test once per session. Commands that do not use an event_ops (such as record and top) can simply pass NULL in it's place. Acked-by: Thomas Gleixner Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner LKML-Reference: <1291951882-sup-6069@au1.ibm.com> Signed-off-by: Ian Munsie Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 2 +- tools/perf/builtin-buildid-list.c | 3 ++- tools/perf/builtin-diff.c | 4 ++-- tools/perf/builtin-inject.c | 2 +- tools/perf/builtin-kmem.c | 3 ++- tools/perf/builtin-lock.c | 2 +- tools/perf/builtin-record.c | 2 +- tools/perf/builtin-report.c | 2 +- tools/perf/builtin-sched.c | 3 ++- tools/perf/builtin-script.c | 2 +- tools/perf/builtin-timechart.c | 3 ++- tools/perf/builtin-top.c | 2 +- tools/perf/util/session.c | 11 ++++++++++- tools/perf/util/session.h | 5 ++++- 14 files changed, 31 insertions(+), 15 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 569a2761b90a..48dbab4b482f 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -382,7 +382,7 @@ static int __cmd_annotate(void) int ret; struct perf_session *session; - session = perf_session__new(input_name, O_RDONLY, force, false); + session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 44a47e13bd67..3b06f9ca2638 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -39,7 +39,8 @@ static int __cmd_buildid_list(void) int err = -1; struct perf_session *session; - session = perf_session__new(input_name, O_RDONLY, force, false); + session = perf_session__new(input_name, O_RDONLY, force, false, + &build_id__mark_dso_hit_ops); if (session == NULL) return -1; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 5e1a043aae03..af84e1c0519d 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -142,8 +142,8 @@ static int __cmd_diff(void) int ret, i; struct perf_session *session[2]; - session[0] = perf_session__new(input_old, O_RDONLY, force, false); - session[1] = perf_session__new(input_new, O_RDONLY, force, false); + session[0] = perf_session__new(input_old, O_RDONLY, force, false, &event_ops); + session[1] = perf_session__new(input_new, O_RDONLY, force, false, &event_ops); if (session[0] == NULL || session[1] == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 4b66b8579410..0c78ffa7bf67 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -196,7 +196,7 @@ static int __cmd_inject(void) inject_ops.tracing_data = event__repipe_tracing_data; } - session = perf_session__new(input_name, O_RDONLY, false, true); + session = perf_session__new(input_name, O_RDONLY, false, true, &inject_ops); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index c9620ff6496f..def7ddc2fd4f 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -481,7 +481,8 @@ static void sort_result(void) static int __cmd_kmem(void) { int err = -EINVAL; - struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false); + struct perf_session *session = perf_session__new(input_name, O_RDONLY, + 0, false, &event_ops); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index b41b4492b1cc..b9c6e5432971 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -858,7 +858,7 @@ static struct perf_event_ops eops = { static int read_events(void) { - session = perf_session__new(input_name, O_RDONLY, 0, false); + session = perf_session__new(input_name, O_RDONLY, 0, false, &eops); if (!session) die("Initializing perf session failed\n"); diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index e9be6ae87a27..efd1b3c3d4a0 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -572,7 +572,7 @@ static int __cmd_record(int argc, const char **argv) } session = perf_session__new(output_name, O_WRONLY, - write_mode == WRITE_FORCE, false); + write_mode == WRITE_FORCE, false, NULL); if (session == NULL) { pr_err("Not enough memory for reading perf file header\n"); return -1; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index b6a2a899aa8f..fd4c4500cd15 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -308,7 +308,7 @@ static int __cmd_report(void) signal(SIGINT, sig_handler); - session = perf_session__new(input_name, O_RDONLY, force, false); + session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index c7753940aea0..7a4ebeb8b016 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1643,7 +1643,8 @@ static struct perf_event_ops event_ops = { static int read_events(void) { int err = -EINVAL; - struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false); + struct perf_session *session = perf_session__new(input_name, O_RDONLY, + 0, false, &event_ops); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 54f1ea808db5..6ef65c04ab9a 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -779,7 +779,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __used) if (!script_name) setup_pager(); - session = perf_session__new(input_name, O_RDONLY, 0, false); + session = perf_session__new(input_name, O_RDONLY, 0, false, &event_ops); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index d2fc46103f83..459b5e3db267 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -937,7 +937,8 @@ static struct perf_event_ops event_ops = { static int __cmd_timechart(void) { - struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false); + struct perf_session *session = perf_session__new(input_name, O_RDONLY, + 0, false, &event_ops); int ret = -EINVAL; if (session == NULL) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 0515ce9d3d3e..ae15f046c405 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1272,7 +1272,7 @@ static int __cmd_top(void) * FIXME: perf_session__new should allow passing a O_MMAP, so that all this * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. */ - struct perf_session *session = perf_session__new(NULL, O_WRONLY, false, false); + struct perf_session *session = perf_session__new(NULL, O_WRONLY, false, false, NULL); if (session == NULL) return -ENOMEM; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index b59abf5aba36..0f7e544544f5 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -125,7 +125,9 @@ static void perf_session__destroy_kernel_maps(struct perf_session *self) machines__destroy_guest_kernel_maps(&self->machines); } -struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe) +struct perf_session *perf_session__new(const char *filename, int mode, + bool force, bool repipe, + struct perf_event_ops *ops) { size_t len = filename ? strlen(filename) + 1 : 0; struct perf_session *self = zalloc(sizeof(*self) + len); @@ -170,6 +172,13 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc } perf_session__update_sample_type(self); + + if (ops && ops->ordering_requires_timestamps && + ops->ordered_samples && !self->sample_id_all) { + dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); + ops->ordered_samples = false; + } + out: return self; out_free: diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index ac36f99f14af..ffe4b98db8f0 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -78,9 +78,12 @@ struct perf_event_ops { build_id; event_op2 finished_round; bool ordered_samples; + bool ordering_requires_timestamps; }; -struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe); +struct perf_session *perf_session__new(const char *filename, int mode, + bool force, bool repipe, + struct perf_event_ops *ops); void perf_session__delete(struct perf_session *self); void perf_event_header__bswap(struct perf_event_header *self); -- cgit v1.2.3 From 1e7972cc5c16e06f258b0278d8c9adfb5aa75c68 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 3 Jan 2011 16:50:55 -0200 Subject: perf util: Move do_read from session to util Not really something to be exported from session.c. Rename it to 'readn' as others did in the past. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 6 +++--- tools/perf/util/session.c | 22 ++-------------------- tools/perf/util/session.h | 1 - tools/perf/util/util.c | 17 +++++++++++++++++ tools/perf/util/util.h | 1 + 5 files changed, 23 insertions(+), 24 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index ecb5a8444f42..05dec98fc3ff 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -604,7 +604,7 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) static int perf_header__getbuffer64(struct perf_header *self, int fd, void *buf, size_t size) { - if (do_read(fd, buf, size) <= 0) + if (readn(fd, buf, size) <= 0) return -1; if (self->needs_swap) @@ -660,7 +660,7 @@ int perf_file_header__read(struct perf_file_header *self, { lseek(fd, 0, SEEK_SET); - if (do_read(fd, self, sizeof(*self)) <= 0 || + if (readn(fd, self, sizeof(*self)) <= 0 || memcmp(&self->magic, __perf_magic, sizeof(self->magic))) return -1; @@ -821,7 +821,7 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *self, struct perf_header *ph, int fd, bool repipe) { - if (do_read(fd, self, sizeof(*self)) <= 0 || + if (readn(fd, self, sizeof(*self)) <= 0 || memcmp(&self->magic, __perf_magic, sizeof(self->magic))) return -1; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 0f7e544544f5..b163dfd6cbc5 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -838,23 +838,6 @@ 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; @@ -872,7 +855,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self, head = 0; more: - err = do_read(self->fd, &event, sizeof(struct perf_event_header)); + err = readn(self->fd, &event, sizeof(struct perf_event_header)); if (err <= 0) { if (err == 0) goto done; @@ -892,8 +875,7 @@ more: p += sizeof(struct perf_event_header); if (size - sizeof(struct perf_event_header)) { - err = do_read(self->fd, p, - size - sizeof(struct perf_event_header)); + err = readn(self->fd, p, size - sizeof(struct perf_event_header)); if (err <= 0) { if (err == 0) { pr_err("unexpected end of event stream\n"); diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index ffe4b98db8f0..decd83f274fd 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -109,7 +109,6 @@ void mem_bswap_64(void *src, int byte_size); int perf_session__create_kernel_maps(struct perf_session *self); -int do_read(int fd, void *buf, size_t size); void perf_session__update_sample_type(struct perf_session *self); void perf_session__set_sample_id_all(struct perf_session *session, bool value); void perf_session__set_sample_type(struct perf_session *session, u64 type); diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 214265674ddd..5b3ea49aa63e 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -114,3 +114,20 @@ unsigned long convert_unit(unsigned long value, char *unit) return value; } + +int readn(int fd, void *buf, size_t n) +{ + void *buf_start = buf; + + while (n) { + int ret = read(fd, buf, n); + + if (ret <= 0) + return ret; + + n -= ret; + buf += ret; + } + + return buf - buf_start; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 7562707ddd1c..e833f26f3bfc 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -265,6 +265,7 @@ void argv_free(char **argv); bool strglobmatch(const char *str, const char *pat); bool strlazymatch(const char *str, const char *pat); unsigned long convert_unit(unsigned long value, char *unit); +int readn(int fd, void *buf, size_t size); #define _STR(x) #x #define STR(x) _STR(x) -- cgit v1.2.3 From 1109599458c06256064213dc44ca5f5fa8ee3833 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 4 Jan 2011 16:25:15 -0200 Subject: perf session: Warn about errors when processing pipe events too Just like we do at __perf_session__process_events Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 57 ++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 26 deletions(-) (limited to 'tools/perf/util/session.c') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index b163dfd6cbc5..6fb4694d05fa 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -838,6 +838,35 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se return thread; } +static void perf_session__warn_about_errors(const struct perf_session *session, + const struct perf_event_ops *ops) +{ + if (ops->lost == event__process_lost && + session->hists.stats.total_lost != 0) { + ui__warning("Processed %Lu events and LOST %Lu!\n\n" + "Check IO/CPU overload!\n\n", + session->hists.stats.total_period, + session->hists.stats.total_lost); + } + + if (session->hists.stats.nr_unknown_events != 0) { + ui__warning("Found %u unknown events!\n\n" + "Is this an older tool processing a perf.data " + "file generated by a more recent tool?\n\n" + "If that is not the case, consider " + "reporting to linux-kernel@vger.kernel.org.\n\n", + session->hists.stats.nr_unknown_events); + } + + if (session->hists.stats.nr_invalid_chains != 0) { + ui__warning("Found invalid callchains!\n\n" + "%u out of %u events were discarded for this reason.\n\n" + "Consider reporting to linux-kernel@vger.kernel.org.\n\n", + session->hists.stats.nr_invalid_chains, + session->hists.stats.nr_events[PERF_RECORD_SAMPLE]); + } +} + #define session_done() (*(volatile int *)(&session_done)) volatile int session_done; @@ -911,6 +940,7 @@ more: done: err = 0; out_err: + perf_session__warn_about_errors(self, ops); perf_session_free_sample_buffers(self); return err; } @@ -1023,32 +1053,7 @@ more: flush_sample_queue(session, ops); out_err: ui_progress__delete(progress); - - if (ops->lost == event__process_lost && - session->hists.stats.total_lost != 0) { - ui__warning("Processed %Lu events and LOST %Lu!\n\n" - "Check IO/CPU overload!\n\n", - session->hists.stats.total_period, - session->hists.stats.total_lost); - } - - if (session->hists.stats.nr_unknown_events != 0) { - ui__warning("Found %u unknown events!\n\n" - "Is this an older tool processing a perf.data " - "file generated by a more recent tool?\n\n" - "If that is not the case, consider " - "reporting to linux-kernel@vger.kernel.org.\n\n", - session->hists.stats.nr_unknown_events); - } - - if (session->hists.stats.nr_invalid_chains != 0) { - ui__warning("Found invalid callchains!\n\n" - "%u out of %u events were discarded for this reason.\n\n" - "Consider reporting to linux-kernel@vger.kernel.org.\n\n", - session->hists.stats.nr_invalid_chains, - session->hists.stats.nr_events[PERF_RECORD_SAMPLE]); - } - + perf_session__warn_about_errors(session, ops); perf_session_free_sample_buffers(session); return err; } -- cgit v1.2.3