diff options
Diffstat (limited to 'tools/perf/util/session.c')
-rw-r--r-- | tools/perf/util/session.c | 192 |
1 files changed, 183 insertions, 9 deletions
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index ddf288fca3eb..0fdf3ebef1e9 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -14,6 +14,16 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
14 | { | 14 | { |
15 | struct stat input_stat; | 15 | struct stat input_stat; |
16 | 16 | ||
17 | if (!strcmp(self->filename, "-")) { | ||
18 | self->fd_pipe = true; | ||
19 | self->fd = STDIN_FILENO; | ||
20 | |||
21 | if (perf_header__read(self, self->fd) < 0) | ||
22 | pr_err("incompatible file format"); | ||
23 | |||
24 | return 0; | ||
25 | } | ||
26 | |||
17 | self->fd = open(self->filename, O_RDONLY); | 27 | self->fd = open(self->filename, O_RDONLY); |
18 | if (self->fd < 0) { | 28 | if (self->fd < 0) { |
19 | pr_err("failed to open file: %s", self->filename); | 29 | pr_err("failed to open file: %s", self->filename); |
@@ -38,7 +48,7 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
38 | goto out_close; | 48 | goto out_close; |
39 | } | 49 | } |
40 | 50 | ||
41 | if (perf_header__read(&self->header, self->fd) < 0) { | 51 | if (perf_header__read(self, self->fd) < 0) { |
42 | pr_err("incompatible file format"); | 52 | pr_err("incompatible file format"); |
43 | goto out_close; | 53 | goto out_close; |
44 | } | 54 | } |
@@ -52,6 +62,11 @@ out_close: | |||
52 | return -1; | 62 | return -1; |
53 | } | 63 | } |
54 | 64 | ||
65 | void perf_session__update_sample_type(struct perf_session *self) | ||
66 | { | ||
67 | self->sample_type = perf_header__sample_type(&self->header); | ||
68 | } | ||
69 | |||
55 | struct perf_session *perf_session__new(const char *filename, int mode, bool force) | 70 | struct perf_session *perf_session__new(const char *filename, int mode, bool force) |
56 | { | 71 | { |
57 | size_t len = filename ? strlen(filename) + 1 : 0; | 72 | size_t len = filename ? strlen(filename) + 1 : 0; |
@@ -85,7 +100,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc | |||
85 | goto out_delete; | 100 | goto out_delete; |
86 | } | 101 | } |
87 | 102 | ||
88 | self->sample_type = perf_header__sample_type(&self->header); | 103 | perf_session__update_sample_type(self); |
89 | out: | 104 | out: |
90 | return self; | 105 | return self; |
91 | out_free: | 106 | out_free: |
@@ -185,6 +200,14 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | |||
185 | handler->throttle = process_event_stub; | 200 | handler->throttle = process_event_stub; |
186 | if (handler->unthrottle == NULL) | 201 | if (handler->unthrottle == NULL) |
187 | handler->unthrottle = process_event_stub; | 202 | handler->unthrottle = process_event_stub; |
203 | if (handler->attr == NULL) | ||
204 | handler->attr = process_event_stub; | ||
205 | if (handler->event_type == NULL) | ||
206 | handler->event_type = process_event_stub; | ||
207 | if (handler->tracing_data == NULL) | ||
208 | handler->tracing_data = process_event_stub; | ||
209 | if (handler->build_id == NULL) | ||
210 | handler->build_id = process_event_stub; | ||
188 | } | 211 | } |
189 | 212 | ||
190 | static const char *event__name[] = { | 213 | static const char *event__name[] = { |
@@ -198,16 +221,23 @@ static const char *event__name[] = { | |||
198 | [PERF_RECORD_FORK] = "FORK", | 221 | [PERF_RECORD_FORK] = "FORK", |
199 | [PERF_RECORD_READ] = "READ", | 222 | [PERF_RECORD_READ] = "READ", |
200 | [PERF_RECORD_SAMPLE] = "SAMPLE", | 223 | [PERF_RECORD_SAMPLE] = "SAMPLE", |
224 | [PERF_RECORD_HEADER_ATTR] = "ATTR", | ||
225 | [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", | ||
226 | [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", | ||
227 | [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", | ||
201 | }; | 228 | }; |
202 | 229 | ||
203 | unsigned long event__total[PERF_RECORD_MAX]; | 230 | unsigned long event__total[PERF_RECORD_HEADER_MAX]; |
204 | 231 | ||
205 | void event__print_totals(void) | 232 | void event__print_totals(void) |
206 | { | 233 | { |
207 | int i; | 234 | int i; |
208 | for (i = 0; i < PERF_RECORD_MAX; ++i) | 235 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { |
236 | if (!event__name[i]) | ||
237 | continue; | ||
209 | pr_info("%10s events: %10ld\n", | 238 | pr_info("%10s events: %10ld\n", |
210 | event__name[i], event__total[i]); | 239 | event__name[i], event__total[i]); |
240 | } | ||
211 | } | 241 | } |
212 | 242 | ||
213 | void mem_bswap_64(void *src, int byte_size) | 243 | void mem_bswap_64(void *src, int byte_size) |
@@ -261,6 +291,37 @@ static void event__read_swap(event_t *self) | |||
261 | self->read.id = bswap_64(self->read.id); | 291 | self->read.id = bswap_64(self->read.id); |
262 | } | 292 | } |
263 | 293 | ||
294 | static void event__attr_swap(event_t *self) | ||
295 | { | ||
296 | size_t size; | ||
297 | |||
298 | self->attr.attr.type = bswap_32(self->attr.attr.type); | ||
299 | self->attr.attr.size = bswap_32(self->attr.attr.size); | ||
300 | self->attr.attr.config = bswap_64(self->attr.attr.config); | ||
301 | self->attr.attr.sample_period = bswap_64(self->attr.attr.sample_period); | ||
302 | self->attr.attr.sample_type = bswap_64(self->attr.attr.sample_type); | ||
303 | self->attr.attr.read_format = bswap_64(self->attr.attr.read_format); | ||
304 | self->attr.attr.wakeup_events = bswap_32(self->attr.attr.wakeup_events); | ||
305 | self->attr.attr.bp_type = bswap_32(self->attr.attr.bp_type); | ||
306 | self->attr.attr.bp_addr = bswap_64(self->attr.attr.bp_addr); | ||
307 | self->attr.attr.bp_len = bswap_64(self->attr.attr.bp_len); | ||
308 | |||
309 | size = self->header.size; | ||
310 | size -= (void *)&self->attr.id - (void *)self; | ||
311 | mem_bswap_64(self->attr.id, size); | ||
312 | } | ||
313 | |||
314 | static void event__event_type_swap(event_t *self) | ||
315 | { | ||
316 | self->event_type.event_type.event_id = | ||
317 | bswap_64(self->event_type.event_type.event_id); | ||
318 | } | ||
319 | |||
320 | static void event__tracing_data_swap(event_t *self) | ||
321 | { | ||
322 | self->tracing_data.size = bswap_32(self->tracing_data.size); | ||
323 | } | ||
324 | |||
264 | typedef void (*event__swap_op)(event_t *self); | 325 | typedef void (*event__swap_op)(event_t *self); |
265 | 326 | ||
266 | static event__swap_op event__swap_ops[] = { | 327 | static event__swap_op event__swap_ops[] = { |
@@ -271,7 +332,11 @@ static event__swap_op event__swap_ops[] = { | |||
271 | [PERF_RECORD_LOST] = event__all64_swap, | 332 | [PERF_RECORD_LOST] = event__all64_swap, |
272 | [PERF_RECORD_READ] = event__read_swap, | 333 | [PERF_RECORD_READ] = event__read_swap, |
273 | [PERF_RECORD_SAMPLE] = event__all64_swap, | 334 | [PERF_RECORD_SAMPLE] = event__all64_swap, |
274 | [PERF_RECORD_MAX] = NULL, | 335 | [PERF_RECORD_HEADER_ATTR] = event__attr_swap, |
336 | [PERF_RECORD_HEADER_EVENT_TYPE] = event__event_type_swap, | ||
337 | [PERF_RECORD_HEADER_TRACING_DATA] = event__tracing_data_swap, | ||
338 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, | ||
339 | [PERF_RECORD_HEADER_MAX] = NULL, | ||
275 | }; | 340 | }; |
276 | 341 | ||
277 | static int perf_session__process_event(struct perf_session *self, | 342 | static int perf_session__process_event(struct perf_session *self, |
@@ -281,7 +346,7 @@ static int perf_session__process_event(struct perf_session *self, | |||
281 | { | 346 | { |
282 | trace_event(event); | 347 | trace_event(event); |
283 | 348 | ||
284 | if (event->header.type < PERF_RECORD_MAX) { | 349 | if (event->header.type < PERF_RECORD_HEADER_MAX) { |
285 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", | 350 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", |
286 | offset + head, event->header.size, | 351 | offset + head, event->header.size, |
287 | event__name[event->header.type]); | 352 | event__name[event->header.type]); |
@@ -311,6 +376,16 @@ static int perf_session__process_event(struct perf_session *self, | |||
311 | return ops->throttle(event, self); | 376 | return ops->throttle(event, self); |
312 | case PERF_RECORD_UNTHROTTLE: | 377 | case PERF_RECORD_UNTHROTTLE: |
313 | return ops->unthrottle(event, self); | 378 | return ops->unthrottle(event, self); |
379 | case PERF_RECORD_HEADER_ATTR: | ||
380 | return ops->attr(event, self); | ||
381 | case PERF_RECORD_HEADER_EVENT_TYPE: | ||
382 | return ops->event_type(event, self); | ||
383 | case PERF_RECORD_HEADER_TRACING_DATA: | ||
384 | /* setup for reading amidst mmap */ | ||
385 | lseek(self->fd, offset + head, SEEK_SET); | ||
386 | return ops->tracing_data(event, self); | ||
387 | case PERF_RECORD_HEADER_BUILD_ID: | ||
388 | return ops->build_id(event, self); | ||
314 | default: | 389 | default: |
315 | self->unknown_events++; | 390 | self->unknown_events++; |
316 | return -1; | 391 | return -1; |
@@ -376,6 +451,101 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se | |||
376 | return thread; | 451 | return thread; |
377 | } | 452 | } |
378 | 453 | ||
454 | int do_read(int fd, void *buf, size_t size) | ||
455 | { | ||
456 | void *buf_start = buf; | ||
457 | |||
458 | while (size) { | ||
459 | int ret = read(fd, buf, size); | ||
460 | |||
461 | if (ret <= 0) | ||
462 | return ret; | ||
463 | |||
464 | size -= ret; | ||
465 | buf += ret; | ||
466 | } | ||
467 | |||
468 | return buf - buf_start; | ||
469 | } | ||
470 | |||
471 | #define session_done() (*(volatile int *)(&session_done)) | ||
472 | volatile int session_done; | ||
473 | |||
474 | static int __perf_session__process_pipe_events(struct perf_session *self, | ||
475 | struct perf_event_ops *ops) | ||
476 | { | ||
477 | event_t event; | ||
478 | uint32_t size; | ||
479 | int skip = 0; | ||
480 | u64 head; | ||
481 | int err; | ||
482 | void *p; | ||
483 | |||
484 | perf_event_ops__fill_defaults(ops); | ||
485 | |||
486 | head = 0; | ||
487 | more: | ||
488 | err = do_read(self->fd, &event, sizeof(struct perf_event_header)); | ||
489 | if (err <= 0) { | ||
490 | if (err == 0) | ||
491 | goto done; | ||
492 | |||
493 | pr_err("failed to read event header\n"); | ||
494 | goto out_err; | ||
495 | } | ||
496 | |||
497 | if (self->header.needs_swap) | ||
498 | perf_event_header__bswap(&event.header); | ||
499 | |||
500 | size = event.header.size; | ||
501 | if (size == 0) | ||
502 | size = 8; | ||
503 | |||
504 | p = &event; | ||
505 | p += sizeof(struct perf_event_header); | ||
506 | |||
507 | err = do_read(self->fd, p, size - sizeof(struct perf_event_header)); | ||
508 | if (err <= 0) { | ||
509 | if (err == 0) { | ||
510 | pr_err("unexpected end of event stream\n"); | ||
511 | goto done; | ||
512 | } | ||
513 | |||
514 | pr_err("failed to read event data\n"); | ||
515 | goto out_err; | ||
516 | } | ||
517 | |||
518 | if (size == 0 || | ||
519 | (skip = perf_session__process_event(self, &event, ops, | ||
520 | 0, head)) < 0) { | ||
521 | dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", | ||
522 | head, event.header.size, event.header.type); | ||
523 | /* | ||
524 | * assume we lost track of the stream, check alignment, and | ||
525 | * increment a single u64 in the hope to catch on again 'soon'. | ||
526 | */ | ||
527 | if (unlikely(head & 7)) | ||
528 | head &= ~7ULL; | ||
529 | |||
530 | size = 8; | ||
531 | } | ||
532 | |||
533 | head += size; | ||
534 | |||
535 | dump_printf("\n%#Lx [%#x]: event: %d\n", | ||
536 | head, event.header.size, event.header.type); | ||
537 | |||
538 | if (skip > 0) | ||
539 | head += skip; | ||
540 | |||
541 | if (!session_done()) | ||
542 | goto more; | ||
543 | done: | ||
544 | err = 0; | ||
545 | out_err: | ||
546 | return err; | ||
547 | } | ||
548 | |||
379 | int __perf_session__process_events(struct perf_session *self, | 549 | int __perf_session__process_events(struct perf_session *self, |
380 | u64 data_offset, u64 data_size, | 550 | u64 data_offset, u64 data_size, |
381 | u64 file_size, struct perf_event_ops *ops) | 551 | u64 file_size, struct perf_event_ops *ops) |
@@ -499,9 +669,13 @@ out_getcwd_err: | |||
499 | self->cwdlen = strlen(self->cwd); | 669 | self->cwdlen = strlen(self->cwd); |
500 | } | 670 | } |
501 | 671 | ||
502 | err = __perf_session__process_events(self, self->header.data_offset, | 672 | if (!self->fd_pipe) |
503 | self->header.data_size, | 673 | err = __perf_session__process_events(self, |
504 | self->size, ops); | 674 | self->header.data_offset, |
675 | self->header.data_size, | ||
676 | self->size, ops); | ||
677 | else | ||
678 | err = __perf_session__process_pipe_events(self, ops); | ||
505 | out_err: | 679 | out_err: |
506 | return err; | 680 | return err; |
507 | } | 681 | } |