2012-03-15 20:09:17 +01:00
# include <linux/list.h>
# include <sys/types.h>
# include <unistd.h>
# include <stdio.h>
2014-07-31 09:00:49 +03:00
# include <stdbool.h>
2014-07-31 09:00:50 +03:00
# include <stdarg.h>
2012-03-15 20:09:17 +01:00
# include <dirent.h>
2013-12-09 17:14:24 +01:00
# include <api/fs/fs.h>
2013-11-12 17:58:49 +01:00
# include <locale.h>
2012-03-15 20:09:17 +01:00
# include "util.h"
# include "pmu.h"
# include "parse-events.h"
2012-09-10 15:53:50 +08:00
# include "cpumap.h"
2012-03-15 20:09:17 +01:00
2013-01-18 17:05:09 -03:00
struct perf_pmu_format {
char * name ;
int value ;
DECLARE_BITMAP ( bits , PERF_PMU_FORMAT_BITS ) ;
struct list_head list ;
} ;
2012-08-16 21:10:24 +02:00
# define EVENT_SOURCE_DEVICE_PATH " / bus / event_source / devices / "
2012-03-15 20:09:17 +01: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 01:46:50 +01:00
int perf_pmu__format_parse ( char * dir , struct list_head * head )
2012-03-15 20:09:17 +01: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 16:20:25 +03:00
static int pmu_format ( const char * name , struct list_head * format )
2012-03-15 20:09:17 +01:00
{
struct stat st ;
char path [ PATH_MAX ] ;
2013-11-05 14:48:50 -03:00
const char * sysfs = sysfs__mountpoint ( ) ;
2012-03-15 20:09:17 +01:00
if ( ! sysfs )
return - 1 ;
snprintf ( path , PATH_MAX ,
2012-08-16 21:10:24 +02:00
" %s " EVENT_SOURCE_DEVICE_PATH " %s/format " , sysfs , name ) ;
2012-03-15 20:09:17 +01:00
if ( stat ( path , & st ) < 0 )
2012-06-14 22:38:37 +02:00
return 0 ; /* no error if format does not exist */
2012-03-15 20:09:17 +01:00
2012-11-10 01:46:50 +01:00
if ( perf_pmu__format_parse ( path , format ) )
2012-03-15 20:09:17 +01:00
return - 1 ;
return 0 ;
}
2013-11-12 17:58:49 +01:00
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 ] ;
2014-01-17 16:34:05 +01:00
const char * lc ;
2013-11-12 17:58:49 +01:00
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 ;
scale [ sret ] = ' \0 ' ;
/*
* save current locale
*/
lc = setlocale ( LC_NUMERIC , NULL ) ;
/*
* force to C locale to ensure kernel
* scale string is converted correctly .
* kernel uses default C locale .
*/
setlocale ( LC_NUMERIC , " C " ) ;
alias - > scale = strtod ( scale , NULL ) ;
/* restore locale */
setlocale ( LC_NUMERIC , lc ) ;
ret = 0 ;
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 ;
sret = read ( fd , alias - > unit , UNIT_MAX_LEN ) ;
if ( sret < 0 )
goto error ;
close ( fd ) ;
alias - > unit [ sret ] = ' \0 ' ;
return 0 ;
error :
close ( fd ) ;
alias - > unit [ 0 ] = ' \0 ' ;
return - 1 ;
}
2014-11-21 10:31:12 +01: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 10:31:13 +01: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 ;
}
2013-11-12 17:58:49 +01:00
static int perf_pmu__new_alias ( struct list_head * list , char * dir , char * name , FILE * file )
2012-06-15 14:31:41 +08:00
{
2013-01-18 16:54:00 -03:00
struct perf_pmu_alias * alias ;
2012-06-15 14:31:41 +08:00
char buf [ 256 ] ;
int ret ;
ret = fread ( buf , 1 , sizeof ( buf ) , file ) ;
if ( ret = = 0 )
return - EINVAL ;
buf [ ret ] = 0 ;
alias = malloc ( sizeof ( * alias ) ) ;
if ( ! alias )
return - ENOMEM ;
INIT_LIST_HEAD ( & alias - > terms ) ;
2013-11-12 17:58:49 +01:00
alias - > scale = 1.0 ;
alias - > unit [ 0 ] = ' \0 ' ;
2014-11-21 10:31:12 +01:00
alias - > per_pkg = false ;
2013-11-12 17:58:49 +01:00
2012-06-15 14:31:41 +08:00
ret = parse_events_terms ( & alias - > terms , buf ) ;
if ( ret ) {
free ( alias ) ;
return ret ;
}
alias - > name = strdup ( name ) ;
2013-11-12 17:58:49 +01:00
/*
* load unit name and scale if available
*/
perf_pmu__parse_unit ( alias , dir , name ) ;
perf_pmu__parse_scale ( alias , dir , name ) ;
2014-11-21 10:31:12 +01:00
perf_pmu__parse_per_pkg ( alias , dir , name ) ;
2014-11-21 10:31:13 +01:00
perf_pmu__parse_snapshot ( alias , dir , name ) ;
2013-11-12 17:58:49 +01:00
2012-06-15 14:31:41 +08:00
list_add_tail ( & alias - > list , list ) ;
2013-11-12 17:58:49 +01:00
2012-06-15 14:31:41 +08:00
return 0 ;
}
2014-09-24 15:04:06 +01: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 10:31:12 +01:00
if ( len > 8 & & ! strcmp ( name + len - 8 , " .per-pkg " ) )
return true ;
2014-11-21 10:31:13 +01:00
if ( len > 9 & & ! strcmp ( name + len - 9 , " .snapshot " ) )
return true ;
2014-09-24 15:04:06 +01:00
return false ;
}
2012-06-15 14:31:41 +08: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 ;
int ret = 0 ;
event_dir = opendir ( dir ) ;
if ( ! event_dir )
return - EINVAL ;
while ( ! ret & & ( evt_ent = readdir ( event_dir ) ) ) {
char path [ PATH_MAX ] ;
char * name = evt_ent - > d_name ;
FILE * file ;
if ( ! strcmp ( name , " . " ) | | ! strcmp ( name , " .. " ) )
continue ;
2013-11-12 17:58:49 +01:00
/*
2014-09-24 15:04:06 +01:00
* skip info files parsed in perf_pmu__new_alias ( )
2013-11-12 17:58:49 +01:00
*/
2014-09-24 15:04:06 +01:00
if ( pmu_alias_info_file ( name ) )
2013-11-12 17:58:49 +01:00
continue ;
2012-06-15 14:31:41 +08:00
snprintf ( path , PATH_MAX , " %s/%s " , dir , name ) ;
ret = - EINVAL ;
file = fopen ( path , " r " ) ;
if ( ! file )
break ;
2013-11-12 17:58:49 +01:00
ret = perf_pmu__new_alias ( head , dir , name , file ) ;
2012-06-15 14:31:41 +08:00
fclose ( file ) ;
}
closedir ( event_dir ) ;
return ret ;
}
/*
* 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 16:20:25 +03:00
static int pmu_aliases ( const char * name , struct list_head * head )
2012-06-15 14:31:41 +08:00
{
struct stat st ;
char path [ PATH_MAX ] ;
2013-11-05 14:48:50 -03:00
const char * sysfs = sysfs__mountpoint ( ) ;
2012-06-15 14:31:41 +08: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 14:53:16 +02:00
return 0 ; /* no error if 'events' does not exist */
2012-06-15 14:31:41 +08:00
if ( pmu_aliases_parse ( path , head ) )
return - 1 ;
return 0 ;
}
2013-01-18 16:54:00 -03:00
static int pmu_alias_terms ( struct perf_pmu_alias * alias ,
2012-06-15 14:31:41 +08:00
struct list_head * terms )
{
2014-04-16 20:49:02 +02:00
struct parse_events_term * term , * cloned ;
2012-06-15 14:31:41 +08:00
LIST_HEAD ( list ) ;
int ret ;
list_for_each_entry ( term , & alias - > terms , list ) {
2014-04-16 20:49:02 +02:00
ret = parse_events_term__clone ( & cloned , term ) ;
2012-06-15 14:31:41 +08:00
if ( ret ) {
parse_events__free_terms ( & list ) ;
return ret ;
}
2014-04-16 20:49:02 +02:00
list_add_tail ( & cloned - > list , & list ) ;
2012-06-15 14:31:41 +08:00
}
list_splice ( & list , terms ) ;
return 0 ;
}
2012-03-15 20:09:17 +01: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 16:20:25 +03:00
static int pmu_type ( const char * name , __u32 * type )
2012-03-15 20:09:17 +01:00
{
struct stat st ;
char path [ PATH_MAX ] ;
FILE * file ;
int ret = 0 ;
2013-11-05 14:48:50 -03:00
const char * sysfs = sysfs__mountpoint ( ) ;
2012-03-15 20:09:17 +01:00
if ( ! sysfs )
return - 1 ;
snprintf ( path , PATH_MAX ,
2012-08-16 21:10:24 +02:00
" %s " EVENT_SOURCE_DEVICE_PATH " %s/type " , sysfs , name ) ;
2012-03-15 20:09:17 +01: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 21:10:24 +02: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 14:48:50 -03:00
const char * sysfs = sysfs__mountpoint ( ) ;
2012-08-16 21:10:24 +02: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 16:20:25 +03:00
static struct cpu_map * pmu_cpumask ( const char * name )
2012-09-10 15:53:50 +08:00
{
struct stat st ;
char path [ PATH_MAX ] ;
FILE * file ;
struct cpu_map * cpus ;
2013-11-05 14:48:50 -03:00
const char * sysfs = sysfs__mountpoint ( ) ;
2012-09-10 15:53:50 +08:00
if ( ! sysfs )
return NULL ;
snprintf ( path , PATH_MAX ,
" %s/bus/event_source/devices/%s/cpumask " , sysfs , name ) ;
if ( stat ( path , & st ) < 0 )
return NULL ;
file = fopen ( path , " r " ) ;
if ( ! file )
return NULL ;
cpus = cpu_map__read ( file ) ;
fclose ( file ) ;
return cpus ;
}
2014-07-31 09:00:49 +03:00
struct perf_event_attr * __attribute__ ( ( weak ) )
perf_pmu__get_default_config ( struct perf_pmu * pmu __maybe_unused )
{
return NULL ;
}
2013-07-04 16:20:25 +03:00
static struct perf_pmu * pmu_lookup ( const char * name )
2012-03-15 20:09:17 +01:00
{
struct perf_pmu * pmu ;
LIST_HEAD ( format ) ;
2012-06-15 14:31:41 +08:00
LIST_HEAD ( aliases ) ;
2012-03-15 20:09:17 +01: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 ;
2012-10-10 14:53:16 +02:00
if ( pmu_aliases ( name , & aliases ) )
return NULL ;
2012-03-15 20:09:17 +01:00
if ( pmu_type ( name , & type ) )
return NULL ;
pmu = zalloc ( sizeof ( * pmu ) ) ;
if ( ! pmu )
return NULL ;
2012-09-10 15:53:50 +08:00
pmu - > cpus = pmu_cpumask ( name ) ;
2012-03-15 20:09:17 +01:00
INIT_LIST_HEAD ( & pmu - > format ) ;
2012-06-15 14:31:41 +08:00
INIT_LIST_HEAD ( & pmu - > aliases ) ;
2012-03-15 20:09:17 +01:00
list_splice ( & format , & pmu - > format ) ;
2012-06-15 14:31:41 +08:00
list_splice ( & aliases , & pmu - > aliases ) ;
2012-03-15 20:09:17 +01:00
pmu - > name = strdup ( name ) ;
pmu - > type = type ;
2012-06-14 22:38:37 +02:00
list_add_tail ( & pmu - > list , & pmus ) ;
2014-07-31 09:00:49 +03:00
pmu - > default_config = perf_pmu__get_default_config ( pmu ) ;
2012-03-15 20:09:17 +01:00
return pmu ;
}
2013-07-04 16:20:25 +03:00
static struct perf_pmu * pmu_find ( const char * name )
2012-03-15 20:09:17 +01:00
{
struct perf_pmu * pmu ;
list_for_each_entry ( pmu , & pmus , list )
if ( ! strcmp ( pmu - > name , name ) )
return pmu ;
return NULL ;
}
2012-08-16 21:10:24 +02: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 16:20:25 +03:00
struct perf_pmu * perf_pmu__find ( const char * name )
2012-03-15 20:09:17 +01: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 16:54:00 -03:00
static struct perf_pmu_format *
2012-03-15 20:09:17 +01:00
pmu_find_format ( struct list_head * formats , char * name )
{
2013-01-18 16:54:00 -03:00
struct perf_pmu_format * format ;
2012-03-15 20:09:17 +01:00
list_for_each_entry ( format , formats , list )
if ( ! strcmp ( format - > name , name ) )
return format ;
return NULL ;
}
/*
2014-07-31 09:00:49 +03:00
* Sets value based on the format definition ( format parameter )
2012-03-15 20:09:17 +01:00
* and unformated value ( value parameter ) .
*/
2014-07-31 09:00:49 +03:00
static void pmu_format_value ( unsigned long * format , __u64 value , __u64 * v ,
bool zero )
2012-03-15 20:09:17 +01: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 09:00:49 +03:00
if ( value & ( 1llu < < vbit + + ) )
* v | = ( 1llu < < fbit ) ;
else if ( zero )
* v & = ~ ( 1llu < < fbit ) ;
2012-03-15 20:09:17 +01:00
}
}
2015-01-07 17:13:50 -08: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 ;
}
2012-03-15 20:09:17 +01:00
/*
* Setup one of config [ 12 ] attr members based on the
2014-01-08 08:43:51 -08:00
* user input data - term parameter .
2012-03-15 20:09:17 +01:00
*/
static int pmu_config_term ( struct list_head * formats ,
struct perf_event_attr * attr ,
2014-07-31 09:00:49 +03:00
struct parse_events_term * term ,
2015-01-07 17:13:50 -08:00
struct list_head * head_terms ,
2014-07-31 09:00:49 +03:00
bool zero )
2012-03-15 20:09:17 +01:00
{
2013-01-18 16:54:00 -03:00
struct perf_pmu_format * format ;
2012-03-15 20:09:17 +01:00
__u64 * vp ;
2015-01-07 17:13:50 -08:00
__u64 val ;
/*
* 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 20:09:17 +01: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-07 17:13:50 -08:00
if ( ! format ) {
if ( verbose )
printf ( " Invalid event/parameter '%s' \n " , term - > config ) ;
2012-03-15 20:09:17 +01:00
return - EINVAL ;
2015-01-07 17:13:50 -08:00
}
2012-03-15 20:09:17 +01: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 18:24:57 +02:00
/*
2015-01-07 17:13:50 -08:00
* Either directly use a numeric term , or try to translate string terms
* using event parameters .
2012-04-25 18:24:57 +02:00
*/
2015-01-07 17:13:50 -08: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 , " ? " ) ) {
if ( verbose )
pr_info ( " Invalid sysfs entry %s=%s \n " ,
term - > config , term - > val . str ) ;
return - EINVAL ;
}
if ( pmu_resolve_param_term ( term , head_terms , & val ) )
return - EINVAL ;
} else
return - EINVAL ;
pmu_format_value ( format - > bits , val , vp , zero ) ;
2012-03-15 20:09:17 +01:00
return 0 ;
}
2012-11-10 01:46:50 +01:00
int perf_pmu__config_terms ( struct list_head * formats ,
struct perf_event_attr * attr ,
2014-07-31 09:00:49 +03:00
struct list_head * head_terms ,
bool zero )
2012-03-15 20:09:17 +01:00
{
2013-01-18 16:29:49 -03:00
struct parse_events_term * term ;
2012-03-15 20:09:17 +01:00
2015-01-07 17:13:50 -08:00
list_for_each_entry ( term , head_terms , list ) {
if ( pmu_config_term ( formats , attr , term , head_terms , zero ) )
2012-03-15 20:09:17 +01:00
return - EINVAL ;
2015-01-07 17:13:50 -08:00
}
2012-03-15 20:09:17 +01: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 ,
struct list_head * head_terms )
{
2014-07-31 09:00:49 +03:00
bool zero = ! ! pmu - > default_config ;
2012-03-15 20:09:17 +01:00
attr - > type = pmu - > type ;
2014-07-31 09:00:49 +03:00
return perf_pmu__config_terms ( & pmu - > format , attr , head_terms , zero ) ;
2012-03-15 20:09:17 +01:00
}
2013-01-18 16:54:00 -03:00
static struct perf_pmu_alias * pmu_find_alias ( struct perf_pmu * pmu ,
struct parse_events_term * term )
2012-06-15 14:31:41 +08:00
{
2013-01-18 16:54:00 -03:00
struct perf_pmu_alias * alias ;
2012-06-15 14:31:41 +08: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 17:58:49 +01:00
2014-11-21 10:31:13 +01:00
static int check_info_data ( struct perf_pmu_alias * alias ,
struct perf_pmu_info * info )
2013-11-12 17:58:49 +01:00
{
/*
* Only one term in event definition can
2014-11-21 10:31:13 +01:00
* define unit , scale and snapshot , fail
* if there ' s more than one .
2013-11-12 17:58:49 +01:00
*/
2014-11-21 10:31:13 +01:00
if ( ( info - > unit & & alias - > unit ) | |
( info - > scale & & alias - > scale ) | |
( info - > snapshot & & alias - > snapshot ) )
2013-11-12 17:58:49 +01:00
return - EINVAL ;
if ( alias - > unit )
2014-11-21 10:31:13 +01:00
info - > unit = alias - > unit ;
2013-11-12 17:58:49 +01:00
if ( alias - > scale )
2014-11-21 10:31:13 +01:00
info - > scale = alias - > scale ;
if ( alias - > snapshot )
info - > snapshot = alias - > snapshot ;
2013-11-12 17:58:49 +01:00
return 0 ;
}
2012-06-15 14:31:41 +08:00
/*
* Find alias in the terms list and replace it with the terms
* defined for the alias
*/
2013-11-12 17:58:49 +01:00
int perf_pmu__check_alias ( struct perf_pmu * pmu , struct list_head * head_terms ,
2014-09-24 15:04:06 +01:00
struct perf_pmu_info * info )
2012-06-15 14:31:41 +08:00
{
2013-01-18 16:29:49 -03:00
struct parse_events_term * term , * h ;
2013-01-18 16:54:00 -03:00
struct perf_pmu_alias * alias ;
2012-06-15 14:31:41 +08:00
int ret ;
2014-11-21 10:31:12 +01:00
info - > per_pkg = false ;
2014-01-17 16:34:05 +01:00
/*
* Mark unit and scale as not set
* ( different from default values , see below )
*/
2014-11-21 10:31:13 +01:00
info - > unit = NULL ;
info - > scale = 0.0 ;
info - > snapshot = false ;
2013-11-12 17:58:49 +01:00
2012-06-15 14:31:41 +08: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 17:58:49 +01:00
2014-11-21 10:31:13 +01:00
ret = check_info_data ( alias , info ) ;
2013-11-12 17:58:49 +01:00
if ( ret )
return ret ;
2014-11-21 10:31:12 +01:00
if ( alias - > per_pkg )
info - > per_pkg = true ;
2012-06-15 14:31:41 +08:00
list_del ( & term - > list ) ;
free ( term ) ;
}
2014-01-17 16:34:05 +01:00
/*
* if no unit or scale foundin aliases , then
* set defaults as for evsel
* unit cannot left to NULL
*/
2014-09-24 15:04:06 +01:00
if ( info - > unit = = NULL )
info - > unit = " " ;
2014-01-17 16:34:05 +01:00
2014-09-24 15:04:06 +01:00
if ( info - > scale = = 0.0 )
info - > scale = 1.0 ;
2014-01-17 16:34:05 +01:00
2012-06-15 14:31:41 +08:00
return 0 ;
}
2012-03-15 20:09:17 +01:00
int perf_pmu__new_format ( struct list_head * list , char * name ,
int config , unsigned long * bits )
{
2013-01-18 16:54:00 -03:00
struct perf_pmu_format * format ;
2012-03-15 20:09:17 +01: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 09:11:30 -08:00
memset ( bits , 0 , BITS_TO_BYTES ( PERF_PMU_FORMAT_BITS ) ) ;
2012-03-15 20:09:17 +01:00
for ( b = from ; b < = to ; b + + )
set_bit ( b , bits ) ;
}
2013-04-20 11:02:29 -07:00
2015-01-07 17:13:51 -08:00
static int sub_non_neg ( int a , int b )
{
if ( b > a )
return 0 ;
return a - b ;
}
2013-04-20 11:02:29 -07:00
static char * format_alias ( char * buf , int len , struct perf_pmu * pmu ,
struct perf_pmu_alias * alias )
{
2015-01-07 17:13:51 -08: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 11:02:29 -07: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 ;
}
static int cmp_string ( const void * a , const void * b )
{
const char * const * as = a ;
const char * const * bs = b ;
return strcmp ( * as , * bs ) ;
}
void print_pmu_events ( const char * event_glob , bool name_only )
{
struct perf_pmu * pmu ;
struct perf_pmu_alias * alias ;
char buf [ 1024 ] ;
int printed = 0 ;
int len , j ;
char * * aliases ;
pmu = NULL ;
len = 0 ;
2014-10-23 13:45:10 +03:00
while ( ( pmu = perf_pmu__scan ( pmu ) ) ! = NULL ) {
2013-04-20 11:02:29 -07:00
list_for_each_entry ( alias , & pmu - > aliases , list )
len + + ;
2014-10-23 13:45:10 +03:00
if ( pmu - > selectable )
len + + ;
}
2014-10-24 10:25:09 -03:00
aliases = zalloc ( sizeof ( char * ) * len ) ;
2013-04-20 11:02:29 -07:00
if ( ! aliases )
2014-10-24 10:25:09 -03:00
goto out_enomem ;
2013-04-20 11:02:29 -07:00
pmu = NULL ;
j = 0 ;
2014-10-23 13:45:10 +03:00
while ( ( pmu = perf_pmu__scan ( pmu ) ) ! = NULL ) {
2013-04-20 11:02:29 -07:00
list_for_each_entry ( alias , & pmu - > aliases , list ) {
char * name = format_alias ( buf , sizeof ( buf ) , pmu , alias ) ;
bool is_cpu = ! strcmp ( pmu - > name , " cpu " ) ;
if ( event_glob ! = NULL & &
! ( strglobmatch ( name , event_glob ) | |
( ! is_cpu & & strglobmatch ( alias - > name ,
event_glob ) ) ) )
continue ;
2014-10-24 10:25:09 -03:00
2013-04-20 11:02:29 -07:00
if ( is_cpu & & ! name_only )
2014-10-24 10:25:09 -03:00
name = format_alias_or ( buf , sizeof ( buf ) , pmu , alias ) ;
aliases [ j ] = strdup ( name ) ;
if ( aliases [ j ] = = NULL )
goto out_enomem ;
2013-04-20 11:02:29 -07:00
j + + ;
}
2014-10-23 13:45:10 +03:00
if ( pmu - > selectable ) {
2014-10-24 10:25:09 -03:00
char * s ;
if ( asprintf ( & s , " %s// " , pmu - > name ) < 0 )
goto out_enomem ;
aliases [ j ] = s ;
2014-10-23 13:45:10 +03:00
j + + ;
}
}
2013-04-20 11:02:29 -07:00
len = j ;
qsort ( aliases , len , sizeof ( char * ) , cmp_string ) ;
for ( j = 0 ; j < len ; j + + ) {
if ( name_only ) {
printf ( " %s " , aliases [ j ] ) ;
continue ;
}
printf ( " %-50s [Kernel PMU event] \n " , aliases [ j ] ) ;
printed + + ;
}
if ( printed )
printf ( " \n " ) ;
2014-10-24 10:25:09 -03:00
out_free :
for ( j = 0 ; j < len ; j + + )
zfree ( & aliases [ j ] ) ;
zfree ( & aliases ) ;
return ;
out_enomem :
printf ( " FATAL: not enough memory to print PMU events \n " ) ;
if ( aliases )
goto out_free ;
2013-04-20 11:02:29 -07:00
}
2013-08-21 16:47:26 -07: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 09:00:50 +03: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 ;
}