2013-08-27 12:23:08 +04:00
# include "evlist.h"
# include "evsel.h"
# include "cpumap.h"
2013-08-27 12:23:09 +04:00
# include "parse-events.h"
2013-12-09 20:14:24 +04:00
# include <api/fs/fs.h>
2013-11-05 18:14:47 +04:00
# include "util.h"
2013-08-27 12:23:09 +04:00
typedef void ( * setup_probe_fn_t ) ( struct perf_evsel * evsel ) ;
static int perf_do_probe_api ( setup_probe_fn_t fn , int cpu , const char * str )
{
struct perf_evlist * evlist ;
struct perf_evsel * evsel ;
int err = - EAGAIN , fd ;
evlist = perf_evlist__new ( ) ;
if ( ! evlist )
return - ENOMEM ;
if ( parse_events ( evlist , str ) )
goto out_delete ;
evsel = perf_evlist__first ( evlist ) ;
fd = sys_perf_event_open ( & evsel - > attr , - 1 , cpu , - 1 , 0 ) ;
if ( fd < 0 )
goto out_delete ;
close ( fd ) ;
fn ( evsel ) ;
fd = sys_perf_event_open ( & evsel - > attr , - 1 , cpu , - 1 , 0 ) ;
if ( fd < 0 ) {
if ( errno = = EINVAL )
err = - EINVAL ;
goto out_delete ;
}
close ( fd ) ;
err = 0 ;
out_delete :
perf_evlist__delete ( evlist ) ;
return err ;
}
static bool perf_probe_api ( setup_probe_fn_t fn )
{
const char * try [ ] = { " cycles:u " , " instructions:u " , " cpu-clock " , NULL } ;
struct cpu_map * cpus ;
int cpu , ret , i = 0 ;
cpus = cpu_map__new ( NULL ) ;
if ( ! cpus )
return false ;
cpu = cpus - > map [ 0 ] ;
cpu_map__delete ( cpus ) ;
do {
ret = perf_do_probe_api ( fn , cpu , try [ i + + ] ) ;
if ( ! ret )
return true ;
} while ( ret = = - EAGAIN & & try [ i ] ) ;
return false ;
}
static void perf_probe_sample_identifier ( struct perf_evsel * evsel )
{
evsel - > attr . sample_type | = PERF_SAMPLE_IDENTIFIER ;
}
bool perf_can_sample_identifier ( void )
{
return perf_probe_api ( perf_probe_sample_identifier ) ;
}
2013-08-27 12:23:08 +04:00
2013-12-19 21:43:45 +04:00
void perf_evlist__config ( struct perf_evlist * evlist , struct record_opts * opts )
2013-08-27 12:23:08 +04:00
{
struct perf_evsel * evsel ;
2013-08-27 12:23:09 +04:00
bool use_sample_identifier = false ;
2013-08-27 12:23:08 +04:00
/*
* Set the evsel leader links before we configure attributes ,
* since some might depend on this info .
*/
if ( opts - > group )
perf_evlist__set_leader ( evlist ) ;
if ( evlist - > cpus - > map [ 0 ] < 0 )
opts - > no_inherit = true ;
2014-01-10 17:37:27 +04:00
evlist__for_each ( evlist , evsel )
2013-08-27 12:23:08 +04:00
perf_evsel__config ( evsel , opts ) ;
2013-08-27 12:23:09 +04:00
if ( evlist - > nr_entries > 1 ) {
struct perf_evsel * first = perf_evlist__first ( evlist ) ;
2014-01-10 17:37:27 +04:00
evlist__for_each ( evlist , evsel ) {
2013-08-27 12:23:09 +04:00
if ( evsel - > attr . sample_type = = first - > attr . sample_type )
continue ;
use_sample_identifier = perf_can_sample_identifier ( ) ;
break ;
}
2014-01-10 17:37:27 +04:00
evlist__for_each ( evlist , evsel )
2013-08-27 12:23:09 +04:00
perf_evsel__set_sample_id ( evsel , use_sample_identifier ) ;
2013-08-27 12:23:08 +04:00
}
2013-08-27 12:23:09 +04:00
perf_evlist__set_id_pos ( evlist ) ;
2013-08-27 12:23:08 +04:00
}
2013-11-05 18:14:47 +04:00
static int get_max_rate ( unsigned int * rate )
{
char path [ PATH_MAX ] ;
const char * procfs = procfs__mountpoint ( ) ;
if ( ! procfs )
return - 1 ;
snprintf ( path , PATH_MAX ,
" %s/sys/kernel/perf_event_max_sample_rate " , procfs ) ;
return filename__read_int ( path , ( int * ) rate ) ;
}
2013-12-19 21:43:45 +04:00
static int record_opts__config_freq ( struct record_opts * opts )
2013-11-05 18:14:47 +04:00
{
bool user_freq = opts - > user_freq ! = UINT_MAX ;
unsigned int max_rate ;
if ( opts - > user_interval ! = ULLONG_MAX )
opts - > default_interval = opts - > user_interval ;
if ( user_freq )
opts - > freq = opts - > user_freq ;
/*
* User specified count overrides default frequency .
*/
if ( opts - > default_interval )
opts - > freq = 0 ;
else if ( opts - > freq ) {
opts - > default_interval = opts - > freq ;
} else {
pr_err ( " frequency and count are zero, aborting \n " ) ;
return - 1 ;
}
if ( get_max_rate ( & max_rate ) )
return 0 ;
/*
* User specified frequency is over current maximum .
*/
if ( user_freq & & ( max_rate < opts - > freq ) ) {
pr_err ( " Maximum frequency rate (%u) reached. \n "
" Please use -F freq option with lower value or consider \n "
" tweaking /proc/sys/kernel/perf_event_max_sample_rate. \n " ,
max_rate ) ;
return - 1 ;
}
/*
* Default frequency is over current maximum .
*/
if ( max_rate < opts - > freq ) {
pr_warning ( " Lowering default frequency rate to %u. \n "
" Please consider tweaking "
" /proc/sys/kernel/perf_event_max_sample_rate. \n " ,
max_rate ) ;
opts - > freq = max_rate ;
}
return 0 ;
}
2013-12-19 21:43:45 +04:00
int record_opts__config ( struct record_opts * opts )
2013-11-05 18:14:47 +04:00
{
2013-12-19 21:43:45 +04:00
return record_opts__config_freq ( opts ) ;
2013-11-05 18:14:47 +04:00
}
2013-12-11 16:36:29 +04:00
bool perf_evlist__can_select_event ( struct perf_evlist * evlist , const char * str )
{
struct perf_evlist * temp_evlist ;
struct perf_evsel * evsel ;
int err , fd , cpu ;
bool ret = false ;
temp_evlist = perf_evlist__new ( ) ;
if ( ! temp_evlist )
return false ;
err = parse_events ( temp_evlist , str ) ;
if ( err )
goto out_delete ;
evsel = perf_evlist__last ( temp_evlist ) ;
if ( ! evlist | | cpu_map__empty ( evlist - > cpus ) ) {
struct cpu_map * cpus = cpu_map__new ( NULL ) ;
cpu = cpus ? cpus - > map [ 0 ] : 0 ;
cpu_map__delete ( cpus ) ;
} else {
cpu = evlist - > cpus - > map [ 0 ] ;
}
fd = sys_perf_event_open ( & evsel - > attr , - 1 , cpu , - 1 , 0 ) ;
if ( fd > = 0 ) {
close ( fd ) ;
ret = true ;
}
out_delete :
perf_evlist__delete ( temp_evlist ) ;
return ret ;
}