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>
2010-03-10 12:36:09 +03:00
# include "util.h"
# include "../perf.h"
# include "cpumap.h"
# include <assert.h>
# include <stdio.h>
int cpumap [ MAX_NR_CPUS ] ;
static int default_cpu_map ( void )
{
int nr_cpus , i ;
nr_cpus = sysconf ( _SC_NPROCESSORS_ONLN ) ;
assert ( nr_cpus < = MAX_NR_CPUS ) ;
assert ( ( int ) nr_cpus > = 0 ) ;
for ( i = 0 ; i < nr_cpus ; + + i )
cpumap [ i ] = i ;
return nr_cpus ;
}
2010-05-28 14:00:01 +04:00
static int read_all_cpu_map ( void )
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>
2010-03-10 12:36:09 +03:00
{
FILE * onlnf ;
int nr_cpus = 0 ;
int n , cpu , prev ;
char sep ;
onlnf = fopen ( " /sys/devices/system/cpu/online " , " r " ) ;
if ( ! onlnf )
return default_cpu_map ( ) ;
sep = 0 ;
prev = - 1 ;
for ( ; ; ) {
n = fscanf ( onlnf , " %u%c " , & cpu , & sep ) ;
if ( n < = 0 )
break ;
if ( prev > = 0 ) {
assert ( nr_cpus + cpu - prev - 1 < MAX_NR_CPUS ) ;
while ( + + prev < cpu )
cpumap [ nr_cpus + + ] = prev ;
}
assert ( nr_cpus < MAX_NR_CPUS ) ;
cpumap [ nr_cpus + + ] = cpu ;
if ( n = = 2 & & sep = = ' - ' )
prev = cpu ;
else
prev = - 1 ;
if ( n = = 1 | | sep = = ' \n ' )
break ;
}
fclose ( onlnf ) ;
if ( nr_cpus > 0 )
return nr_cpus ;
return default_cpu_map ( ) ;
}
2010-05-28 14:00:01 +04:00
int read_cpu_map ( const char * cpu_list )
{
unsigned long start_cpu , end_cpu = 0 ;
char * p = NULL ;
int i , nr_cpus = 0 ;
if ( ! cpu_list )
return read_all_cpu_map ( ) ;
if ( ! isdigit ( * cpu_list ) )
goto invalid ;
while ( isdigit ( * cpu_list ) ) {
p = NULL ;
start_cpu = strtoul ( cpu_list , & p , 0 ) ;
if ( start_cpu > = INT_MAX
| | ( * p ! = ' \0 ' & & * p ! = ' , ' & & * p ! = ' - ' ) )
goto invalid ;
if ( * p = = ' - ' ) {
cpu_list = + + p ;
p = NULL ;
end_cpu = strtoul ( cpu_list , & p , 0 ) ;
if ( end_cpu > = INT_MAX | | ( * p ! = ' \0 ' & & * p ! = ' , ' ) )
goto invalid ;
if ( end_cpu < start_cpu )
goto invalid ;
} else {
end_cpu = start_cpu ;
}
for ( ; start_cpu < = end_cpu ; start_cpu + + ) {
/* check for duplicates */
for ( i = 0 ; i < nr_cpus ; i + + )
if ( cpumap [ i ] = = ( int ) start_cpu )
goto invalid ;
assert ( nr_cpus < MAX_NR_CPUS ) ;
cpumap [ nr_cpus + + ] = ( int ) start_cpu ;
}
if ( * p )
+ + p ;
cpu_list = p ;
}
if ( nr_cpus > 0 )
return nr_cpus ;
return default_cpu_map ( ) ;
invalid :
return - 1 ;
}