2012-03-15 23:09:17 +04:00
# include <linux/list.h>
# 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"
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 ;
}
2013-11-12 20:58:49 +04: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 19:34:05 +04:00
const char * lc ;
2013-11-12 20:58:49 +04: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 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 ;
}
2013-11-12 20:58:49 +04:00
static int perf_pmu__new_alias ( struct list_head * list , char * dir , char * name , FILE * file )
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 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 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 ;
2013-11-12 20:58:49 +04:00
2012-06-15 10:31:41 +04:00
ret = parse_events_terms ( & alias - > terms , buf ) ;
if ( ret ) {
free ( alias ) ;
return ret ;
}
alias - > name = strdup ( name ) ;
2013-11-12 20:58:49 +04: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 12:31:12 +03:00
perf_pmu__parse_per_pkg ( alias , dir , name ) ;
2013-11-12 20:58:49 +04:00
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 ;
}
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-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 ;
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 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 ) ;
ret = - EINVAL ;
file = fopen ( path , " r " ) ;
if ( ! file )
break ;
2013-11-12 20:58:49 +04:00
ret = perf_pmu__new_alias ( head , dir , name , file ) ;
2012-06-15 10:31:41 +04: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 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 ) {
parse_events__free_terms ( & list ) ;
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 ( ) ;
2012-09-10 11:53:50 +04: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 10:00:49 +04:00
struct perf_event_attr * __attribute__ ( ( weak ) )
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 ;
2012-10-10 16:53:16 +04:00
if ( pmu_aliases ( name , & aliases ) )
return NULL ;
2012-03-15 23:09:17 +04:00
if ( pmu_type ( name , & type ) )
return NULL ;
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 *
2012-03-15 23:09:17 +04:00
pmu_find_format ( struct list_head * formats , char * name )
{
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 ;
}
/*
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
}
}
/*
* 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 ,
bool zero )
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 ;
/*
* Support only for hardcoded and numnerial terms .
* Hardcoded terms should be already in , so nothing
* to be done for them .
*/
if ( parse_events__is_hardcoded_term ( term ) )
return 0 ;
2012-04-25 20:24:57 +04:00
if ( term - > type_val ! = PARSE_EVENTS__TERM_TYPE_NUM )
2012-03-15 23:09:17 +04:00
return - EINVAL ;
format = pmu_find_format ( formats , term - > config ) ;
if ( ! format )
return - EINVAL ;
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
/*
* XXX If we ever decide to go with string values for
* non - hardcoded terms , here ' s the place to translate
* them into value .
*/
2014-07-31 10:00:49 +04:00
pmu_format_value ( format - > bits , term - > val . num , 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 ,
bool zero )
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
2012-05-21 11:12:53 +04:00
list_for_each_entry ( term , head_terms , list )
2014-07-31 10:00:49 +04:00
if ( pmu_config_term ( formats , attr , term , zero ) )
2012-03-15 23:09:17 +04:00
return - EINVAL ;
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 10:00:49 +04:00
bool zero = ! ! pmu - > default_config ;
2012-03-15 23:09:17 +04:00
attr - > type = pmu - > type ;
2014-07-31 10:00:49 +04:00
return perf_pmu__config_terms ( & pmu - > format , attr , head_terms , zero ) ;
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
static int check_unit_scale ( struct perf_pmu_alias * alias ,
2014-01-17 19:34:05 +04:00
const char * * unit , double * scale )
2013-11-12 20:58:49 +04:00
{
/*
* Only one term in event definition can
* define unit and scale , fail if there ' s
* more than one .
*/
if ( ( * unit & & alias - > unit ) | |
( * scale & & alias - > scale ) )
return - EINVAL ;
if ( alias - > unit )
* unit = alias - > unit ;
if ( alias - > scale )
* scale = alias - > scale ;
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-09-24 18:04:06 +04:00
info - > unit = NULL ;
info - > scale = 0.0 ;
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-09-24 18:04:06 +04:00
ret = check_unit_scale ( alias , & info - > unit , & info - > scale ) ;
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
static char * format_alias ( char * buf , int len , struct perf_pmu * pmu ,
struct perf_pmu_alias * alias )
{
snprintf ( buf , len , " %s/%s/ " , pmu - > name , alias - > name ) ;
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 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 + + ;
}
2014-10-24 17:25:09 +04:00
aliases = zalloc ( sizeof ( char * ) * 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 ) {
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 17:25:09 +04:00
2013-04-20 22:02:29 +04:00
if ( is_cpu & & ! name_only )
2014-10-24 17:25:09 +04:00
name = format_alias_or ( buf , sizeof ( buf ) , pmu , alias ) ;
aliases [ j ] = strdup ( name ) ;
if ( aliases [ j ] = = NULL )
goto out_enomem ;
2013-04-20 22:02:29 +04:00
j + + ;
}
2014-10-23 14:45:10 +04:00
if ( pmu - > selectable ) {
2014-10-24 17:25:09 +04:00
char * s ;
if ( asprintf ( & s , " %s// " , pmu - > name ) < 0 )
goto out_enomem ;
aliases [ j ] = s ;
2014-10-23 14:45:10 +04:00
j + + ;
}
}
2013-04-20 22:02:29 +04: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 17:25:09 +04: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 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 ;
}