2013-06-28 16:22:19 +03:00
# include <stdio.h>
# include <sys/types.h>
# include <unistd.h>
# include <inttypes.h>
# include <sys/prctl.h>
# include "parse-events.h"
# include "evlist.h"
# include "evsel.h"
# include "thread_map.h"
# include "cpumap.h"
# include "tests.h"
# include "../arch/x86/util/tsc.h"
# define CHECK__(x) { \
while ( ( x ) < 0 ) { \
pr_debug ( # x " failed! \n " ) ; \
goto out_err ; \
} \
}
# define CHECK_NOT_NULL__(x) { \
while ( ( x ) = = NULL ) { \
pr_debug ( # x " failed! \n " ) ; \
goto out_err ; \
} \
}
static u64 rdtsc ( void )
{
unsigned int low , high ;
asm volatile ( " rdtsc " : " =a " ( low ) , " =d " ( high ) ) ;
return low | ( ( u64 ) high ) < < 32 ;
}
/**
* test__perf_time_to_tsc - test converting perf time to TSC .
*
* This function implements a test that checks that the conversion of perf time
* to and from TSC is consistent with the order of events . If the test passes
* % 0 is returned , otherwise % - 1 is returned . If TSC conversion is not
* supported then then the test passes but " (not supported) " is printed .
*/
int test__perf_time_to_tsc ( void )
{
struct perf_record_opts opts = {
. mmap_pages = UINT_MAX ,
. user_freq = UINT_MAX ,
. user_interval = ULLONG_MAX ,
. freq = 4000 ,
. target = {
. uses_mmap = true ,
} ,
. sample_time = true ,
} ;
struct thread_map * threads = NULL ;
struct cpu_map * cpus = NULL ;
struct perf_evlist * evlist = NULL ;
struct perf_evsel * evsel = NULL ;
int err = - 1 , ret , i ;
const char * comm1 , * comm2 ;
struct perf_tsc_conversion tc ;
struct perf_event_mmap_page * pc ;
union perf_event * event ;
u64 test_tsc , comm1_tsc , comm2_tsc ;
u64 test_time , comm1_time = 0 , comm2_time = 0 ;
threads = thread_map__new ( - 1 , getpid ( ) , UINT_MAX ) ;
CHECK_NOT_NULL__ ( threads ) ;
cpus = cpu_map__new ( NULL ) ;
CHECK_NOT_NULL__ ( cpus ) ;
evlist = perf_evlist__new ( ) ;
CHECK_NOT_NULL__ ( evlist ) ;
perf_evlist__set_maps ( evlist , cpus , threads ) ;
CHECK__ ( parse_events ( evlist , " cycles:u " ) ) ;
perf_evlist__config ( evlist , & opts ) ;
evsel = perf_evlist__first ( evlist ) ;
evsel - > attr . comm = 1 ;
evsel - > attr . disabled = 1 ;
evsel - > attr . enable_on_exec = 0 ;
CHECK__ ( perf_evlist__open ( evlist ) ) ;
CHECK__ ( perf_evlist__mmap ( evlist , UINT_MAX , false ) ) ;
pc = evlist - > mmap [ 0 ] . base ;
ret = perf_read_tsc_conversion ( pc , & tc ) ;
if ( ret ) {
if ( ret = = - EOPNOTSUPP ) {
fprintf ( stderr , " (not supported) " ) ;
return 0 ;
}
goto out_err ;
}
perf_evlist__enable ( evlist ) ;
comm1 = " Test COMM 1 " ;
CHECK__ ( prctl ( PR_SET_NAME , ( unsigned long ) comm1 , 0 , 0 , 0 ) ) ;
test_tsc = rdtsc ( ) ;
comm2 = " Test COMM 2 " ;
CHECK__ ( prctl ( PR_SET_NAME , ( unsigned long ) comm2 , 0 , 0 , 0 ) ) ;
perf_evlist__disable ( evlist ) ;
for ( i = 0 ; i < evlist - > nr_mmaps ; i + + ) {
while ( ( event = perf_evlist__mmap_read ( evlist , i ) ) ! = NULL ) {
struct perf_sample sample ;
if ( event - > header . type ! = PERF_RECORD_COMM | |
( pid_t ) event - > comm . pid ! = getpid ( ) | |
( pid_t ) event - > comm . tid ! = getpid ( ) )
2013-10-24 15:43:33 +08:00
goto next_event ;
2013-06-28 16:22:19 +03:00
if ( strcmp ( event - > comm . comm , comm1 ) = = 0 ) {
CHECK__ ( perf_evsel__parse_sample ( evsel , event ,
& sample ) ) ;
comm1_time = sample . time ;
}
if ( strcmp ( event - > comm . comm , comm2 ) = = 0 ) {
CHECK__ ( perf_evsel__parse_sample ( evsel , event ,
& sample ) ) ;
comm2_time = sample . time ;
}
2013-10-24 15:43:33 +08:00
next_event :
perf_evlist__mmap_consume ( evlist , i ) ;
2013-06-28 16:22:19 +03:00
}
}
if ( ! comm1_time | | ! comm2_time )
goto out_err ;
test_time = tsc_to_perf_time ( test_tsc , & tc ) ;
comm1_tsc = perf_time_to_tsc ( comm1_time , & tc ) ;
comm2_tsc = perf_time_to_tsc ( comm2_time , & tc ) ;
pr_debug ( " 1st event perf time % " PRIu64 " tsc % " PRIu64 " \n " ,
comm1_time , comm1_tsc ) ;
pr_debug ( " rdtsc time % " PRIu64 " tsc % " PRIu64 " \n " ,
test_time , test_tsc ) ;
pr_debug ( " 2nd event perf time % " PRIu64 " tsc % " PRIu64 " \n " ,
comm2_time , comm2_tsc ) ;
if ( test_time < = comm1_time | |
test_time > = comm2_time )
goto out_err ;
if ( test_tsc < = comm1_tsc | |
test_tsc > = comm2_tsc )
goto out_err ;
err = 0 ;
out_err :
if ( evlist ) {
perf_evlist__disable ( evlist ) ;
perf_evlist__munmap ( evlist ) ;
perf_evlist__close ( evlist ) ;
perf_evlist__delete ( evlist ) ;
}
if ( cpus )
cpu_map__delete ( cpus ) ;
if ( threads )
thread_map__delete ( threads ) ;
return err ;
}