Commit a12b51c478899fe0b7e874a559b05ba35f1128ee
Committed by
Ingo Molnar
1 parent
220b140b52
Exists in
master
and in
39 other branches
perf tools: Fix sparse CPU numbering related bugs
At present, the perf subcommands that do system-wide monitoring (perf stat, perf record and perf top) don't work properly unless the online cpus are numbered 0, 1, ..., N-1. These tools ask for the number of online cpus with sysconf(_SC_NPROCESSORS_ONLN) and then try to create events for cpus 0, 1, ..., N-1. This creates problems for systems where the online cpus are numbered sparsely. For example, a POWER6 system in single-threaded mode (i.e. only running 1 hardware thread per core) will have only even-numbered cpus online. This fixes the problem by reading the /sys/devices/system/cpu/online file to find out which cpus are online. The code that does that is in tools/perf/util/cpumap.[ch], and consists of a read_cpu_map() function that sets up a cpumap[] array and returns the number of online cpus. If /sys/devices/system/cpu/online can't be read or can't be parsed successfully, it falls back to using sysconf to ask how many cpus are online and sets up an identity map in cpumap[]. The perf record, perf stat and perf top code then calls read_cpu_map() in the system-wide monitoring case (instead of sysconf) and uses cpumap[] to get the cpu numbers to pass to perf_event_open. Signed-off-by: Paul Mackerras <paulus@samba.org> Cc: Anton Blanchard <anton@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Arnaldo Carvalho de Melo <acme@infradead.org> LKML-Reference: <20100310093609.GA3959@brick.ozlabs.ibm.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Showing 6 changed files with 81 additions and 13 deletions Side-by-side Diff
tools/perf/Makefile
... | ... | @@ -387,6 +387,7 @@ |
387 | 387 | LIB_H += util/trace-event.h |
388 | 388 | LIB_H += util/probe-finder.h |
389 | 389 | LIB_H += util/probe-event.h |
390 | +LIB_H += util/cpumap.h | |
390 | 391 | |
391 | 392 | LIB_OBJS += util/abspath.o |
392 | 393 | LIB_OBJS += util/alias.o |
... | ... | @@ -433,6 +434,7 @@ |
433 | 434 | LIB_OBJS += util/hist.o |
434 | 435 | LIB_OBJS += util/probe-event.o |
435 | 436 | LIB_OBJS += util/util.o |
437 | +LIB_OBJS += util/cpumap.o | |
436 | 438 | |
437 | 439 | BUILTIN_OBJS += builtin-annotate.o |
438 | 440 |
tools/perf/builtin-record.c
... | ... | @@ -22,6 +22,7 @@ |
22 | 22 | #include "util/debug.h" |
23 | 23 | #include "util/session.h" |
24 | 24 | #include "util/symbol.h" |
25 | +#include "util/cpumap.h" | |
25 | 26 | |
26 | 27 | #include <unistd.h> |
27 | 28 | #include <sched.h> |
... | ... | @@ -421,9 +422,6 @@ |
421 | 422 | char buf; |
422 | 423 | |
423 | 424 | page_size = sysconf(_SC_PAGE_SIZE); |
424 | - nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); | |
425 | - assert(nr_cpus <= MAX_NR_CPUS); | |
426 | - assert(nr_cpus >= 0); | |
427 | 425 | |
428 | 426 | atexit(sig_atexit); |
429 | 427 | signal(SIGCHLD, sig_handler); |
430 | 428 | |
... | ... | @@ -547,8 +545,9 @@ |
547 | 545 | if ((!system_wide && !inherit) || profile_cpu != -1) { |
548 | 546 | open_counters(profile_cpu, target_pid); |
549 | 547 | } else { |
548 | + nr_cpus = read_cpu_map(); | |
550 | 549 | for (i = 0; i < nr_cpus; i++) |
551 | - open_counters(i, target_pid); | |
550 | + open_counters(cpumap[i], target_pid); | |
552 | 551 | } |
553 | 552 | |
554 | 553 | if (file_new) { |
tools/perf/builtin-stat.c
... | ... | @@ -45,6 +45,7 @@ |
45 | 45 | #include "util/event.h" |
46 | 46 | #include "util/debug.h" |
47 | 47 | #include "util/header.h" |
48 | +#include "util/cpumap.h" | |
48 | 49 | |
49 | 50 | #include <sys/prctl.h> |
50 | 51 | #include <math.h> |
... | ... | @@ -151,7 +152,7 @@ |
151 | 152 | unsigned int cpu; |
152 | 153 | |
153 | 154 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
154 | - fd[cpu][counter] = sys_perf_event_open(attr, -1, cpu, -1, 0); | |
155 | + fd[cpu][counter] = sys_perf_event_open(attr, -1, cpumap[cpu], -1, 0); | |
155 | 156 | if (fd[cpu][counter] < 0 && verbose) |
156 | 157 | fprintf(stderr, ERR_PERF_OPEN, counter, |
157 | 158 | fd[cpu][counter], strerror(errno)); |
... | ... | @@ -519,9 +520,10 @@ |
519 | 520 | nr_counters = ARRAY_SIZE(default_attrs); |
520 | 521 | } |
521 | 522 | |
522 | - nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); | |
523 | - assert(nr_cpus <= MAX_NR_CPUS); | |
524 | - assert((int)nr_cpus >= 0); | |
523 | + if (system_wide) | |
524 | + nr_cpus = read_cpu_map(); | |
525 | + else | |
526 | + nr_cpus = 1; | |
525 | 527 | |
526 | 528 | /* |
527 | 529 | * We dont want to block the signals - that would cause |
tools/perf/builtin-top.c
... | ... | @@ -28,6 +28,7 @@ |
28 | 28 | #include <linux/rbtree.h> |
29 | 29 | #include "util/parse-options.h" |
30 | 30 | #include "util/parse-events.h" |
31 | +#include "util/cpumap.h" | |
31 | 32 | |
32 | 33 | #include "util/debug.h" |
33 | 34 | |
... | ... | @@ -1123,7 +1124,7 @@ |
1123 | 1124 | |
1124 | 1125 | cpu = profile_cpu; |
1125 | 1126 | if (target_pid == -1 && profile_cpu == -1) |
1126 | - cpu = i; | |
1127 | + cpu = cpumap[i]; | |
1127 | 1128 | |
1128 | 1129 | attr = attrs + counter; |
1129 | 1130 | |
1130 | 1131 | |
... | ... | @@ -1347,12 +1348,10 @@ |
1347 | 1348 | attrs[counter].sample_period = default_interval; |
1348 | 1349 | } |
1349 | 1350 | |
1350 | - nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); | |
1351 | - assert(nr_cpus <= MAX_NR_CPUS); | |
1352 | - assert(nr_cpus >= 0); | |
1353 | - | |
1354 | 1351 | if (target_pid != -1 || profile_cpu != -1) |
1355 | 1352 | nr_cpus = 1; |
1353 | + else | |
1354 | + nr_cpus = read_cpu_map(); | |
1356 | 1355 | |
1357 | 1356 | get_term_dimensions(&winsize); |
1358 | 1357 | if (print_entries == 0) { |
tools/perf/util/cpumap.c
1 | +#include "util.h" | |
2 | +#include "../perf.h" | |
3 | +#include "cpumap.h" | |
4 | +#include <assert.h> | |
5 | +#include <stdio.h> | |
6 | + | |
7 | +int cpumap[MAX_NR_CPUS]; | |
8 | + | |
9 | +static int default_cpu_map(void) | |
10 | +{ | |
11 | + int nr_cpus, i; | |
12 | + | |
13 | + nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); | |
14 | + assert(nr_cpus <= MAX_NR_CPUS); | |
15 | + assert((int)nr_cpus >= 0); | |
16 | + | |
17 | + for (i = 0; i < nr_cpus; ++i) | |
18 | + cpumap[i] = i; | |
19 | + | |
20 | + return nr_cpus; | |
21 | +} | |
22 | + | |
23 | +int read_cpu_map(void) | |
24 | +{ | |
25 | + FILE *onlnf; | |
26 | + int nr_cpus = 0; | |
27 | + int n, cpu, prev; | |
28 | + char sep; | |
29 | + | |
30 | + onlnf = fopen("/sys/devices/system/cpu/online", "r"); | |
31 | + if (!onlnf) | |
32 | + return default_cpu_map(); | |
33 | + | |
34 | + sep = 0; | |
35 | + prev = -1; | |
36 | + for (;;) { | |
37 | + n = fscanf(onlnf, "%u%c", &cpu, &sep); | |
38 | + if (n <= 0) | |
39 | + break; | |
40 | + if (prev >= 0) { | |
41 | + assert(nr_cpus + cpu - prev - 1 < MAX_NR_CPUS); | |
42 | + while (++prev < cpu) | |
43 | + cpumap[nr_cpus++] = prev; | |
44 | + } | |
45 | + assert (nr_cpus < MAX_NR_CPUS); | |
46 | + cpumap[nr_cpus++] = cpu; | |
47 | + if (n == 2 && sep == '-') | |
48 | + prev = cpu; | |
49 | + else | |
50 | + prev = -1; | |
51 | + if (n == 1 || sep == '\n') | |
52 | + break; | |
53 | + } | |
54 | + fclose(onlnf); | |
55 | + if (nr_cpus > 0) | |
56 | + return nr_cpus; | |
57 | + | |
58 | + return default_cpu_map(); | |
59 | +} |
tools/perf/util/cpumap.h