diff options
author | Ingo Molnar <mingo@elte.hu> | 2010-05-10 02:20:19 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-05-10 02:20:19 -0400 |
commit | 1f0ac7183f4d270bd9ce511254ba5d931d4f29c9 (patch) | |
tree | 124b2682a249b0393f29e929537aa76ab299bb5f /tools/perf/util | |
parent | 232a5c948da5e23dff27e48180abf4a4238f7602 (diff) | |
parent | 76ba7e846fcc89d9d4b25b89e303c9058de96d60 (diff) |
Merge branch 'perf/test' of git://git.kernel.org/pub/scm/linux/kernel/git/frederic/random-tracing into perf/core
Diffstat (limited to 'tools/perf/util')
-rw-r--r-- | tools/perf/util/event.h | 3 | ||||
-rw-r--r-- | tools/perf/util/session.c | 125 | ||||
-rw-r--r-- | tools/perf/util/session.h | 36 |
3 files changed, 110 insertions, 54 deletions
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index b364da5b0cbf..6cc1b1dced55 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -84,11 +84,12 @@ struct build_id_event { | |||
84 | char filename[]; | 84 | char filename[]; |
85 | }; | 85 | }; |
86 | 86 | ||
87 | enum perf_header_event_type { /* above any possible kernel type */ | 87 | enum perf_user_event_type { /* above any possible kernel type */ |
88 | PERF_RECORD_HEADER_ATTR = 64, | 88 | PERF_RECORD_HEADER_ATTR = 64, |
89 | PERF_RECORD_HEADER_EVENT_TYPE = 65, | 89 | PERF_RECORD_HEADER_EVENT_TYPE = 65, |
90 | PERF_RECORD_HEADER_TRACING_DATA = 66, | 90 | PERF_RECORD_HEADER_TRACING_DATA = 66, |
91 | PERF_RECORD_HEADER_BUILD_ID = 67, | 91 | PERF_RECORD_HEADER_BUILD_ID = 67, |
92 | PERF_RECORD_FINISHED_ROUND = 68, | ||
92 | PERF_RECORD_HEADER_MAX | 93 | PERF_RECORD_HEADER_MAX |
93 | }; | 94 | }; |
94 | 95 | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 71bc608e0ec6..c088d8f9b51c 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -98,7 +98,6 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc | |||
98 | self->unknown_events = 0; | 98 | self->unknown_events = 0; |
99 | self->machines = RB_ROOT; | 99 | self->machines = RB_ROOT; |
100 | self->repipe = repipe; | 100 | self->repipe = repipe; |
101 | self->ordered_samples.flush_limit = ULLONG_MAX; | ||
102 | INIT_LIST_HEAD(&self->ordered_samples.samples_head); | 101 | INIT_LIST_HEAD(&self->ordered_samples.samples_head); |
103 | machine__init(&self->host_machine, "", HOST_KERNEL_ID); | 102 | machine__init(&self->host_machine, "", HOST_KERNEL_ID); |
104 | 103 | ||
@@ -195,6 +194,18 @@ static int process_event_stub(event_t *event __used, | |||
195 | return 0; | 194 | return 0; |
196 | } | 195 | } |
197 | 196 | ||
197 | static int process_finished_round_stub(event_t *event __used, | ||
198 | struct perf_session *session __used, | ||
199 | struct perf_event_ops *ops __used) | ||
200 | { | ||
201 | dump_printf(": unhandled!\n"); | ||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static int process_finished_round(event_t *event, | ||
206 | struct perf_session *session, | ||
207 | struct perf_event_ops *ops); | ||
208 | |||
198 | static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | 209 | static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) |
199 | { | 210 | { |
200 | if (handler->sample == NULL) | 211 | if (handler->sample == NULL) |
@@ -223,6 +234,12 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | |||
223 | handler->tracing_data = process_event_stub; | 234 | handler->tracing_data = process_event_stub; |
224 | if (handler->build_id == NULL) | 235 | if (handler->build_id == NULL) |
225 | handler->build_id = process_event_stub; | 236 | handler->build_id = process_event_stub; |
237 | if (handler->finished_round == NULL) { | ||
238 | if (handler->ordered_samples) | ||
239 | handler->finished_round = process_finished_round; | ||
240 | else | ||
241 | handler->finished_round = process_finished_round_stub; | ||
242 | } | ||
226 | } | 243 | } |
227 | 244 | ||
228 | static const char *event__name[] = { | 245 | static const char *event__name[] = { |
@@ -360,16 +377,14 @@ struct sample_queue { | |||
360 | struct list_head list; | 377 | struct list_head list; |
361 | }; | 378 | }; |
362 | 379 | ||
363 | #define FLUSH_PERIOD (2 * NSEC_PER_SEC) | ||
364 | |||
365 | static void flush_sample_queue(struct perf_session *s, | 380 | static void flush_sample_queue(struct perf_session *s, |
366 | struct perf_event_ops *ops) | 381 | struct perf_event_ops *ops) |
367 | { | 382 | { |
368 | struct list_head *head = &s->ordered_samples.samples_head; | 383 | struct list_head *head = &s->ordered_samples.samples_head; |
369 | u64 limit = s->ordered_samples.flush_limit; | 384 | u64 limit = s->ordered_samples.next_flush; |
370 | struct sample_queue *tmp, *iter; | 385 | struct sample_queue *tmp, *iter; |
371 | 386 | ||
372 | if (!ops->ordered_samples) | 387 | if (!ops->ordered_samples || !limit) |
373 | return; | 388 | return; |
374 | 389 | ||
375 | list_for_each_entry_safe(iter, tmp, head, list) { | 390 | list_for_each_entry_safe(iter, tmp, head, list) { |
@@ -388,6 +403,55 @@ static void flush_sample_queue(struct perf_session *s, | |||
388 | } | 403 | } |
389 | } | 404 | } |
390 | 405 | ||
406 | /* | ||
407 | * When perf record finishes a pass on every buffers, it records this pseudo | ||
408 | * event. | ||
409 | * We record the max timestamp t found in the pass n. | ||
410 | * Assuming these timestamps are monotonic across cpus, we know that if | ||
411 | * a buffer still has events with timestamps below t, they will be all | ||
412 | * available and then read in the pass n + 1. | ||
413 | * Hence when we start to read the pass n + 2, we can safely flush every | ||
414 | * events with timestamps below t. | ||
415 | * | ||
416 | * ============ PASS n ================= | ||
417 | * CPU 0 | CPU 1 | ||
418 | * | | ||
419 | * cnt1 timestamps | cnt2 timestamps | ||
420 | * 1 | 2 | ||
421 | * 2 | 3 | ||
422 | * - | 4 <--- max recorded | ||
423 | * | ||
424 | * ============ PASS n + 1 ============== | ||
425 | * CPU 0 | CPU 1 | ||
426 | * | | ||
427 | * cnt1 timestamps | cnt2 timestamps | ||
428 | * 3 | 5 | ||
429 | * 4 | 6 | ||
430 | * 5 | 7 <---- max recorded | ||
431 | * | ||
432 | * Flush every events below timestamp 4 | ||
433 | * | ||
434 | * ============ PASS n + 2 ============== | ||
435 | * CPU 0 | CPU 1 | ||
436 | * | | ||
437 | * cnt1 timestamps | cnt2 timestamps | ||
438 | * 6 | 8 | ||
439 | * 7 | 9 | ||
440 | * - | 10 | ||
441 | * | ||
442 | * Flush every events below timestamp 7 | ||
443 | * etc... | ||
444 | */ | ||
445 | static int process_finished_round(event_t *event __used, | ||
446 | struct perf_session *session, | ||
447 | struct perf_event_ops *ops) | ||
448 | { | ||
449 | flush_sample_queue(session, ops); | ||
450 | session->ordered_samples.next_flush = session->ordered_samples.max_timestamp; | ||
451 | |||
452 | return 0; | ||
453 | } | ||
454 | |||
391 | static void __queue_sample_end(struct sample_queue *new, struct list_head *head) | 455 | static void __queue_sample_end(struct sample_queue *new, struct list_head *head) |
392 | { | 456 | { |
393 | struct sample_queue *iter; | 457 | struct sample_queue *iter; |
@@ -456,17 +520,12 @@ static void __queue_sample_event(struct sample_queue *new, | |||
456 | } | 520 | } |
457 | 521 | ||
458 | static int queue_sample_event(event_t *event, struct sample_data *data, | 522 | static int queue_sample_event(event_t *event, struct sample_data *data, |
459 | struct perf_session *s, | 523 | struct perf_session *s) |
460 | struct perf_event_ops *ops) | ||
461 | { | 524 | { |
462 | u64 timestamp = data->time; | 525 | u64 timestamp = data->time; |
463 | struct sample_queue *new; | 526 | struct sample_queue *new; |
464 | u64 flush_limit; | ||
465 | 527 | ||
466 | 528 | ||
467 | if (s->ordered_samples.flush_limit == ULLONG_MAX) | ||
468 | s->ordered_samples.flush_limit = timestamp + FLUSH_PERIOD; | ||
469 | |||
470 | if (timestamp < s->ordered_samples.last_flush) { | 529 | if (timestamp < s->ordered_samples.last_flush) { |
471 | printf("Warning: Timestamp below last timeslice flush\n"); | 530 | printf("Warning: Timestamp below last timeslice flush\n"); |
472 | return -EINVAL; | 531 | return -EINVAL; |
@@ -489,23 +548,8 @@ static int queue_sample_event(event_t *event, struct sample_data *data, | |||
489 | __queue_sample_event(new, s); | 548 | __queue_sample_event(new, s); |
490 | s->ordered_samples.last_inserted = new; | 549 | s->ordered_samples.last_inserted = new; |
491 | 550 | ||
492 | /* | 551 | if (new->timestamp > s->ordered_samples.max_timestamp) |
493 | * We want to have a slice of events covering 2 * FLUSH_PERIOD | 552 | s->ordered_samples.max_timestamp = new->timestamp; |
494 | * If FLUSH_PERIOD is big enough, it ensures every events that occured | ||
495 | * in the first half of the timeslice have all been buffered and there | ||
496 | * are none remaining (we need that because of the weakly ordered | ||
497 | * event recording we have). Then once we reach the 2 * FLUSH_PERIOD | ||
498 | * timeslice, we flush the first half to be gentle with the memory | ||
499 | * (the second half can still get new events in the middle, so wait | ||
500 | * another period to flush it) | ||
501 | */ | ||
502 | flush_limit = s->ordered_samples.flush_limit; | ||
503 | |||
504 | if (new->timestamp > flush_limit && | ||
505 | new->timestamp - flush_limit > FLUSH_PERIOD) { | ||
506 | s->ordered_samples.flush_limit += FLUSH_PERIOD; | ||
507 | flush_sample_queue(s, ops); | ||
508 | } | ||
509 | 553 | ||
510 | return 0; | 554 | return 0; |
511 | } | 555 | } |
@@ -521,7 +565,7 @@ static int perf_session__process_sample(event_t *event, struct perf_session *s, | |||
521 | bzero(&data, sizeof(struct sample_data)); | 565 | bzero(&data, sizeof(struct sample_data)); |
522 | event__parse_sample(event, s->sample_type, &data); | 566 | event__parse_sample(event, s->sample_type, &data); |
523 | 567 | ||
524 | queue_sample_event(event, &data, s, ops); | 568 | queue_sample_event(event, &data, s); |
525 | 569 | ||
526 | return 0; | 570 | return 0; |
527 | } | 571 | } |
@@ -573,6 +617,8 @@ static int perf_session__process_event(struct perf_session *self, | |||
573 | return ops->tracing_data(event, self); | 617 | return ops->tracing_data(event, self); |
574 | case PERF_RECORD_HEADER_BUILD_ID: | 618 | case PERF_RECORD_HEADER_BUILD_ID: |
575 | return ops->build_id(event, self); | 619 | return ops->build_id(event, self); |
620 | case PERF_RECORD_FINISHED_ROUND: | ||
621 | return ops->finished_round(event, self, ops); | ||
576 | default: | 622 | default: |
577 | self->unknown_events++; | 623 | self->unknown_events++; |
578 | return -1; | 624 | return -1; |
@@ -651,15 +697,18 @@ more: | |||
651 | p = &event; | 697 | p = &event; |
652 | p += sizeof(struct perf_event_header); | 698 | p += sizeof(struct perf_event_header); |
653 | 699 | ||
654 | err = do_read(self->fd, p, size - sizeof(struct perf_event_header)); | 700 | if (size - sizeof(struct perf_event_header)) { |
655 | if (err <= 0) { | 701 | err = do_read(self->fd, p, |
656 | if (err == 0) { | 702 | size - sizeof(struct perf_event_header)); |
657 | pr_err("unexpected end of event stream\n"); | 703 | if (err <= 0) { |
658 | goto done; | 704 | if (err == 0) { |
659 | } | 705 | pr_err("unexpected end of event stream\n"); |
706 | goto done; | ||
707 | } | ||
660 | 708 | ||
661 | pr_err("failed to read event data\n"); | 709 | pr_err("failed to read event data\n"); |
662 | goto out_err; | 710 | goto out_err; |
711 | } | ||
663 | } | 712 | } |
664 | 713 | ||
665 | if (size == 0 || | 714 | if (size == 0 || |
@@ -787,7 +836,7 @@ more: | |||
787 | done: | 836 | done: |
788 | err = 0; | 837 | err = 0; |
789 | /* do the final flush for ordered samples */ | 838 | /* do the final flush for ordered samples */ |
790 | self->ordered_samples.flush_limit = ULLONG_MAX; | 839 | self->ordered_samples.next_flush = ULLONG_MAX; |
791 | flush_sample_queue(self, ops); | 840 | flush_sample_queue(self, ops); |
792 | out_err: | 841 | out_err: |
793 | ui_progress__delete(progress); | 842 | ui_progress__delete(progress); |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index eb9f179376a5..242d528bfae2 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -14,7 +14,8 @@ struct thread; | |||
14 | 14 | ||
15 | struct ordered_samples { | 15 | struct ordered_samples { |
16 | u64 last_flush; | 16 | u64 last_flush; |
17 | u64 flush_limit; | 17 | u64 next_flush; |
18 | u64 max_timestamp; | ||
18 | struct list_head samples_head; | 19 | struct list_head samples_head; |
19 | struct sample_queue *last_inserted; | 20 | struct sample_queue *last_inserted; |
20 | }; | 21 | }; |
@@ -42,23 +43,28 @@ struct perf_session { | |||
42 | char filename[0]; | 43 | char filename[0]; |
43 | }; | 44 | }; |
44 | 45 | ||
46 | struct perf_event_ops; | ||
47 | |||
45 | typedef int (*event_op)(event_t *self, struct perf_session *session); | 48 | typedef int (*event_op)(event_t *self, struct perf_session *session); |
49 | typedef int (*event_op2)(event_t *self, struct perf_session *session, | ||
50 | struct perf_event_ops *ops); | ||
46 | 51 | ||
47 | struct perf_event_ops { | 52 | struct perf_event_ops { |
48 | event_op sample, | 53 | event_op sample, |
49 | mmap, | 54 | mmap, |
50 | comm, | 55 | comm, |
51 | fork, | 56 | fork, |
52 | exit, | 57 | exit, |
53 | lost, | 58 | lost, |
54 | read, | 59 | read, |
55 | throttle, | 60 | throttle, |
56 | unthrottle, | 61 | unthrottle, |
57 | attr, | 62 | attr, |
58 | event_type, | 63 | event_type, |
59 | tracing_data, | 64 | tracing_data, |
60 | build_id; | 65 | build_id; |
61 | bool ordered_samples; | 66 | event_op2 finished_round; |
67 | bool ordered_samples; | ||
62 | }; | 68 | }; |
63 | 69 | ||
64 | struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe); | 70 | struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe); |