diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-06-07 11:31:52 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-06-07 11:31:52 -0400 |
commit | 716c69fecacd42f2a304a97158e04af2786a3f65 (patch) | |
tree | 5f86a0ce67ba87595d14a6fd6d5f59f8f7cf1f56 | |
parent | 743ee1f80434138495bbb95ffb897acf46b51d54 (diff) |
perf top: Fall back to cpu-clock-tick hrtimer sampling if no cycle counter available
On architectures/CPUs without PMU support but with perfcounters
enabled 'perf top' currently fails because it cannot create a
cycle based hw-perfcounter.
Fall back to the cpu-clock-tick sw-perfcounter in this case, which
is hrtimer based and will always work (as long as perfcounters
is enabled).
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | tools/perf/builtin-top.c | 113 | ||||
-rw-r--r-- | tools/perf/util/usage.c | 10 |
2 files changed, 73 insertions, 50 deletions
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index fdc1d5863b01..6da30a140e86 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -527,58 +527,81 @@ static void mmap_read(void) | |||
527 | } | 527 | } |
528 | } | 528 | } |
529 | 529 | ||
530 | static int __cmd_top(void) | 530 | int nr_poll; |
531 | int group_fd; | ||
532 | |||
533 | static void start_counter(int i, int counter) | ||
531 | { | 534 | { |
532 | struct perf_counter_attr *attr; | 535 | struct perf_counter_attr *attr; |
533 | pthread_t thread; | ||
534 | int i, counter, group_fd, nr_poll = 0; | ||
535 | unsigned int cpu; | 536 | unsigned int cpu; |
537 | |||
538 | cpu = profile_cpu; | ||
539 | if (target_pid == -1 && profile_cpu == -1) | ||
540 | cpu = i; | ||
541 | |||
542 | attr = attrs + counter; | ||
543 | |||
544 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | ||
545 | attr->freq = freq; | ||
546 | |||
547 | try_again: | ||
548 | fd[i][counter] = sys_perf_counter_open(attr, target_pid, cpu, group_fd, 0); | ||
549 | |||
550 | if (fd[i][counter] < 0) { | ||
551 | int err = errno; | ||
552 | |||
553 | error("sys_perf_counter_open() syscall returned with %d (%s)\n", | ||
554 | fd[i][counter], strerror(err)); | ||
555 | |||
556 | if (err == EPERM) | ||
557 | die(" No permission - are you root?\n"); | ||
558 | /* | ||
559 | * If it's cycles then fall back to hrtimer | ||
560 | * based cpu-clock-tick sw counter, which | ||
561 | * is always available even if no PMU support: | ||
562 | */ | ||
563 | if (attr->type == PERF_TYPE_HARDWARE | ||
564 | && attr->config == PERF_COUNT_CPU_CYCLES) { | ||
565 | |||
566 | warning(" ... trying to fall back to cpu-clock-ticks\n"); | ||
567 | attr->type = PERF_TYPE_SOFTWARE; | ||
568 | attr->config = PERF_COUNT_CPU_CLOCK; | ||
569 | goto try_again; | ||
570 | } | ||
571 | exit(-1); | ||
572 | } | ||
573 | assert(fd[i][counter] >= 0); | ||
574 | fcntl(fd[i][counter], F_SETFL, O_NONBLOCK); | ||
575 | |||
576 | /* | ||
577 | * First counter acts as the group leader: | ||
578 | */ | ||
579 | if (group && group_fd == -1) | ||
580 | group_fd = fd[i][counter]; | ||
581 | |||
582 | event_array[nr_poll].fd = fd[i][counter]; | ||
583 | event_array[nr_poll].events = POLLIN; | ||
584 | nr_poll++; | ||
585 | |||
586 | mmap_array[i][counter].counter = counter; | ||
587 | mmap_array[i][counter].prev = 0; | ||
588 | mmap_array[i][counter].mask = mmap_pages*page_size - 1; | ||
589 | mmap_array[i][counter].base = mmap(NULL, (mmap_pages+1)*page_size, | ||
590 | PROT_READ, MAP_SHARED, fd[i][counter], 0); | ||
591 | if (mmap_array[i][counter].base == MAP_FAILED) | ||
592 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); | ||
593 | } | ||
594 | |||
595 | static int __cmd_top(void) | ||
596 | { | ||
597 | pthread_t thread; | ||
598 | int i, counter; | ||
536 | int ret; | 599 | int ret; |
537 | 600 | ||
538 | for (i = 0; i < nr_cpus; i++) { | 601 | for (i = 0; i < nr_cpus; i++) { |
539 | group_fd = -1; | 602 | group_fd = -1; |
540 | for (counter = 0; counter < nr_counters; counter++) { | 603 | for (counter = 0; counter < nr_counters; counter++) |
541 | 604 | start_counter(i, counter); | |
542 | cpu = profile_cpu; | ||
543 | if (target_pid == -1 && profile_cpu == -1) | ||
544 | cpu = i; | ||
545 | |||
546 | attr = attrs + counter; | ||
547 | |||
548 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | ||
549 | attr->freq = freq; | ||
550 | |||
551 | fd[i][counter] = sys_perf_counter_open(attr, target_pid, cpu, group_fd, 0); | ||
552 | if (fd[i][counter] < 0) { | ||
553 | int err = errno; | ||
554 | |||
555 | error("syscall returned with %d (%s)\n", | ||
556 | fd[i][counter], strerror(err)); | ||
557 | if (err == EPERM) | ||
558 | printf("Are you root?\n"); | ||
559 | exit(-1); | ||
560 | } | ||
561 | assert(fd[i][counter] >= 0); | ||
562 | fcntl(fd[i][counter], F_SETFL, O_NONBLOCK); | ||
563 | |||
564 | /* | ||
565 | * First counter acts as the group leader: | ||
566 | */ | ||
567 | if (group && group_fd == -1) | ||
568 | group_fd = fd[i][counter]; | ||
569 | |||
570 | event_array[nr_poll].fd = fd[i][counter]; | ||
571 | event_array[nr_poll].events = POLLIN; | ||
572 | nr_poll++; | ||
573 | |||
574 | mmap_array[i][counter].counter = counter; | ||
575 | mmap_array[i][counter].prev = 0; | ||
576 | mmap_array[i][counter].mask = mmap_pages*page_size - 1; | ||
577 | mmap_array[i][counter].base = mmap(NULL, (mmap_pages+1)*page_size, | ||
578 | PROT_READ, MAP_SHARED, fd[i][counter], 0); | ||
579 | if (mmap_array[i][counter].base == MAP_FAILED) | ||
580 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); | ||
581 | } | ||
582 | } | 605 | } |
583 | 606 | ||
584 | /* Wait for a minimal set of events before starting the snapshot */ | 607 | /* Wait for a minimal set of events before starting the snapshot */ |
diff --git a/tools/perf/util/usage.c b/tools/perf/util/usage.c index 2cad286e4371..e16bf9a707e8 100644 --- a/tools/perf/util/usage.c +++ b/tools/perf/util/usage.c | |||
@@ -9,29 +9,29 @@ static void report(const char *prefix, const char *err, va_list params) | |||
9 | { | 9 | { |
10 | char msg[1024]; | 10 | char msg[1024]; |
11 | vsnprintf(msg, sizeof(msg), err, params); | 11 | vsnprintf(msg, sizeof(msg), err, params); |
12 | fprintf(stderr, "%s%s\n", prefix, msg); | 12 | fprintf(stderr, " %s%s\n", prefix, msg); |
13 | } | 13 | } |
14 | 14 | ||
15 | static NORETURN void usage_builtin(const char *err) | 15 | static NORETURN void usage_builtin(const char *err) |
16 | { | 16 | { |
17 | fprintf(stderr, "\n usage: %s\n", err); | 17 | fprintf(stderr, "\n Usage: %s\n", err); |
18 | exit(129); | 18 | exit(129); |
19 | } | 19 | } |
20 | 20 | ||
21 | static NORETURN void die_builtin(const char *err, va_list params) | 21 | static NORETURN void die_builtin(const char *err, va_list params) |
22 | { | 22 | { |
23 | report("fatal: ", err, params); | 23 | report(" Fatal: ", err, params); |
24 | exit(128); | 24 | exit(128); |
25 | } | 25 | } |
26 | 26 | ||
27 | static void error_builtin(const char *err, va_list params) | 27 | static void error_builtin(const char *err, va_list params) |
28 | { | 28 | { |
29 | report("error: ", err, params); | 29 | report(" Error: ", err, params); |
30 | } | 30 | } |
31 | 31 | ||
32 | static void warn_builtin(const char *warn, va_list params) | 32 | static void warn_builtin(const char *warn, va_list params) |
33 | { | 33 | { |
34 | report("warning: ", warn, params); | 34 | report(" Warning: ", warn, params); |
35 | } | 35 | } |
36 | 36 | ||
37 | /* If we are in a dlopen()ed .so write to a global variable would segfault | 37 | /* If we are in a dlopen()ed .so write to a global variable would segfault |