2012-03-15 23:09:17 +04:00
# include <linux/list.h>
2015-06-10 10:25:07 +03:00
# include <linux/compiler.h>
2012-03-15 23:09:17 +04:00
# include <sys/types.h>
# include <unistd.h>
# include <stdio.h>
2014-07-31 10:00:49 +04:00
# include <stdbool.h>
2014-07-31 10:00:50 +04:00
# include <stdarg.h>
2012-03-15 23:09:17 +04:00
# include <dirent.h>
2013-12-09 20:14:24 +04:00
# include <api/fs/fs.h>
2013-11-12 20:58:49 +04:00
# include <locale.h>
2012-03-15 23:09:17 +04:00
# include "util.h"
# include "pmu.h"
# include "parse-events.h"
2012-09-10 11:53:50 +04:00
# include "cpumap.h"
2016-09-16 01:24:40 +03:00
# include "header.h"
# include "pmu-events/pmu-events.h"
2016-09-16 01:24:44 +03:00
# include "cache.h"
2012-03-15 23:09:17 +04:00
2013-01-19 00:05:09 +04:00
struct perf_pmu_format {
char * name ;
int value ;
DECLARE_BITMAP ( bits , PERF_PMU_FORMAT_BITS ) ;
struct list_head list ;
} ;
2012-08-16 23:10:24 +04:00
# define EVENT_SOURCE_DEVICE_PATH " / bus / event_source / devices / "
2012-03-15 23:09:17 +04:00
int perf_pmu_parse ( struct list_head * list , char * name ) ;
extern FILE * perf_pmu_in ;
static LIST_HEAD ( pmus ) ;
/*
* Parse & process all the sysfs attributes located under
* the directory specified in ' dir ' parameter .
*/
2012-11-10 04:46:50 +04:00
int perf_pmu__format_parse ( char * dir , struct list_head * head )
2012-03-15 23:09:17 +04:00
{
struct dirent * evt_ent ;
DIR * format_dir ;
int ret = 0 ;
format_dir = opendir ( dir ) ;
if ( ! format_dir )
return - EINVAL ;
while ( ! ret & & ( evt_ent = readdir ( format_dir ) ) ) {
char path [ PATH_MAX ] ;
char * name = evt_ent - > d_name ;
FILE * file ;
if ( ! strcmp ( name , " . " ) | | ! strcmp ( name , " .. " ) )
continue ;
snprintf ( path , PATH_MAX , " %s/%s " , dir , name ) ;
ret = - EINVAL ;
file = fopen ( path , " r " ) ;
if ( ! file )
break ;
perf_pmu_in = file ;
ret = perf_pmu_parse ( head , name ) ;
fclose ( file ) ;
}
closedir ( format_dir ) ;
return ret ;
}
/*
* Reading / parsing the default pmu format definition , which should be
* located at :
* / sys / bus / event_source / devices / < dev > / format as sysfs group attributes .
*/
2013-07-04 17:20:25 +04:00
static int pmu_format ( const char * name , struct list_head * format )
2012-03-15 23:09:17 +04:00
{
struct stat st ;
char path [ PATH_MAX ] ;
2013-11-05 21:48:50 +04:00
const char * sysfs = sysfs__mountpoint ( ) ;
2012-03-15 23:09:17 +04:00
if ( ! sysfs )
return - 1 ;
snprintf ( path , PATH_MAX ,
2012-08-16 23:10:24 +04:00
" %s " EVENT_SOURCE_DEVICE_PATH " %s/format " , sysfs , name ) ;
2012-03-15 23:09:17 +04:00
if ( stat ( path , & st ) < 0 )
2012-06-15 00:38:37 +04:00
return 0 ; /* no error if format does not exist */
2012-03-15 23:09:17 +04:00
2012-11-10 04:46:50 +04:00
if ( perf_pmu__format_parse ( path , format ) )
2012-03-15 23:09:17 +04:00
return - 1 ;
return 0 ;
}
2017-01-03 18:08:23 +03:00
static int convert_scale ( const char * scale , char * * end , double * sval )
2013-11-12 20:58:49 +04:00
{
2016-03-08 21:42:30 +03:00
char * lc ;
2017-01-03 18:08:23 +03:00
int ret = 0 ;
2015-05-31 09:06:23 +03:00
2013-11-12 20:58:49 +04:00
/*
* save current locale
*/
lc = setlocale ( LC_NUMERIC , NULL ) ;
perf tools: Fix locale handling in pmu parsing
Ingo reported regression on display format of big numbers, which is
missing separators (in default perf stat output).
triton:~/tip> perf stat -a sleep 1
...
127008602 cycles # 0.011 GHz
279538533 stalled-cycles-frontend # 220.09% frontend cycles idle
119213269 instructions # 0.94 insn per cycle
This is caused by recent change:
perf stat: Check existence of frontend/backed stalled cycles
that added call to pmu_have_event, that subsequently calls
perf_pmu__parse_scale, which has a bug in locale handling.
The lc string returned from setlocale, that we use to store old locale
value, may be allocated in static storage. Getting a dynamic copy to
make it survive another setlocale call.
$ perf stat ls
...
2,360,602 cycles # 3.080 GHz
2,703,090 instructions # 1.15 insn per cycle
546,031 branches # 712.511 M/sec
Committer note:
Since the patch introducing the regression didn't made to perf/core,
move it to just before where the regression was introduced, so that we
don't break bisection for this feature.
Reported-by: Ingo Molnar <mingo@redhat.com>
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20160303095348.GA24511@krava.redhat.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-03-03 12:53:48 +03:00
/*
* The lc string may be allocated in static storage ,
* so get a dynamic copy to make it survive setlocale
* call below .
*/
lc = strdup ( lc ) ;
if ( ! lc ) {
ret = - ENOMEM ;
2017-01-03 18:08:23 +03:00
goto out ;
perf tools: Fix locale handling in pmu parsing
Ingo reported regression on display format of big numbers, which is
missing separators (in default perf stat output).
triton:~/tip> perf stat -a sleep 1
...
127008602 cycles # 0.011 GHz
279538533 stalled-cycles-frontend # 220.09% frontend cycles idle
119213269 instructions # 0.94 insn per cycle
This is caused by recent change:
perf stat: Check existence of frontend/backed stalled cycles
that added call to pmu_have_event, that subsequently calls
perf_pmu__parse_scale, which has a bug in locale handling.
The lc string returned from setlocale, that we use to store old locale
value, may be allocated in static storage. Getting a dynamic copy to
make it survive another setlocale call.
$ perf stat ls
...
2,360,602 cycles # 3.080 GHz
2,703,090 instructions # 1.15 insn per cycle
546,031 branches # 712.511 M/sec
Committer note:
Since the patch introducing the regression didn't made to perf/core,
move it to just before where the regression was introduced, so that we
don't break bisection for this feature.
Reported-by: Ingo Molnar <mingo@redhat.com>
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20160303095348.GA24511@krava.redhat.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-03-03 12:53:48 +03:00
}
2013-11-12 20:58:49 +04:00
/*
* force to C locale to ensure kernel
* scale string is converted correctly .
* kernel uses default C locale .
*/
setlocale ( LC_NUMERIC , " C " ) ;
2017-01-03 18:08:23 +03:00
* sval = strtod ( scale , end ) ;
2013-11-12 20:58:49 +04:00
2017-01-03 18:08:23 +03:00
out :
2013-11-12 20:58:49 +04:00
/* restore locale */
setlocale ( LC_NUMERIC , lc ) ;
2016-03-08 21:42:30 +03:00
free ( lc ) ;
2017-01-03 18:08:23 +03:00
return ret ;
}
static int perf_pmu__parse_scale ( struct perf_pmu_alias * alias , char * dir , char * name )
{
struct stat st ;
ssize_t sret ;
char scale [ 128 ] ;
int fd , ret = - 1 ;
char path [ PATH_MAX ] ;
snprintf ( path , PATH_MAX , " %s/%s.scale " , dir , name ) ;
fd = open ( path , O_RDONLY ) ;
if ( fd = = - 1 )
return - 1 ;
if ( fstat ( fd , & st ) < 0 )
goto error ;
sret = read ( fd , scale , sizeof ( scale ) - 1 ) ;
if ( sret < 0 )
goto error ;
if ( scale [ sret - 1 ] = = ' \n ' )
scale [ sret - 1 ] = ' \0 ' ;
else
scale [ sret ] = ' \0 ' ;
perf tools: Fix locale handling in pmu parsing
Ingo reported regression on display format of big numbers, which is
missing separators (in default perf stat output).
triton:~/tip> perf stat -a sleep 1
...
127008602 cycles # 0.011 GHz
279538533 stalled-cycles-frontend # 220.09% frontend cycles idle
119213269 instructions # 0.94 insn per cycle
This is caused by recent change:
perf stat: Check existence of frontend/backed stalled cycles
that added call to pmu_have_event, that subsequently calls
perf_pmu__parse_scale, which has a bug in locale handling.
The lc string returned from setlocale, that we use to store old locale
value, may be allocated in static storage. Getting a dynamic copy to
make it survive another setlocale call.
$ perf stat ls
...
2,360,602 cycles # 3.080 GHz
2,703,090 instructions # 1.15 insn per cycle
546,031 branches # 712.511 M/sec
Committer note:
Since the patch introducing the regression didn't made to perf/core,
move it to just before where the regression was introduced, so that we
don't break bisection for this feature.
Reported-by: Ingo Molnar <mingo@redhat.com>
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20160303095348.GA24511@krava.redhat.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-03-03 12:53:48 +03:00
2017-01-03 18:08:23 +03:00
ret = convert_scale ( scale , NULL , & alias - > scale ) ;
2013-11-12 20:58:49 +04:00
error :
close ( fd ) ;
return ret ;
}
static int perf_pmu__parse_unit ( struct perf_pmu_alias * alias , char * dir , char * name )
{
char path [ PATH_MAX ] ;
ssize_t sret ;
int fd ;
snprintf ( path , PATH_MAX , " %s/%s.unit " , dir , name ) ;
fd = open ( path , O_RDONLY ) ;
if ( fd = = - 1 )
return - 1 ;
2015-12-14 18:44:40 +03:00
sret = read ( fd , alias - > unit , UNIT_MAX_LEN ) ;
2013-11-12 20:58:49 +04:00
if ( sret < 0 )
goto error ;
close ( fd ) ;
2015-05-31 09:06:23 +03:00
if ( alias - > unit [ sret - 1 ] = = ' \n ' )
alias - > unit [ sret - 1 ] = ' \0 ' ;
else
alias - > unit [ sret ] = ' \0 ' ;
2013-11-12 20:58:49 +04:00
return 0 ;
error :
close ( fd ) ;
alias - > unit [ 0 ] = ' \0 ' ;
return - 1 ;
}
2014-11-21 12:31:12 +03:00
static int
perf_pmu__parse_per_pkg ( struct perf_pmu_alias * alias , char * dir , char * name )
{
char path [ PATH_MAX ] ;
int fd ;
snprintf ( path , PATH_MAX , " %s/%s.per-pkg " , dir , name ) ;
fd = open ( path , O_RDONLY ) ;
if ( fd = = - 1 )
return - 1 ;
close ( fd ) ;
alias - > per_pkg = true ;
return 0 ;
}
2014-11-21 12:31:13 +03:00
static int perf_pmu__parse_snapshot ( struct perf_pmu_alias * alias ,
char * dir , char * name )
{
char path [ PATH_MAX ] ;
int fd ;
snprintf ( path , PATH_MAX , " %s/%s.snapshot " , dir , name ) ;
fd = open ( path , O_RDONLY ) ;
if ( fd = = - 1 )
return - 1 ;
alias - > snapshot = true ;
close ( fd ) ;
return 0 ;
}
2015-06-10 10:25:08 +03:00
static int __perf_pmu__new_alias ( struct list_head * list , char * dir , char * name ,
2017-01-28 05:03:37 +03:00
char * desc , char * val ,
char * long_desc , char * topic ,
char * unit , char * perpkg )
2012-06-15 10:31:41 +04:00
{
2013-01-18 23:54:00 +04:00
struct perf_pmu_alias * alias ;
2012-06-15 10:31:41 +04:00
int ret ;
2017-01-28 05:03:37 +03:00
int num ;
2012-06-15 10:31:41 +04:00
alias = malloc ( sizeof ( * alias ) ) ;
if ( ! alias )
return - ENOMEM ;
INIT_LIST_HEAD ( & alias - > terms ) ;
2013-11-12 20:58:49 +04:00
alias - > scale = 1.0 ;
alias - > unit [ 0 ] = ' \0 ' ;
2014-11-21 12:31:12 +03:00
alias - > per_pkg = false ;
2016-01-06 21:50:01 +03:00
alias - > snapshot = false ;
2013-11-12 20:58:49 +04:00
2015-06-10 10:25:08 +03:00
ret = parse_events_terms ( & alias - > terms , val ) ;
2012-06-15 10:31:41 +04:00
if ( ret ) {
2015-06-10 10:25:08 +03:00
pr_err ( " Cannot parse alias %s: %d \n " , val , ret ) ;
2012-06-15 10:31:41 +04:00
free ( alias ) ;
return ret ;
}
alias - > name = strdup ( name ) ;
2015-06-10 10:25:08 +03:00
if ( dir ) {
/*
* load unit name and scale if available
*/
perf_pmu__parse_unit ( alias , dir , name ) ;
perf_pmu__parse_scale ( alias , dir , name ) ;
perf_pmu__parse_per_pkg ( alias , dir , name ) ;
perf_pmu__parse_snapshot ( alias , dir , name ) ;
}
2013-11-12 20:58:49 +04:00
2016-09-16 01:24:43 +03:00
alias - > desc = desc ? strdup ( desc ) : NULL ;
2016-09-16 01:24:48 +03:00
alias - > long_desc = long_desc ? strdup ( long_desc ) :
desc ? strdup ( desc ) : NULL ;
2016-09-16 01:24:50 +03:00
alias - > topic = topic ? strdup ( topic ) : NULL ;
2017-01-28 05:03:37 +03:00
if ( unit ) {
if ( convert_scale ( unit , & unit , & alias - > scale ) < 0 )
return - 1 ;
snprintf ( alias - > unit , sizeof ( alias - > unit ) , " %s " , unit ) ;
}
alias - > per_pkg = perpkg & & sscanf ( perpkg , " %d " , & num ) = = 1 & & num = = 1 ;
2017-01-28 05:03:40 +03:00
alias - > str = strdup ( val ) ;
2012-06-15 10:31:41 +04:00
list_add_tail ( & alias - > list , list ) ;
2013-11-12 20:58:49 +04:00
2012-06-15 10:31:41 +04:00
return 0 ;
}
2015-06-10 10:25:08 +03:00
static int perf_pmu__new_alias ( struct list_head * list , char * dir , char * name , FILE * file )
{
char buf [ 256 ] ;
int ret ;
ret = fread ( buf , 1 , sizeof ( buf ) , file ) ;
if ( ret = = 0 )
return - EINVAL ;
buf [ ret ] = 0 ;
2017-01-28 05:03:37 +03:00
return __perf_pmu__new_alias ( list , dir , name , NULL , buf , NULL , NULL , NULL ,
NULL ) ;
2015-06-10 10:25:08 +03:00
}
2014-09-24 18:04:06 +04:00
static inline bool pmu_alias_info_file ( char * name )
{
size_t len ;
len = strlen ( name ) ;
if ( len > 5 & & ! strcmp ( name + len - 5 , " .unit " ) )
return true ;
if ( len > 6 & & ! strcmp ( name + len - 6 , " .scale " ) )
return true ;
2014-11-21 12:31:12 +03:00
if ( len > 8 & & ! strcmp ( name + len - 8 , " .per-pkg " ) )
return true ;
2014-11-21 12:31:13 +03:00
if ( len > 9 & & ! strcmp ( name + len - 9 , " .snapshot " ) )
return true ;
2014-09-24 18:04:06 +04:00
return false ;
}
2012-06-15 10:31:41 +04:00
/*
* Process all the sysfs attributes located under the directory
* specified in ' dir ' parameter .
*/
static int pmu_aliases_parse ( char * dir , struct list_head * head )
{
struct dirent * evt_ent ;
DIR * event_dir ;
event_dir = opendir ( dir ) ;
if ( ! event_dir )
return - EINVAL ;
2016-02-18 01:44:55 +03:00
while ( ( evt_ent = readdir ( event_dir ) ) ) {
2012-06-15 10:31:41 +04:00
char path [ PATH_MAX ] ;
char * name = evt_ent - > d_name ;
FILE * file ;
if ( ! strcmp ( name , " . " ) | | ! strcmp ( name , " .. " ) )
continue ;
2013-11-12 20:58:49 +04:00
/*
2014-09-24 18:04:06 +04:00
* skip info files parsed in perf_pmu__new_alias ( )
2013-11-12 20:58:49 +04:00
*/
2014-09-24 18:04:06 +04:00
if ( pmu_alias_info_file ( name ) )
2013-11-12 20:58:49 +04:00
continue ;
2012-06-15 10:31:41 +04:00
snprintf ( path , PATH_MAX , " %s/%s " , dir , name ) ;
file = fopen ( path , " r " ) ;
2016-02-18 01:44:55 +03:00
if ( ! file ) {
pr_debug ( " Cannot open %s \n " , path ) ;
continue ;
}
2013-11-12 20:58:49 +04:00
2016-02-18 01:44:55 +03:00
if ( perf_pmu__new_alias ( head , dir , name , file ) < 0 )
pr_debug ( " Cannot set up %s \n " , name ) ;
2012-06-15 10:31:41 +04:00
fclose ( file ) ;
}
closedir ( event_dir ) ;
2016-02-18 01:44:55 +03:00
return 0 ;
2012-06-15 10:31:41 +04:00
}
/*
* Reading the pmu event aliases definition , which should be located at :
* / sys / bus / event_source / devices / < dev > / events as sysfs group attributes .
*/
2013-07-04 17:20:25 +04:00
static int pmu_aliases ( const char * name , struct list_head * head )
2012-06-15 10:31:41 +04:00
{
struct stat st ;
char path [ PATH_MAX ] ;
2013-11-05 21:48:50 +04:00
const char * sysfs = sysfs__mountpoint ( ) ;
2012-06-15 10:31:41 +04:00
if ( ! sysfs )
return - 1 ;
snprintf ( path , PATH_MAX ,
" %s/bus/event_source/devices/%s/events " , sysfs , name ) ;
if ( stat ( path , & st ) < 0 )
2012-10-10 16:53:16 +04:00
return 0 ; /* no error if 'events' does not exist */
2012-06-15 10:31:41 +04:00
if ( pmu_aliases_parse ( path , head ) )
return - 1 ;
return 0 ;
}
2013-01-18 23:54:00 +04:00
static int pmu_alias_terms ( struct perf_pmu_alias * alias ,
2012-06-15 10:31:41 +04:00
struct list_head * terms )
{
2014-04-16 22:49:02 +04:00
struct parse_events_term * term , * cloned ;
2012-06-15 10:31:41 +04:00
LIST_HEAD ( list ) ;
int ret ;
list_for_each_entry ( term , & alias - > terms , list ) {
2014-04-16 22:49:02 +04:00
ret = parse_events_term__clone ( & cloned , term ) ;
2012-06-15 10:31:41 +04:00
if ( ret ) {
2016-02-12 22:48:00 +03:00
parse_events_terms__purge ( & list ) ;
2012-06-15 10:31:41 +04:00
return ret ;
}
2014-04-16 22:49:02 +04:00
list_add_tail ( & cloned - > list , & list ) ;
2012-06-15 10:31:41 +04:00
}
list_splice ( & list , terms ) ;
return 0 ;
}
2012-03-15 23:09:17 +04:00
/*
* Reading / parsing the default pmu type value , which should be
* located at :
* / sys / bus / event_source / devices / < dev > / type as sysfs attribute .
*/
2013-07-04 17:20:25 +04:00
static int pmu_type ( const char * name , __u32 * type )
2012-03-15 23:09:17 +04:00
{
struct stat st ;
char path [ PATH_MAX ] ;
FILE * file ;
int ret = 0 ;
2013-11-05 21:48:50 +04:00
const char * sysfs = sysfs__mountpoint ( ) ;
2012-03-15 23:09:17 +04:00
if ( ! sysfs )
return - 1 ;
snprintf ( path , PATH_MAX ,
2012-08-16 23:10:24 +04:00
" %s " EVENT_SOURCE_DEVICE_PATH " %s/type " , sysfs , name ) ;
2012-03-15 23:09:17 +04:00
if ( stat ( path , & st ) < 0 )
return - 1 ;
file = fopen ( path , " r " ) ;
if ( ! file )
return - EINVAL ;
if ( 1 ! = fscanf ( file , " %u " , type ) )
ret = - 1 ;
fclose ( file ) ;
return ret ;
}
2012-08-16 23:10:24 +04:00
/* Add all pmus in sysfs to pmu list: */
static void pmu_read_sysfs ( void )
{
char path [ PATH_MAX ] ;
DIR * dir ;
struct dirent * dent ;
2013-11-05 21:48:50 +04:00
const char * sysfs = sysfs__mountpoint ( ) ;
2012-08-16 23:10:24 +04:00
if ( ! sysfs )
return ;
snprintf ( path , PATH_MAX ,
" %s " EVENT_SOURCE_DEVICE_PATH , sysfs ) ;
dir = opendir ( path ) ;
if ( ! dir )
return ;
while ( ( dent = readdir ( dir ) ) ) {
if ( ! strcmp ( dent - > d_name , " . " ) | | ! strcmp ( dent - > d_name , " .. " ) )
continue ;
/* add to static LIST_HEAD(pmus): */
perf_pmu__find ( dent - > d_name ) ;
}
closedir ( dir ) ;
}
2013-07-04 17:20:25 +04:00
static struct cpu_map * pmu_cpumask ( const char * name )
2012-09-10 11:53:50 +04:00
{
struct stat st ;
char path [ PATH_MAX ] ;
FILE * file ;
struct cpu_map * cpus ;
2013-11-05 21:48:50 +04:00
const char * sysfs = sysfs__mountpoint ( ) ;
perf pmu: Support alternative sysfs cpumask
The perf tools can read a cpumask file for a PMU, describing a subset of
CPUs which that PMU covers. So far this has only been used to cater for
uncore PMUs, which in practice happen to only have a single CPU
described in the mask.
Until recently, the perf tools only correctly handled cpumask containing
a single CPU, and only when monitoring in system-wide mode. For example,
prior to commit 00e727bb389359c8 ("perf stat: Balance opening and
reading events"), a mask with more than a single CPU could cause perf
stat to hang. When a CPU PMU covers a subset of CPUs, but lacks a
cpumask, perf record will fail to open events (on the cores the PMU does
not support), and gives up.
For systems with heterogeneous CPUs such as ARM big.LITTLE systems, this
presents a problem. We have a PMU for each microarchitecture (e.g. a big
PMU and a little PMU), and would like to expose a cpumask for each (so
as to allow perf record and other tools to do the right thing). However,
doing so kernel-side will cause old perf binaries to not function (e.g.
hitting the issue solved by 00e727bb389359c8), and thus commits the
cardinal sin of breaking (existing) userspace.
To address this chicken-and-egg problem, this patch adds support got a
new file, cpus, which is largely identical to the existing cpumask file.
A kernel can expose this file, knowing that new perf binaries will
correctly support it, while old perf binaries will not look for it (and
thus will not be broken).
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Will Deacon <will.deacon@arm.com>
Link: http://lkml.kernel.org/r/1473330112-28528-8-git-send-email-mark.rutland@arm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-09-08 13:21:52 +03:00
const char * templates [ ] = {
" %s/bus/event_source/devices/%s/cpumask " ,
" %s/bus/event_source/devices/%s/cpus " ,
NULL
} ;
const char * * template ;
2012-09-10 11:53:50 +04:00
if ( ! sysfs )
return NULL ;
perf pmu: Support alternative sysfs cpumask
The perf tools can read a cpumask file for a PMU, describing a subset of
CPUs which that PMU covers. So far this has only been used to cater for
uncore PMUs, which in practice happen to only have a single CPU
described in the mask.
Until recently, the perf tools only correctly handled cpumask containing
a single CPU, and only when monitoring in system-wide mode. For example,
prior to commit 00e727bb389359c8 ("perf stat: Balance opening and
reading events"), a mask with more than a single CPU could cause perf
stat to hang. When a CPU PMU covers a subset of CPUs, but lacks a
cpumask, perf record will fail to open events (on the cores the PMU does
not support), and gives up.
For systems with heterogeneous CPUs such as ARM big.LITTLE systems, this
presents a problem. We have a PMU for each microarchitecture (e.g. a big
PMU and a little PMU), and would like to expose a cpumask for each (so
as to allow perf record and other tools to do the right thing). However,
doing so kernel-side will cause old perf binaries to not function (e.g.
hitting the issue solved by 00e727bb389359c8), and thus commits the
cardinal sin of breaking (existing) userspace.
To address this chicken-and-egg problem, this patch adds support got a
new file, cpus, which is largely identical to the existing cpumask file.
A kernel can expose this file, knowing that new perf binaries will
correctly support it, while old perf binaries will not look for it (and
thus will not be broken).
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Will Deacon <will.deacon@arm.com>
Link: http://lkml.kernel.org/r/1473330112-28528-8-git-send-email-mark.rutland@arm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-09-08 13:21:52 +03:00
for ( template = templates ; * template ; template + + ) {
snprintf ( path , PATH_MAX , * template , sysfs , name ) ;
if ( stat ( path , & st ) = = 0 )
break ;
}
2012-09-10 11:53:50 +04:00
perf pmu: Support alternative sysfs cpumask
The perf tools can read a cpumask file for a PMU, describing a subset of
CPUs which that PMU covers. So far this has only been used to cater for
uncore PMUs, which in practice happen to only have a single CPU
described in the mask.
Until recently, the perf tools only correctly handled cpumask containing
a single CPU, and only when monitoring in system-wide mode. For example,
prior to commit 00e727bb389359c8 ("perf stat: Balance opening and
reading events"), a mask with more than a single CPU could cause perf
stat to hang. When a CPU PMU covers a subset of CPUs, but lacks a
cpumask, perf record will fail to open events (on the cores the PMU does
not support), and gives up.
For systems with heterogeneous CPUs such as ARM big.LITTLE systems, this
presents a problem. We have a PMU for each microarchitecture (e.g. a big
PMU and a little PMU), and would like to expose a cpumask for each (so
as to allow perf record and other tools to do the right thing). However,
doing so kernel-side will cause old perf binaries to not function (e.g.
hitting the issue solved by 00e727bb389359c8), and thus commits the
cardinal sin of breaking (existing) userspace.
To address this chicken-and-egg problem, this patch adds support got a
new file, cpus, which is largely identical to the existing cpumask file.
A kernel can expose this file, knowing that new perf binaries will
correctly support it, while old perf binaries will not look for it (and
thus will not be broken).
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Will Deacon <will.deacon@arm.com>
Link: http://lkml.kernel.org/r/1473330112-28528-8-git-send-email-mark.rutland@arm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-09-08 13:21:52 +03:00
if ( ! * template )
2012-09-10 11:53:50 +04:00
return NULL ;
file = fopen ( path , " r " ) ;
if ( ! file )
return NULL ;
cpus = cpu_map__read ( file ) ;
fclose ( file ) ;
return cpus ;
}
2016-09-16 01:24:40 +03:00
/*
* Return the CPU id as a raw string .
*
* Each architecture should provide a more precise id string that
* can be use to match the architecture ' s " mapfile " .
*/
char * __weak get_cpuid_str ( void )
{
return NULL ;
}
/*
* From the pmu_events_map , find the table of PMU events that corresponds
* to the current running CPU . Then , add all PMU events from that table
* as aliases .
*/
2017-01-28 05:03:37 +03:00
static void pmu_add_cpu_aliases ( struct list_head * head , const char * name )
2016-09-16 01:24:40 +03:00
{
int i ;
struct pmu_events_map * map ;
struct pmu_event * pe ;
char * cpuid ;
2016-10-14 00:15:24 +03:00
static bool printed ;
2016-09-16 01:24:40 +03:00
2016-09-16 01:24:46 +03:00
cpuid = getenv ( " PERF_CPUID " ) ;
if ( cpuid )
cpuid = strdup ( cpuid ) ;
if ( ! cpuid )
cpuid = get_cpuid_str ( ) ;
2016-09-16 01:24:40 +03:00
if ( ! cpuid )
return ;
2016-10-14 00:15:24 +03:00
if ( ! printed ) {
pr_debug ( " Using CPUID %s \n " , cpuid ) ;
printed = true ;
}
2016-09-16 01:24:46 +03:00
2016-09-16 01:24:40 +03:00
i = 0 ;
while ( 1 ) {
map = & pmu_events_map [ i + + ] ;
if ( ! map - > table )
goto out ;
if ( ! strcmp ( map - > cpuid , cpuid ) )
break ;
}
/*
* Found a matching PMU events table . Create aliases
*/
i = 0 ;
while ( 1 ) {
2017-01-28 05:03:37 +03:00
const char * pname ;
2016-09-16 01:24:40 +03:00
pe = & map - > table [ i + + ] ;
if ( ! pe - > name )
break ;
2017-01-28 05:03:37 +03:00
pname = pe - > pmu ? pe - > pmu : " cpu " ;
if ( strncmp ( pname , name , strlen ( pname ) ) )
continue ;
2016-09-16 01:24:40 +03:00
/* need type casts to override 'const' */
__perf_pmu__new_alias ( head , NULL , ( char * ) pe - > name ,
2016-09-16 01:24:48 +03:00
( char * ) pe - > desc , ( char * ) pe - > event ,
2017-01-28 05:03:37 +03:00
( char * ) pe - > long_desc , ( char * ) pe - > topic ,
( char * ) pe - > unit , ( char * ) pe - > perpkg ) ;
2016-09-16 01:24:40 +03:00
}
out :
free ( cpuid ) ;
}
2015-06-10 10:25:07 +03:00
struct perf_event_attr * __weak
2014-07-31 10:00:49 +04:00
perf_pmu__get_default_config ( struct perf_pmu * pmu __maybe_unused )
{
return NULL ;
}
2013-07-04 17:20:25 +04:00
static struct perf_pmu * pmu_lookup ( const char * name )
2012-03-15 23:09:17 +04:00
{
struct perf_pmu * pmu ;
LIST_HEAD ( format ) ;
2012-06-15 10:31:41 +04:00
LIST_HEAD ( aliases ) ;
2012-03-15 23:09:17 +04:00
__u32 type ;
/*
* The pmu data we store & need consists of the pmu
* type value and format definitions . Load both right
* now .
*/
if ( pmu_format ( name , & format ) )
return NULL ;
2017-01-28 05:03:38 +03:00
/*
* Check the type first to avoid unnecessary work .
*/
if ( pmu_type ( name , & type ) )
2012-10-10 16:53:16 +04:00
return NULL ;
2017-01-28 05:03:38 +03:00
if ( pmu_aliases ( name , & aliases ) )
2012-03-15 23:09:17 +04:00
return NULL ;
2017-01-28 05:03:38 +03:00
pmu_add_cpu_aliases ( & aliases , name ) ;
2012-03-15 23:09:17 +04:00
pmu = zalloc ( sizeof ( * pmu ) ) ;
if ( ! pmu )
return NULL ;
2012-09-10 11:53:50 +04:00
pmu - > cpus = pmu_cpumask ( name ) ;
2012-03-15 23:09:17 +04:00
INIT_LIST_HEAD ( & pmu - > format ) ;
2012-06-15 10:31:41 +04:00
INIT_LIST_HEAD ( & pmu - > aliases ) ;
2012-03-15 23:09:17 +04:00
list_splice ( & format , & pmu - > format ) ;
2012-06-15 10:31:41 +04:00
list_splice ( & aliases , & pmu - > aliases ) ;
2012-03-15 23:09:17 +04:00
pmu - > name = strdup ( name ) ;
pmu - > type = type ;
2012-06-15 00:38:37 +04:00
list_add_tail ( & pmu - > list , & pmus ) ;
2014-07-31 10:00:49 +04:00
pmu - > default_config = perf_pmu__get_default_config ( pmu ) ;
2012-03-15 23:09:17 +04:00
return pmu ;
}
2013-07-04 17:20:25 +04:00
static struct perf_pmu * pmu_find ( const char * name )
2012-03-15 23:09:17 +04:00
{
struct perf_pmu * pmu ;
list_for_each_entry ( pmu , & pmus , list )
if ( ! strcmp ( pmu - > name , name ) )
return pmu ;
return NULL ;
}
2012-08-16 23:10:24 +04:00
struct perf_pmu * perf_pmu__scan ( struct perf_pmu * pmu )
{
/*
* pmu iterator : If pmu is NULL , we start at the begin ,
* otherwise return the next pmu . Returns NULL on end .
*/
if ( ! pmu ) {
pmu_read_sysfs ( ) ;
pmu = list_prepare_entry ( pmu , & pmus , list ) ;
}
list_for_each_entry_continue ( pmu , & pmus , list )
return pmu ;
return NULL ;
}
2013-07-04 17:20:25 +04:00
struct perf_pmu * perf_pmu__find ( const char * name )
2012-03-15 23:09:17 +04:00
{
struct perf_pmu * pmu ;
/*
* Once PMU is loaded it stays in the list ,
* so we keep us from multiple reading / parsing
* the pmu format definitions .
*/
pmu = pmu_find ( name ) ;
if ( pmu )
return pmu ;
return pmu_lookup ( name ) ;
}
2013-01-18 23:54:00 +04:00
static struct perf_pmu_format *
2015-07-17 19:33:49 +03:00
pmu_find_format ( struct list_head * formats , const char * name )
2012-03-15 23:09:17 +04:00
{
2013-01-18 23:54:00 +04:00
struct perf_pmu_format * format ;
2012-03-15 23:09:17 +04:00
list_for_each_entry ( format , formats , list )
if ( ! strcmp ( format - > name , name ) )
return format ;
return NULL ;
}
2015-07-17 19:33:49 +03:00
__u64 perf_pmu__format_bits ( struct list_head * formats , const char * name )
{
struct perf_pmu_format * format = pmu_find_format ( formats , name ) ;
__u64 bits = 0 ;
int fbit ;
if ( ! format )
return 0 ;
for_each_set_bit ( fbit , format - > bits , PERF_PMU_FORMAT_BITS )
bits | = 1ULL < < fbit ;
return bits ;
}
2012-03-15 23:09:17 +04:00
/*
2014-07-31 10:00:49 +04:00
* Sets value based on the format definition ( format parameter )
2012-03-15 23:09:17 +04:00
* and unformated value ( value parameter ) .
*/
2014-07-31 10:00:49 +04:00
static void pmu_format_value ( unsigned long * format , __u64 value , __u64 * v ,
bool zero )
2012-03-15 23:09:17 +04:00
{
unsigned long fbit , vbit ;
for ( fbit = 0 , vbit = 0 ; fbit < PERF_PMU_FORMAT_BITS ; fbit + + ) {
if ( ! test_bit ( fbit , format ) )
continue ;
2014-07-31 10:00:49 +04:00
if ( value & ( 1llu < < vbit + + ) )
* v | = ( 1llu < < fbit ) ;
else if ( zero )
* v & = ~ ( 1llu < < fbit ) ;
2012-03-15 23:09:17 +04:00
}
}
2015-07-17 19:33:50 +03:00
static __u64 pmu_format_max_value ( const unsigned long * format )
{
2016-03-30 22:16:15 +03:00
__u64 w = 0 ;
int fbit ;
2015-07-17 19:33:50 +03:00
2016-03-30 22:16:15 +03:00
for_each_set_bit ( fbit , format , PERF_PMU_FORMAT_BITS )
w | = ( 1ULL < < fbit ) ;
return w ;
2015-07-17 19:33:50 +03:00
}
2015-01-08 04:13:50 +03:00
/*
* Term is a string term , and might be a param - term . Try to look up it ' s value
* in the remaining terms .
* - We have a term like " base-or-format-term=param-term " ,
* - We need to find the value supplied for " param-term " ( with param - term named
* in a config string ) later on in the term list .
*/
static int pmu_resolve_param_term ( struct parse_events_term * term ,
struct list_head * head_terms ,
__u64 * value )
{
struct parse_events_term * t ;
list_for_each_entry ( t , head_terms , list ) {
if ( t - > type_val = = PARSE_EVENTS__TERM_TYPE_NUM ) {
if ( ! strcmp ( t - > config , term - > config ) ) {
t - > used = true ;
* value = t - > val . num ;
return 0 ;
}
}
}
if ( verbose )
printf ( " Required parameter '%s' not specified \n " , term - > config ) ;
return - 1 ;
}
perf tools: Show proper error message for wrong terms of hw/sw events
Show proper error message and show valid terms when wrong config terms
is specified for hw/sw type perf events.
This patch makes the original error format function formats_error_string()
more generic, which only outputs the static config terms for hw/sw perf
events, and prepends pmu formats for pmu events.
Before this patch:
$ perf record -e 'cpu-clock/freqx=200/' -a sleep 1
invalid or unsupported event: 'cpu-clock/freqx=200/'
Run 'perf list' for a list of valid events
usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]
-e, --event <event> event selector. use 'perf list' to list available events
After this patch:
$ perf record -e 'cpu-clock/freqx=200/' -a sleep 1
event syntax error: 'cpu-clock/freqx=200/'
\___ unknown term
valid terms: config,config1,config2,name,period,freq,branch_type,time,call-graph,stack-size
Run 'perf list' for a list of valid events
usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]
-e, --event <event> event selector. use 'perf list' to list available events
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Kan Liang <kan.liang@intel.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1443412336-120050-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-09-28 06:52:14 +03:00
static char * pmu_formats_string ( struct list_head * formats )
perf tools: Add term support for parse_events_error
Allowing event's term processing to report back error, like:
$ perf record -e 'cpu/even=0x1/' ls
event syntax error: 'cpu/even=0x1/'
\___ unknown term
valid terms: pc,any,inv,edge,cmask,event,in_tx,ldlat,umask,in_tx_cp,offcore_rsp,config,config1,config2,name,period,branch_type
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1429729824-13932-7-git-send-email-jolsa@kernel.org
[ Renamed 'error' variables to 'err', not to clash with util.h error() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-04-22 22:10:21 +03:00
{
struct perf_pmu_format * format ;
2016-05-10 08:47:44 +03:00
char * str = NULL ;
struct strbuf buf = STRBUF_INIT ;
perf tools: Add term support for parse_events_error
Allowing event's term processing to report back error, like:
$ perf record -e 'cpu/even=0x1/' ls
event syntax error: 'cpu/even=0x1/'
\___ unknown term
valid terms: pc,any,inv,edge,cmask,event,in_tx,ldlat,umask,in_tx_cp,offcore_rsp,config,config1,config2,name,period,branch_type
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1429729824-13932-7-git-send-email-jolsa@kernel.org
[ Renamed 'error' variables to 'err', not to clash with util.h error() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-04-22 22:10:21 +03:00
unsigned i = 0 ;
perf tools: Show proper error message for wrong terms of hw/sw events
Show proper error message and show valid terms when wrong config terms
is specified for hw/sw type perf events.
This patch makes the original error format function formats_error_string()
more generic, which only outputs the static config terms for hw/sw perf
events, and prepends pmu formats for pmu events.
Before this patch:
$ perf record -e 'cpu-clock/freqx=200/' -a sleep 1
invalid or unsupported event: 'cpu-clock/freqx=200/'
Run 'perf list' for a list of valid events
usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]
-e, --event <event> event selector. use 'perf list' to list available events
After this patch:
$ perf record -e 'cpu-clock/freqx=200/' -a sleep 1
event syntax error: 'cpu-clock/freqx=200/'
\___ unknown term
valid terms: config,config1,config2,name,period,freq,branch_type,time,call-graph,stack-size
Run 'perf list' for a list of valid events
usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]
-e, --event <event> event selector. use 'perf list' to list available events
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Kan Liang <kan.liang@intel.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1443412336-120050-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-09-28 06:52:14 +03:00
if ( ! formats )
perf tools: Add term support for parse_events_error
Allowing event's term processing to report back error, like:
$ perf record -e 'cpu/even=0x1/' ls
event syntax error: 'cpu/even=0x1/'
\___ unknown term
valid terms: pc,any,inv,edge,cmask,event,in_tx,ldlat,umask,in_tx_cp,offcore_rsp,config,config1,config2,name,period,branch_type
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1429729824-13932-7-git-send-email-jolsa@kernel.org
[ Renamed 'error' variables to 'err', not to clash with util.h error() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-04-22 22:10:21 +03:00
return NULL ;
/* sysfs exported terms */
perf tools: Show proper error message for wrong terms of hw/sw events
Show proper error message and show valid terms when wrong config terms
is specified for hw/sw type perf events.
This patch makes the original error format function formats_error_string()
more generic, which only outputs the static config terms for hw/sw perf
events, and prepends pmu formats for pmu events.
Before this patch:
$ perf record -e 'cpu-clock/freqx=200/' -a sleep 1
invalid or unsupported event: 'cpu-clock/freqx=200/'
Run 'perf list' for a list of valid events
usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]
-e, --event <event> event selector. use 'perf list' to list available events
After this patch:
$ perf record -e 'cpu-clock/freqx=200/' -a sleep 1
event syntax error: 'cpu-clock/freqx=200/'
\___ unknown term
valid terms: config,config1,config2,name,period,freq,branch_type,time,call-graph,stack-size
Run 'perf list' for a list of valid events
usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]
-e, --event <event> event selector. use 'perf list' to list available events
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Kan Liang <kan.liang@intel.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1443412336-120050-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-09-28 06:52:14 +03:00
list_for_each_entry ( format , formats , list )
2016-05-10 08:47:44 +03:00
if ( strbuf_addf ( & buf , i + + ? " ,%s " : " %s " , format - > name ) < 0 )
goto error ;
perf tools: Add term support for parse_events_error
Allowing event's term processing to report back error, like:
$ perf record -e 'cpu/even=0x1/' ls
event syntax error: 'cpu/even=0x1/'
\___ unknown term
valid terms: pc,any,inv,edge,cmask,event,in_tx,ldlat,umask,in_tx_cp,offcore_rsp,config,config1,config2,name,period,branch_type
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1429729824-13932-7-git-send-email-jolsa@kernel.org
[ Renamed 'error' variables to 'err', not to clash with util.h error() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-04-22 22:10:21 +03:00
perf tools: Show proper error message for wrong terms of hw/sw events
Show proper error message and show valid terms when wrong config terms
is specified for hw/sw type perf events.
This patch makes the original error format function formats_error_string()
more generic, which only outputs the static config terms for hw/sw perf
events, and prepends pmu formats for pmu events.
Before this patch:
$ perf record -e 'cpu-clock/freqx=200/' -a sleep 1
invalid or unsupported event: 'cpu-clock/freqx=200/'
Run 'perf list' for a list of valid events
usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]
-e, --event <event> event selector. use 'perf list' to list available events
After this patch:
$ perf record -e 'cpu-clock/freqx=200/' -a sleep 1
event syntax error: 'cpu-clock/freqx=200/'
\___ unknown term
valid terms: config,config1,config2,name,period,freq,branch_type,time,call-graph,stack-size
Run 'perf list' for a list of valid events
usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]
-e, --event <event> event selector. use 'perf list' to list available events
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Kan Liang <kan.liang@intel.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1443412336-120050-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-09-28 06:52:14 +03:00
str = strbuf_detach ( & buf , NULL ) ;
2016-05-10 08:47:44 +03:00
error :
perf tools: Show proper error message for wrong terms of hw/sw events
Show proper error message and show valid terms when wrong config terms
is specified for hw/sw type perf events.
This patch makes the original error format function formats_error_string()
more generic, which only outputs the static config terms for hw/sw perf
events, and prepends pmu formats for pmu events.
Before this patch:
$ perf record -e 'cpu-clock/freqx=200/' -a sleep 1
invalid or unsupported event: 'cpu-clock/freqx=200/'
Run 'perf list' for a list of valid events
usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]
-e, --event <event> event selector. use 'perf list' to list available events
After this patch:
$ perf record -e 'cpu-clock/freqx=200/' -a sleep 1
event syntax error: 'cpu-clock/freqx=200/'
\___ unknown term
valid terms: config,config1,config2,name,period,freq,branch_type,time,call-graph,stack-size
Run 'perf list' for a list of valid events
usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]
-e, --event <event> event selector. use 'perf list' to list available events
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Kan Liang <kan.liang@intel.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1443412336-120050-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-09-28 06:52:14 +03:00
strbuf_release ( & buf ) ;
perf tools: Add term support for parse_events_error
Allowing event's term processing to report back error, like:
$ perf record -e 'cpu/even=0x1/' ls
event syntax error: 'cpu/even=0x1/'
\___ unknown term
valid terms: pc,any,inv,edge,cmask,event,in_tx,ldlat,umask,in_tx_cp,offcore_rsp,config,config1,config2,name,period,branch_type
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1429729824-13932-7-git-send-email-jolsa@kernel.org
[ Renamed 'error' variables to 'err', not to clash with util.h error() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-04-22 22:10:21 +03:00
return str ;
}
2012-03-15 23:09:17 +04:00
/*
* Setup one of config [ 12 ] attr members based on the
2014-01-08 20:43:51 +04:00
* user input data - term parameter .
2012-03-15 23:09:17 +04:00
*/
static int pmu_config_term ( struct list_head * formats ,
struct perf_event_attr * attr ,
2014-07-31 10:00:49 +04:00
struct parse_events_term * term ,
2015-01-08 04:13:50 +03:00
struct list_head * head_terms ,
perf tools: Add term support for parse_events_error
Allowing event's term processing to report back error, like:
$ perf record -e 'cpu/even=0x1/' ls
event syntax error: 'cpu/even=0x1/'
\___ unknown term
valid terms: pc,any,inv,edge,cmask,event,in_tx,ldlat,umask,in_tx_cp,offcore_rsp,config,config1,config2,name,period,branch_type
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1429729824-13932-7-git-send-email-jolsa@kernel.org
[ Renamed 'error' variables to 'err', not to clash with util.h error() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-04-22 22:10:21 +03:00
bool zero , struct parse_events_error * err )
2012-03-15 23:09:17 +04:00
{
2013-01-18 23:54:00 +04:00
struct perf_pmu_format * format ;
2012-03-15 23:09:17 +04:00
__u64 * vp ;
2015-07-17 19:33:50 +03:00
__u64 val , max_val ;
2015-01-08 04:13:50 +03:00
/*
* If this is a parameter we ' ve already used for parameterized - eval ,
* skip it in normal eval .
*/
if ( term - > used )
return 0 ;
2012-03-15 23:09:17 +04:00
/*
* Hardcoded terms should be already in , so nothing
* to be done for them .
*/
if ( parse_events__is_hardcoded_term ( term ) )
return 0 ;
format = pmu_find_format ( formats , term - > config ) ;
2015-01-08 04:13:50 +03:00
if ( ! format ) {
if ( verbose )
printf ( " Invalid event/parameter '%s' \n " , term - > config ) ;
perf tools: Add term support for parse_events_error
Allowing event's term processing to report back error, like:
$ perf record -e 'cpu/even=0x1/' ls
event syntax error: 'cpu/even=0x1/'
\___ unknown term
valid terms: pc,any,inv,edge,cmask,event,in_tx,ldlat,umask,in_tx_cp,offcore_rsp,config,config1,config2,name,period,branch_type
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1429729824-13932-7-git-send-email-jolsa@kernel.org
[ Renamed 'error' variables to 'err', not to clash with util.h error() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-04-22 22:10:21 +03:00
if ( err ) {
perf tools: Show proper error message for wrong terms of hw/sw events
Show proper error message and show valid terms when wrong config terms
is specified for hw/sw type perf events.
This patch makes the original error format function formats_error_string()
more generic, which only outputs the static config terms for hw/sw perf
events, and prepends pmu formats for pmu events.
Before this patch:
$ perf record -e 'cpu-clock/freqx=200/' -a sleep 1
invalid or unsupported event: 'cpu-clock/freqx=200/'
Run 'perf list' for a list of valid events
usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]
-e, --event <event> event selector. use 'perf list' to list available events
After this patch:
$ perf record -e 'cpu-clock/freqx=200/' -a sleep 1
event syntax error: 'cpu-clock/freqx=200/'
\___ unknown term
valid terms: config,config1,config2,name,period,freq,branch_type,time,call-graph,stack-size
Run 'perf list' for a list of valid events
usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]
-e, --event <event> event selector. use 'perf list' to list available events
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Kan Liang <kan.liang@intel.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1443412336-120050-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-09-28 06:52:14 +03:00
char * pmu_term = pmu_formats_string ( formats ) ;
perf tools: Add term support for parse_events_error
Allowing event's term processing to report back error, like:
$ perf record -e 'cpu/even=0x1/' ls
event syntax error: 'cpu/even=0x1/'
\___ unknown term
valid terms: pc,any,inv,edge,cmask,event,in_tx,ldlat,umask,in_tx_cp,offcore_rsp,config,config1,config2,name,period,branch_type
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1429729824-13932-7-git-send-email-jolsa@kernel.org
[ Renamed 'error' variables to 'err', not to clash with util.h error() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-04-22 22:10:21 +03:00
err - > idx = term - > err_term ;
err - > str = strdup ( " unknown term " ) ;
perf tools: Show proper error message for wrong terms of hw/sw events
Show proper error message and show valid terms when wrong config terms
is specified for hw/sw type perf events.
This patch makes the original error format function formats_error_string()
more generic, which only outputs the static config terms for hw/sw perf
events, and prepends pmu formats for pmu events.
Before this patch:
$ perf record -e 'cpu-clock/freqx=200/' -a sleep 1
invalid or unsupported event: 'cpu-clock/freqx=200/'
Run 'perf list' for a list of valid events
usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]
-e, --event <event> event selector. use 'perf list' to list available events
After this patch:
$ perf record -e 'cpu-clock/freqx=200/' -a sleep 1
event syntax error: 'cpu-clock/freqx=200/'
\___ unknown term
valid terms: config,config1,config2,name,period,freq,branch_type,time,call-graph,stack-size
Run 'perf list' for a list of valid events
usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]
-e, --event <event> event selector. use 'perf list' to list available events
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Kan Liang <kan.liang@intel.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1443412336-120050-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-09-28 06:52:14 +03:00
err - > help = parse_events_formats_error_string ( pmu_term ) ;
free ( pmu_term ) ;
perf tools: Add term support for parse_events_error
Allowing event's term processing to report back error, like:
$ perf record -e 'cpu/even=0x1/' ls
event syntax error: 'cpu/even=0x1/'
\___ unknown term
valid terms: pc,any,inv,edge,cmask,event,in_tx,ldlat,umask,in_tx_cp,offcore_rsp,config,config1,config2,name,period,branch_type
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1429729824-13932-7-git-send-email-jolsa@kernel.org
[ Renamed 'error' variables to 'err', not to clash with util.h error() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-04-22 22:10:21 +03:00
}
2012-03-15 23:09:17 +04:00
return - EINVAL ;
2015-01-08 04:13:50 +03:00
}
2012-03-15 23:09:17 +04:00
switch ( format - > value ) {
case PERF_PMU_FORMAT_VALUE_CONFIG :
vp = & attr - > config ;
break ;
case PERF_PMU_FORMAT_VALUE_CONFIG1 :
vp = & attr - > config1 ;
break ;
case PERF_PMU_FORMAT_VALUE_CONFIG2 :
vp = & attr - > config2 ;
break ;
default :
return - EINVAL ;
}
2012-04-25 20:24:57 +04:00
/*
2015-01-08 04:13:50 +03:00
* Either directly use a numeric term , or try to translate string terms
* using event parameters .
2012-04-25 20:24:57 +04:00
*/
2015-01-08 04:13:50 +03:00
if ( term - > type_val = = PARSE_EVENTS__TERM_TYPE_NUM )
val = term - > val . num ;
else if ( term - > type_val = = PARSE_EVENTS__TERM_TYPE_STR ) {
if ( strcmp ( term - > val . str , " ? " ) ) {
perf tools: Add term support for parse_events_error
Allowing event's term processing to report back error, like:
$ perf record -e 'cpu/even=0x1/' ls
event syntax error: 'cpu/even=0x1/'
\___ unknown term
valid terms: pc,any,inv,edge,cmask,event,in_tx,ldlat,umask,in_tx_cp,offcore_rsp,config,config1,config2,name,period,branch_type
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1429729824-13932-7-git-send-email-jolsa@kernel.org
[ Renamed 'error' variables to 'err', not to clash with util.h error() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-04-22 22:10:21 +03:00
if ( verbose ) {
2015-01-08 04:13:50 +03:00
pr_info ( " Invalid sysfs entry %s=%s \n " ,
term - > config , term - > val . str ) ;
perf tools: Add term support for parse_events_error
Allowing event's term processing to report back error, like:
$ perf record -e 'cpu/even=0x1/' ls
event syntax error: 'cpu/even=0x1/'
\___ unknown term
valid terms: pc,any,inv,edge,cmask,event,in_tx,ldlat,umask,in_tx_cp,offcore_rsp,config,config1,config2,name,period,branch_type
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1429729824-13932-7-git-send-email-jolsa@kernel.org
[ Renamed 'error' variables to 'err', not to clash with util.h error() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-04-22 22:10:21 +03:00
}
if ( err ) {
err - > idx = term - > err_val ;
err - > str = strdup ( " expected numeric value " ) ;
}
2015-01-08 04:13:50 +03:00
return - EINVAL ;
}
if ( pmu_resolve_param_term ( term , head_terms , & val ) )
return - EINVAL ;
} else
return - EINVAL ;
2015-07-17 19:33:50 +03:00
max_val = pmu_format_max_value ( format - > bits ) ;
if ( val > max_val ) {
if ( err ) {
err - > idx = term - > err_val ;
if ( asprintf ( & err - > str ,
" value too big for format, maximum is %llu " ,
( unsigned long long ) max_val ) < 0 )
err - > str = strdup ( " value too big for format " ) ;
return - EINVAL ;
}
/*
* Assume we don ' t care if ! err , in which case the value will be
* silently truncated .
*/
}
2015-01-08 04:13:50 +03:00
pmu_format_value ( format - > bits , val , vp , zero ) ;
2012-03-15 23:09:17 +04:00
return 0 ;
}
2012-11-10 04:46:50 +04:00
int perf_pmu__config_terms ( struct list_head * formats ,
struct perf_event_attr * attr ,
2014-07-31 10:00:49 +04:00
struct list_head * head_terms ,
perf tools: Add term support for parse_events_error
Allowing event's term processing to report back error, like:
$ perf record -e 'cpu/even=0x1/' ls
event syntax error: 'cpu/even=0x1/'
\___ unknown term
valid terms: pc,any,inv,edge,cmask,event,in_tx,ldlat,umask,in_tx_cp,offcore_rsp,config,config1,config2,name,period,branch_type
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1429729824-13932-7-git-send-email-jolsa@kernel.org
[ Renamed 'error' variables to 'err', not to clash with util.h error() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-04-22 22:10:21 +03:00
bool zero , struct parse_events_error * err )
2012-03-15 23:09:17 +04:00
{
2013-01-18 23:29:49 +04:00
struct parse_events_term * term ;
2012-03-15 23:09:17 +04:00
2015-01-08 04:13:50 +03:00
list_for_each_entry ( term , head_terms , list ) {
perf tools: Add term support for parse_events_error
Allowing event's term processing to report back error, like:
$ perf record -e 'cpu/even=0x1/' ls
event syntax error: 'cpu/even=0x1/'
\___ unknown term
valid terms: pc,any,inv,edge,cmask,event,in_tx,ldlat,umask,in_tx_cp,offcore_rsp,config,config1,config2,name,period,branch_type
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1429729824-13932-7-git-send-email-jolsa@kernel.org
[ Renamed 'error' variables to 'err', not to clash with util.h error() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-04-22 22:10:21 +03:00
if ( pmu_config_term ( formats , attr , term , head_terms ,
zero , err ) )
2012-03-15 23:09:17 +04:00
return - EINVAL ;
2015-01-08 04:13:50 +03:00
}
2012-03-15 23:09:17 +04:00
return 0 ;
}
/*
* Configures event ' s ' attr ' parameter based on the :
* 1 ) users input - specified in terms parameter
* 2 ) pmu format definitions - specified by pmu parameter
*/
int perf_pmu__config ( struct perf_pmu * pmu , struct perf_event_attr * attr ,
perf tools: Add term support for parse_events_error
Allowing event's term processing to report back error, like:
$ perf record -e 'cpu/even=0x1/' ls
event syntax error: 'cpu/even=0x1/'
\___ unknown term
valid terms: pc,any,inv,edge,cmask,event,in_tx,ldlat,umask,in_tx_cp,offcore_rsp,config,config1,config2,name,period,branch_type
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1429729824-13932-7-git-send-email-jolsa@kernel.org
[ Renamed 'error' variables to 'err', not to clash with util.h error() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-04-22 22:10:21 +03:00
struct list_head * head_terms ,
struct parse_events_error * err )
2012-03-15 23:09:17 +04:00
{
2014-07-31 10:00:49 +04:00
bool zero = ! ! pmu - > default_config ;
2012-03-15 23:09:17 +04:00
attr - > type = pmu - > type ;
perf tools: Add term support for parse_events_error
Allowing event's term processing to report back error, like:
$ perf record -e 'cpu/even=0x1/' ls
event syntax error: 'cpu/even=0x1/'
\___ unknown term
valid terms: pc,any,inv,edge,cmask,event,in_tx,ldlat,umask,in_tx_cp,offcore_rsp,config,config1,config2,name,period,branch_type
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1429729824-13932-7-git-send-email-jolsa@kernel.org
[ Renamed 'error' variables to 'err', not to clash with util.h error() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-04-22 22:10:21 +03:00
return perf_pmu__config_terms ( & pmu - > format , attr , head_terms ,
zero , err ) ;
2012-03-15 23:09:17 +04:00
}
2013-01-18 23:54:00 +04:00
static struct perf_pmu_alias * pmu_find_alias ( struct perf_pmu * pmu ,
struct parse_events_term * term )
2012-06-15 10:31:41 +04:00
{
2013-01-18 23:54:00 +04:00
struct perf_pmu_alias * alias ;
2012-06-15 10:31:41 +04:00
char * name ;
if ( parse_events__is_hardcoded_term ( term ) )
return NULL ;
if ( term - > type_val = = PARSE_EVENTS__TERM_TYPE_NUM ) {
if ( term - > val . num ! = 1 )
return NULL ;
if ( pmu_find_format ( & pmu - > format , term - > config ) )
return NULL ;
name = term - > config ;
} else if ( term - > type_val = = PARSE_EVENTS__TERM_TYPE_STR ) {
if ( strcasecmp ( term - > config , " event " ) )
return NULL ;
name = term - > val . str ;
} else {
return NULL ;
}
list_for_each_entry ( alias , & pmu - > aliases , list ) {
if ( ! strcasecmp ( alias - > name , name ) )
return alias ;
}
return NULL ;
}
2013-11-12 20:58:49 +04:00
2014-11-21 12:31:13 +03:00
static int check_info_data ( struct perf_pmu_alias * alias ,
struct perf_pmu_info * info )
2013-11-12 20:58:49 +04:00
{
/*
* Only one term in event definition can
2014-11-21 12:31:13 +03:00
* define unit , scale and snapshot , fail
* if there ' s more than one .
2013-11-12 20:58:49 +04:00
*/
2014-11-21 12:31:13 +03:00
if ( ( info - > unit & & alias - > unit ) | |
( info - > scale & & alias - > scale ) | |
( info - > snapshot & & alias - > snapshot ) )
2013-11-12 20:58:49 +04:00
return - EINVAL ;
if ( alias - > unit )
2014-11-21 12:31:13 +03:00
info - > unit = alias - > unit ;
2013-11-12 20:58:49 +04:00
if ( alias - > scale )
2014-11-21 12:31:13 +03:00
info - > scale = alias - > scale ;
if ( alias - > snapshot )
info - > snapshot = alias - > snapshot ;
2013-11-12 20:58:49 +04:00
return 0 ;
}
2012-06-15 10:31:41 +04:00
/*
* Find alias in the terms list and replace it with the terms
* defined for the alias
*/
2013-11-12 20:58:49 +04:00
int perf_pmu__check_alias ( struct perf_pmu * pmu , struct list_head * head_terms ,
2014-09-24 18:04:06 +04:00
struct perf_pmu_info * info )
2012-06-15 10:31:41 +04:00
{
2013-01-18 23:29:49 +04:00
struct parse_events_term * term , * h ;
2013-01-18 23:54:00 +04:00
struct perf_pmu_alias * alias ;
2012-06-15 10:31:41 +04:00
int ret ;
2014-11-21 12:31:12 +03:00
info - > per_pkg = false ;
2014-01-17 19:34:05 +04:00
/*
* Mark unit and scale as not set
* ( different from default values , see below )
*/
2014-11-21 12:31:13 +03:00
info - > unit = NULL ;
info - > scale = 0.0 ;
info - > snapshot = false ;
2013-11-12 20:58:49 +04:00
2012-06-15 10:31:41 +04:00
list_for_each_entry_safe ( term , h , head_terms , list ) {
alias = pmu_find_alias ( pmu , term ) ;
if ( ! alias )
continue ;
ret = pmu_alias_terms ( alias , & term - > list ) ;
if ( ret )
return ret ;
2013-11-12 20:58:49 +04:00
2014-11-21 12:31:13 +03:00
ret = check_info_data ( alias , info ) ;
2013-11-12 20:58:49 +04:00
if ( ret )
return ret ;
2014-11-21 12:31:12 +03:00
if ( alias - > per_pkg )
info - > per_pkg = true ;
2012-06-15 10:31:41 +04:00
list_del ( & term - > list ) ;
free ( term ) ;
}
2014-01-17 19:34:05 +04:00
/*
* if no unit or scale foundin aliases , then
* set defaults as for evsel
* unit cannot left to NULL
*/
2014-09-24 18:04:06 +04:00
if ( info - > unit = = NULL )
info - > unit = " " ;
2014-01-17 19:34:05 +04:00
2014-09-24 18:04:06 +04:00
if ( info - > scale = = 0.0 )
info - > scale = 1.0 ;
2014-01-17 19:34:05 +04:00
2012-06-15 10:31:41 +04:00
return 0 ;
}
2012-03-15 23:09:17 +04:00
int perf_pmu__new_format ( struct list_head * list , char * name ,
int config , unsigned long * bits )
{
2013-01-18 23:54:00 +04:00
struct perf_pmu_format * format ;
2012-03-15 23:09:17 +04:00
format = zalloc ( sizeof ( * format ) ) ;
if ( ! format )
return - ENOMEM ;
format - > name = strdup ( name ) ;
format - > value = config ;
memcpy ( format - > bits , bits , sizeof ( format - > bits ) ) ;
list_add_tail ( & format - > list , list ) ;
return 0 ;
}
void perf_pmu__set_format ( unsigned long * bits , long from , long to )
{
long b ;
if ( ! to )
to = from ;
2013-01-17 21:11:30 +04:00
memset ( bits , 0 , BITS_TO_BYTES ( PERF_PMU_FORMAT_BITS ) ) ;
2012-03-15 23:09:17 +04:00
for ( b = from ; b < = to ; b + + )
set_bit ( b , bits ) ;
}
2013-04-20 22:02:29 +04:00
2015-01-08 04:13:51 +03:00
static int sub_non_neg ( int a , int b )
{
if ( b > a )
return 0 ;
return a - b ;
}
2013-04-20 22:02:29 +04:00
static char * format_alias ( char * buf , int len , struct perf_pmu * pmu ,
struct perf_pmu_alias * alias )
{
2015-01-08 04:13:51 +03:00
struct parse_events_term * term ;
int used = snprintf ( buf , len , " %s/%s " , pmu - > name , alias - > name ) ;
list_for_each_entry ( term , & alias - > terms , list ) {
if ( term - > type_val = = PARSE_EVENTS__TERM_TYPE_STR )
used + = snprintf ( buf + used , sub_non_neg ( len , used ) ,
" ,%s=%s " , term - > config ,
term - > val . str ) ;
}
if ( sub_non_neg ( len , used ) > 0 ) {
buf [ used ] = ' / ' ;
used + + ;
}
if ( sub_non_neg ( len , used ) > 0 ) {
buf [ used ] = ' \0 ' ;
used + + ;
} else
buf [ len - 1 ] = ' \0 ' ;
2013-04-20 22:02:29 +04:00
return buf ;
}
static char * format_alias_or ( char * buf , int len , struct perf_pmu * pmu ,
struct perf_pmu_alias * alias )
{
snprintf ( buf , len , " %s OR %s/%s/ " , alias - > name , pmu - > name , alias - > name ) ;
return buf ;
}
2016-09-16 01:24:50 +03:00
struct sevent {
2016-09-16 01:24:43 +03:00
char * name ;
char * desc ;
2016-09-16 01:24:50 +03:00
char * topic ;
2017-01-28 05:03:40 +03:00
char * str ;
char * pmu ;
2016-09-16 01:24:43 +03:00
} ;
2016-09-16 01:24:50 +03:00
static int cmp_sevent ( const void * a , const void * b )
2016-09-16 01:24:43 +03:00
{
2016-09-16 01:24:50 +03:00
const struct sevent * as = a ;
const struct sevent * bs = b ;
2016-09-16 01:24:43 +03:00
/* Put extra events last */
if ( ! ! as - > desc ! = ! ! bs - > desc )
return ! ! as - > desc - ! ! bs - > desc ;
2016-09-16 01:24:50 +03:00
if ( as - > topic & & bs - > topic ) {
int n = strcmp ( as - > topic , bs - > topic ) ;
if ( n )
return n ;
}
2016-09-16 01:24:43 +03:00
return strcmp ( as - > name , bs - > name ) ;
}
static void wordwrap ( char * s , int start , int max , int corr )
2013-04-20 22:02:29 +04:00
{
2016-09-16 01:24:43 +03:00
int column = start ;
int n ;
while ( * s ) {
int wlen = strcspn ( s , " \t " ) ;
if ( column + wlen > = max & & column > start ) {
printf ( " \n %*s " , start , " " ) ;
column = start + corr ;
}
n = printf ( " %s%.*s " , column > start ? " " : " " , wlen , s ) ;
if ( n < = 0 )
break ;
s + = wlen ;
column + = n ;
while ( isspace ( * s ) )
s + + ;
}
2013-04-20 22:02:29 +04:00
}
2016-09-16 01:24:48 +03:00
void print_pmu_events ( const char * event_glob , bool name_only , bool quiet_flag ,
bool long_desc )
2013-04-20 22:02:29 +04:00
{
struct perf_pmu * pmu ;
struct perf_pmu_alias * alias ;
char buf [ 1024 ] ;
int printed = 0 ;
int len , j ;
2016-09-16 01:24:50 +03:00
struct sevent * aliases ;
2016-09-16 01:24:43 +03:00
int numdesc = 0 ;
2016-09-16 01:24:44 +03:00
int columns = pager_get_columns ( ) ;
2016-09-16 01:24:50 +03:00
char * topic = NULL ;
2013-04-20 22:02:29 +04:00
pmu = NULL ;
len = 0 ;
2014-10-23 14:45:10 +04:00
while ( ( pmu = perf_pmu__scan ( pmu ) ) ! = NULL ) {
2013-04-20 22:02:29 +04:00
list_for_each_entry ( alias , & pmu - > aliases , list )
len + + ;
2014-10-23 14:45:10 +04:00
if ( pmu - > selectable )
len + + ;
}
2016-09-16 01:24:50 +03:00
aliases = zalloc ( sizeof ( struct sevent ) * len ) ;
2013-04-20 22:02:29 +04:00
if ( ! aliases )
2014-10-24 17:25:09 +04:00
goto out_enomem ;
2013-04-20 22:02:29 +04:00
pmu = NULL ;
j = 0 ;
2014-10-23 14:45:10 +04:00
while ( ( pmu = perf_pmu__scan ( pmu ) ) ! = NULL ) {
2013-04-20 22:02:29 +04:00
list_for_each_entry ( alias , & pmu - > aliases , list ) {
2016-09-16 01:24:43 +03:00
char * name = alias - > desc ? alias - > name :
format_alias ( buf , sizeof ( buf ) , pmu , alias ) ;
2013-04-20 22:02:29 +04:00
bool is_cpu = ! strcmp ( pmu - > name , " cpu " ) ;
if ( event_glob ! = NULL & &
2016-10-19 20:50:01 +03:00
! ( strglobmatch_nocase ( name , event_glob ) | |
( ! is_cpu & & strglobmatch_nocase ( alias - > name ,
2016-10-19 21:45:23 +03:00
event_glob ) ) | |
( alias - > topic & &
strglobmatch_nocase ( alias - > topic , event_glob ) ) ) )
2013-04-20 22:02:29 +04:00
continue ;
2014-10-24 17:25:09 +04:00
2016-09-16 01:24:43 +03:00
if ( is_cpu & & ! name_only & & ! alias - > desc )
2014-10-24 17:25:09 +04:00
name = format_alias_or ( buf , sizeof ( buf ) , pmu , alias ) ;
2016-09-16 01:24:43 +03:00
aliases [ j ] . name = name ;
if ( is_cpu & & ! name_only & & ! alias - > desc )
aliases [ j ] . name = format_alias_or ( buf ,
sizeof ( buf ) ,
pmu , alias ) ;
aliases [ j ] . name = strdup ( aliases [ j ] . name ) ;
if ( ! aliases [ j ] . name )
2014-10-24 17:25:09 +04:00
goto out_enomem ;
2016-09-16 01:24:43 +03:00
2016-09-16 01:24:48 +03:00
aliases [ j ] . desc = long_desc ? alias - > long_desc :
alias - > desc ;
2016-09-16 01:24:50 +03:00
aliases [ j ] . topic = alias - > topic ;
2017-01-28 05:03:40 +03:00
aliases [ j ] . str = alias - > str ;
aliases [ j ] . pmu = pmu - > name ;
2013-04-20 22:02:29 +04:00
j + + ;
}
2015-10-02 21:28:16 +03:00
if ( pmu - > selectable & &
( event_glob = = NULL | | strglobmatch ( pmu - > name , event_glob ) ) ) {
2014-10-24 17:25:09 +04:00
char * s ;
if ( asprintf ( & s , " %s// " , pmu - > name ) < 0 )
goto out_enomem ;
2016-09-16 01:24:43 +03:00
aliases [ j ] . name = s ;
2014-10-23 14:45:10 +04:00
j + + ;
}
}
2013-04-20 22:02:29 +04:00
len = j ;
2016-09-16 01:24:50 +03:00
qsort ( aliases , len , sizeof ( struct sevent ) , cmp_sevent ) ;
2013-04-20 22:02:29 +04:00
for ( j = 0 ; j < len ; j + + ) {
2017-01-28 05:03:38 +03:00
/* Skip duplicates */
if ( j > 0 & & ! strcmp ( aliases [ j ] . name , aliases [ j - 1 ] . name ) )
continue ;
2013-04-20 22:02:29 +04:00
if ( name_only ) {
2016-09-16 01:24:43 +03:00
printf ( " %s " , aliases [ j ] . name ) ;
2013-04-20 22:02:29 +04:00
continue ;
}
2016-09-16 01:24:45 +03:00
if ( aliases [ j ] . desc & & ! quiet_flag ) {
2016-09-16 01:24:43 +03:00
if ( numdesc + + = = 0 )
printf ( " \n " ) ;
2016-09-16 01:24:50 +03:00
if ( aliases [ j ] . topic & & ( ! topic | |
strcmp ( topic , aliases [ j ] . topic ) ) ) {
printf ( " %s%s: \n " , topic ? " \n " : " " ,
aliases [ j ] . topic ) ;
topic = aliases [ j ] . topic ;
}
2016-09-16 01:24:43 +03:00
printf ( " %-50s \n " , aliases [ j ] . name ) ;
printf ( " %*s " , 8 , " [ " ) ;
wordwrap ( aliases [ j ] . desc , 8 , columns , 0 ) ;
printf ( " ] \n " ) ;
2017-01-28 05:03:40 +03:00
if ( verbose )
printf ( " %*s%s/%s/ \n " , 8 , " " , aliases [ j ] . pmu , aliases [ j ] . str ) ;
2016-09-16 01:24:43 +03:00
} else
printf ( " %-50s [Kernel PMU event] \n " , aliases [ j ] . name ) ;
2013-04-20 22:02:29 +04:00
printed + + ;
}
2015-09-30 23:13:26 +03:00
if ( printed & & pager_in_use ( ) )
2013-04-20 22:02:29 +04:00
printf ( " \n " ) ;
2014-10-24 17:25:09 +04:00
out_free :
for ( j = 0 ; j < len ; j + + )
2016-09-16 01:24:43 +03:00
zfree ( & aliases [ j ] . name ) ;
2014-10-24 17:25:09 +04:00
zfree ( & aliases ) ;
return ;
out_enomem :
printf ( " FATAL: not enough memory to print PMU events \n " ) ;
if ( aliases )
goto out_free ;
2013-04-20 22:02:29 +04:00
}
2013-08-22 03:47:26 +04:00
bool pmu_have_event ( const char * pname , const char * name )
{
struct perf_pmu * pmu ;
struct perf_pmu_alias * alias ;
pmu = NULL ;
while ( ( pmu = perf_pmu__scan ( pmu ) ) ! = NULL ) {
if ( strcmp ( pname , pmu - > name ) )
continue ;
list_for_each_entry ( alias , & pmu - > aliases , list )
if ( ! strcmp ( alias - > name , name ) )
return true ;
}
return false ;
}
2014-07-31 10:00:50 +04:00
static FILE * perf_pmu__open_file ( struct perf_pmu * pmu , const char * name )
{
struct stat st ;
char path [ PATH_MAX ] ;
const char * sysfs ;
sysfs = sysfs__mountpoint ( ) ;
if ( ! sysfs )
return NULL ;
snprintf ( path , PATH_MAX ,
" %s " EVENT_SOURCE_DEVICE_PATH " %s/%s " , sysfs , pmu - > name , name ) ;
if ( stat ( path , & st ) < 0 )
return NULL ;
return fopen ( path , " r " ) ;
}
int perf_pmu__scan_file ( struct perf_pmu * pmu , const char * name , const char * fmt ,
. . . )
{
va_list args ;
FILE * file ;
int ret = EOF ;
va_start ( args , fmt ) ;
file = perf_pmu__open_file ( pmu , name ) ;
if ( file ) {
ret = vfscanf ( file , fmt , args ) ;
fclose ( file ) ;
}
va_end ( args ) ;
return ret ;
}