diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-13 17:39:42 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-13 17:39:42 -0500 |
| commit | 9fdfbc2bff587f454dd95e2caa6d147c9abe39e4 (patch) | |
| tree | 2feaee47cbcfb57dd0d5cf23509e22011541e717 /tools | |
| parent | 8cea4eb642890a1de58980e7e1617d1765ef8f7c (diff) | |
| parent | dc1d628a67a8f042e711ea5accc0beedc3ef0092 (diff) | |
Merge branch 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
perf: Provide generic perf_sample_data initialization
MAINTAINERS: Add Arnaldo as tools/perf/ co-maintainer
perf trace: Don't use pager if scripting
perf trace/scripting: Remove extraneous header read
perf, ARM: Modify kuser rmb() call to compile for Thumb-2
x86/stacktrace: Don't dereference bad frame pointers
perf archive: Don't try to collect files without a build-id
perf_events, x86: Fixup fixed counter constraints
perf, x86: Restrict the ANY flag
perf, x86: rename macro in ARCH_PERFMON_EVENTSEL_ENABLE
perf, x86: add some IBS macros to perf_event.h
perf, x86: make IBS macros available in perf_event.h
hw-breakpoints: Remove stub unthrottle callback
x86/hw-breakpoints: Remove the name field
perf: Remove pointless breakpoint union
perf lock: Drop the buffers multiplexing dependency
perf lock: Fix and add misc documentally things
percpu: Add __percpu sparse annotations to hw_breakpoint
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/perf/Documentation/perf-lock.txt | 29 | ||||
| -rw-r--r-- | tools/perf/builtin-lock.c | 148 | ||||
| -rw-r--r-- | tools/perf/builtin-trace.c | 4 | ||||
| -rw-r--r-- | tools/perf/command-list.txt | 1 | ||||
| -rw-r--r-- | tools/perf/perf-archive.sh | 3 | ||||
| -rw-r--r-- | tools/perf/perf.h | 4 | ||||
| -rw-r--r-- | tools/perf/util/probe-event.c | 2 |
7 files changed, 182 insertions, 9 deletions
diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentation/perf-lock.txt new file mode 100644 index 000000000000..b317102138c8 --- /dev/null +++ b/tools/perf/Documentation/perf-lock.txt | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | perf-lock(1) | ||
| 2 | ============ | ||
| 3 | |||
| 4 | NAME | ||
| 5 | ---- | ||
| 6 | perf-lock - Analyze lock events | ||
| 7 | |||
| 8 | SYNOPSIS | ||
| 9 | -------- | ||
| 10 | [verse] | ||
| 11 | 'perf lock' {record|report|trace} | ||
| 12 | |||
| 13 | DESCRIPTION | ||
| 14 | ----------- | ||
| 15 | You can analyze various lock behaviours | ||
| 16 | and statistics with this 'perf lock' command. | ||
| 17 | |||
| 18 | 'perf lock record <command>' records lock events | ||
| 19 | between start and end <command>. And this command | ||
| 20 | produces the file "perf.data" which contains tracing | ||
| 21 | results of lock events. | ||
| 22 | |||
| 23 | 'perf lock trace' shows raw lock events. | ||
| 24 | |||
| 25 | 'perf lock report' reports statistical data. | ||
| 26 | |||
| 27 | SEE ALSO | ||
| 28 | -------- | ||
| 29 | linkperf:perf[1] | ||
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index fb9ab2ad3f92..e12c844df1e2 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c | |||
| @@ -460,6 +460,150 @@ process_raw_event(void *data, int cpu, | |||
| 460 | process_lock_release_event(data, event, cpu, timestamp, thread); | 460 | process_lock_release_event(data, event, cpu, timestamp, thread); |
| 461 | } | 461 | } |
| 462 | 462 | ||
| 463 | struct raw_event_queue { | ||
| 464 | u64 timestamp; | ||
| 465 | int cpu; | ||
| 466 | void *data; | ||
| 467 | struct thread *thread; | ||
| 468 | struct list_head list; | ||
| 469 | }; | ||
| 470 | |||
| 471 | static LIST_HEAD(raw_event_head); | ||
| 472 | |||
| 473 | #define FLUSH_PERIOD (5 * NSEC_PER_SEC) | ||
| 474 | |||
| 475 | static u64 flush_limit = ULLONG_MAX; | ||
| 476 | static u64 last_flush = 0; | ||
| 477 | struct raw_event_queue *last_inserted; | ||
| 478 | |||
| 479 | static void flush_raw_event_queue(u64 limit) | ||
| 480 | { | ||
| 481 | struct raw_event_queue *tmp, *iter; | ||
| 482 | |||
| 483 | list_for_each_entry_safe(iter, tmp, &raw_event_head, list) { | ||
| 484 | if (iter->timestamp > limit) | ||
| 485 | return; | ||
| 486 | |||
| 487 | if (iter == last_inserted) | ||
| 488 | last_inserted = NULL; | ||
| 489 | |||
| 490 | process_raw_event(iter->data, iter->cpu, iter->timestamp, | ||
| 491 | iter->thread); | ||
| 492 | |||
| 493 | last_flush = iter->timestamp; | ||
| 494 | list_del(&iter->list); | ||
| 495 | free(iter->data); | ||
| 496 | free(iter); | ||
| 497 | } | ||
| 498 | } | ||
| 499 | |||
| 500 | static void __queue_raw_event_end(struct raw_event_queue *new) | ||
| 501 | { | ||
| 502 | struct raw_event_queue *iter; | ||
| 503 | |||
| 504 | list_for_each_entry_reverse(iter, &raw_event_head, list) { | ||
| 505 | if (iter->timestamp < new->timestamp) { | ||
| 506 | list_add(&new->list, &iter->list); | ||
| 507 | return; | ||
| 508 | } | ||
| 509 | } | ||
| 510 | |||
| 511 | list_add(&new->list, &raw_event_head); | ||
| 512 | } | ||
| 513 | |||
| 514 | static void __queue_raw_event_before(struct raw_event_queue *new, | ||
| 515 | struct raw_event_queue *iter) | ||
| 516 | { | ||
| 517 | list_for_each_entry_continue_reverse(iter, &raw_event_head, list) { | ||
| 518 | if (iter->timestamp < new->timestamp) { | ||
| 519 | list_add(&new->list, &iter->list); | ||
| 520 | return; | ||
| 521 | } | ||
| 522 | } | ||
| 523 | |||
| 524 | list_add(&new->list, &raw_event_head); | ||
| 525 | } | ||
| 526 | |||
| 527 | static void __queue_raw_event_after(struct raw_event_queue *new, | ||
| 528 | struct raw_event_queue *iter) | ||
| 529 | { | ||
| 530 | list_for_each_entry_continue(iter, &raw_event_head, list) { | ||
| 531 | if (iter->timestamp > new->timestamp) { | ||
| 532 | list_add_tail(&new->list, &iter->list); | ||
| 533 | return; | ||
| 534 | } | ||
| 535 | } | ||
| 536 | list_add_tail(&new->list, &raw_event_head); | ||
| 537 | } | ||
| 538 | |||
| 539 | /* The queue is ordered by time */ | ||
| 540 | static void __queue_raw_event(struct raw_event_queue *new) | ||
| 541 | { | ||
| 542 | if (!last_inserted) { | ||
| 543 | __queue_raw_event_end(new); | ||
| 544 | return; | ||
| 545 | } | ||
| 546 | |||
| 547 | /* | ||
| 548 | * Most of the time the current event has a timestamp | ||
| 549 | * very close to the last event inserted, unless we just switched | ||
| 550 | * to another event buffer. Having a sorting based on a list and | ||
| 551 | * on the last inserted event that is close to the current one is | ||
| 552 | * probably more efficient than an rbtree based sorting. | ||
| 553 | */ | ||
| 554 | if (last_inserted->timestamp >= new->timestamp) | ||
| 555 | __queue_raw_event_before(new, last_inserted); | ||
| 556 | else | ||
| 557 | __queue_raw_event_after(new, last_inserted); | ||
| 558 | } | ||
| 559 | |||
| 560 | static void queue_raw_event(void *data, int raw_size, int cpu, | ||
| 561 | u64 timestamp, struct thread *thread) | ||
| 562 | { | ||
| 563 | struct raw_event_queue *new; | ||
| 564 | |||
| 565 | if (flush_limit == ULLONG_MAX) | ||
| 566 | flush_limit = timestamp + FLUSH_PERIOD; | ||
| 567 | |||
| 568 | if (timestamp < last_flush) { | ||
| 569 | printf("Warning: Timestamp below last timeslice flush\n"); | ||
| 570 | return; | ||
| 571 | } | ||
| 572 | |||
| 573 | new = malloc(sizeof(*new)); | ||
| 574 | if (!new) | ||
| 575 | die("Not enough memory\n"); | ||
| 576 | |||
| 577 | new->timestamp = timestamp; | ||
| 578 | new->cpu = cpu; | ||
| 579 | new->thread = thread; | ||
| 580 | |||
| 581 | new->data = malloc(raw_size); | ||
| 582 | if (!new->data) | ||
| 583 | die("Not enough memory\n"); | ||
| 584 | |||
| 585 | memcpy(new->data, data, raw_size); | ||
| 586 | |||
| 587 | __queue_raw_event(new); | ||
| 588 | last_inserted = new; | ||
| 589 | |||
| 590 | /* | ||
| 591 | * We want to have a slice of events covering 2 * FLUSH_PERIOD | ||
| 592 | * If FLUSH_PERIOD is big enough, it ensures every events that occured | ||
| 593 | * in the first half of the timeslice have all been buffered and there | ||
| 594 | * are none remaining (we need that because of the weakly ordered | ||
| 595 | * event recording we have). Then once we reach the 2 * FLUSH_PERIOD | ||
| 596 | * timeslice, we flush the first half to be gentle with the memory | ||
| 597 | * (the second half can still get new events in the middle, so wait | ||
| 598 | * another period to flush it) | ||
| 599 | */ | ||
| 600 | if (new->timestamp > flush_limit && | ||
| 601 | new->timestamp - flush_limit > FLUSH_PERIOD) { | ||
| 602 | flush_limit += FLUSH_PERIOD; | ||
| 603 | flush_raw_event_queue(flush_limit); | ||
| 604 | } | ||
| 605 | } | ||
| 606 | |||
| 463 | |||
