Blame src/components/perfctr/perfctr.c

Packit 577717
/* 
Packit 577717
* File:    perfctr.c
Packit 577717
* Author:  Philip Mucci
Packit 577717
*          mucci at cs.utk.edu
Packit 577717
* Mods:    Kevin London
Packit 577717
*          london at cs.utk.edu
Packit 577717
* Mods:    Maynard Johnson
Packit 577717
*          maynardj at us.ibm.com
Packit 577717
* Mods:    Brian Sheely
Packit 577717
*          bsheely at eecs.utk.edu
Packit 577717
*/
Packit 577717
Packit 577717
#include <string.h>
Packit 577717
#include <linux/unistd.h>
Packit 577717
#include <errno.h>
Packit 577717
#include <sys/time.h>
Packit 577717
Packit 577717
#include "papi.h"
Packit 577717
#include "papi_internal.h"
Packit 577717
Packit 577717
#ifdef PPC64
Packit 577717
#include "perfctr-ppc64.h"
Packit 577717
#else
Packit 577717
#include "perfctr-x86.h"
Packit 577717
#include "papi_libpfm_events.h"
Packit 577717
#endif
Packit 577717
Packit 577717
#include "papi_vector.h"
Packit 577717
Packit 577717
#include "papi_memory.h"
Packit 577717
#include "extras.h"
Packit 577717
Packit 577717
#include "linux-common.h"
Packit 577717
#include "linux-context.h"
Packit 577717
Packit 577717
extern papi_vector_t _perfctr_vector;
Packit 577717
Packit 577717
#ifdef PPC64
Packit 577717
extern int setup_ppc64_presets( int cputype, int cidx );
Packit 577717
#endif
Packit 577717
Packit 577717
/* This should be in a linux.h header file maybe. */
Packit 577717
#define FOPEN_ERROR "fopen(%s) returned NULL"
Packit 577717
Packit 577717
#if defined(PERFCTR26)
Packit 577717
#define PERFCTR_CPU_NAME(pi)    perfctr_info_cpu_name(pi)
Packit 577717
#define PERFCTR_CPU_NRCTRS(pi)  perfctr_info_nrctrs(pi)
Packit 577717
#else
Packit 577717
#define PERFCTR_CPU_NAME        perfctr_cpu_name
Packit 577717
#define PERFCTR_CPU_NRCTRS      perfctr_cpu_nrctrs
Packit 577717
#endif
Packit 577717
Packit 577717
#if !defined(PPC64) 
Packit 577717
static inline int
Packit 577717
xlate_cpu_type_to_vendor( unsigned perfctr_cpu_type )
Packit 577717
{
Packit 577717
	switch ( perfctr_cpu_type ) {
Packit 577717
	case PERFCTR_X86_INTEL_P5:
Packit 577717
	case PERFCTR_X86_INTEL_P5MMX:
Packit 577717
	case PERFCTR_X86_INTEL_P6:
Packit 577717
	case PERFCTR_X86_INTEL_PII:
Packit 577717
	case PERFCTR_X86_INTEL_PIII:
Packit 577717
	case PERFCTR_X86_INTEL_P4:
Packit 577717
	case PERFCTR_X86_INTEL_P4M2:
Packit 577717
#ifdef PERFCTR_X86_INTEL_P4M3
Packit 577717
	case PERFCTR_X86_INTEL_P4M3:
Packit 577717
#endif
Packit 577717
#ifdef PERFCTR_X86_INTEL_PENTM
Packit 577717
	case PERFCTR_X86_INTEL_PENTM:
Packit 577717
#endif
Packit 577717
#ifdef PERFCTR_X86_INTEL_CORE
Packit 577717
	case PERFCTR_X86_INTEL_CORE:
Packit 577717
#endif
Packit 577717
#ifdef PERFCTR_X86_INTEL_CORE2
Packit 577717
	case PERFCTR_X86_INTEL_CORE2:
Packit 577717
#endif
Packit 577717
#ifdef PERFCTR_X86_INTEL_ATOM	/* family 6 model 28 */
Packit 577717
	case PERFCTR_X86_INTEL_ATOM:
Packit 577717
#endif
Packit 577717
#ifdef PERFCTR_X86_INTEL_NHLM	/* family 6 model 26 */
Packit 577717
	case PERFCTR_X86_INTEL_NHLM:
Packit 577717
#endif
Packit 577717
#ifdef PERFCTR_X86_INTEL_WSTMR
Packit 577717
	case PERFCTR_X86_INTEL_WSTMR:
Packit 577717
#endif
Packit 577717
		return ( PAPI_VENDOR_INTEL );
Packit 577717
#ifdef PERFCTR_X86_AMD_K8
Packit 577717
	case PERFCTR_X86_AMD_K8:
Packit 577717
#endif
Packit 577717
#ifdef PERFCTR_X86_AMD_K8C
Packit 577717
	case PERFCTR_X86_AMD_K8C:
Packit 577717
#endif
Packit 577717
#ifdef PERFCTR_X86_AMD_FAM10 /* this is defined in perfctr 2.6.29 */
Packit 577717
	case PERFCTR_X86_AMD_FAM10:
Packit 577717
#endif
Packit 577717
	case PERFCTR_X86_AMD_K7:
Packit 577717
		return ( PAPI_VENDOR_AMD );
Packit 577717
	default:
Packit 577717
		return ( PAPI_VENDOR_UNKNOWN );
Packit 577717
	}
Packit 577717
}
Packit 577717
#endif
Packit 577717
Packit 577717
long long tb_scale_factor = ( long long ) 1;	/* needed to scale get_cycles on PPC series */
Packit 577717
Packit 577717
int
Packit 577717
_perfctr_init_component( int cidx )
Packit 577717
{
Packit 577717
	int retval;
Packit 577717
	struct perfctr_info info;
Packit 577717
	char abiv[PAPI_MIN_STR_LEN];
Packit 577717
Packit 577717
#if defined(PERFCTR26)
Packit 577717
	int fd;
Packit 577717
#else
Packit 577717
	struct vperfctr *dev;
Packit 577717
#endif
Packit 577717
Packit 577717
#if defined(PERFCTR26)
Packit 577717
	/* Get info from the kernel */
Packit 577717
	/* Use lower level calls per Mikael to get the perfctr info
Packit 577717
	   without actually creating a new kernel-side state.
Packit 577717
	   Also, close the fd immediately after retrieving the info.
Packit 577717
	   This is much lighter weight and doesn't reserve the counter
Packit 577717
	   resources. Also compatible with perfctr 2.6.14.
Packit 577717
	 */
Packit 577717
	fd = _vperfctr_open( 0 );
Packit 577717
	if ( fd < 0 ) {
Packit 577717
	   strncpy(_perfctr_vector.cmp_info.disabled_reason,
Packit 577717
		  VOPEN_ERROR,PAPI_MAX_STR_LEN);
Packit 577717
	   return PAPI_ESYS;
Packit 577717
	}
Packit 577717
	retval = perfctr_info( fd, &info );
Packit 577717
	close( fd );
Packit 577717
	if ( retval < 0 ) {
Packit 577717
	   strncpy(_perfctr_vector.cmp_info.disabled_reason,
Packit 577717
		  VINFO_ERROR,PAPI_MAX_STR_LEN);
Packit 577717
	   return PAPI_ESYS;
Packit 577717
	}
Packit 577717
Packit 577717
	/* copy tsc multiplier to local variable        */
Packit 577717
	/* this field appears in perfctr 2.6 and higher */
Packit 577717
	tb_scale_factor = ( long long ) info.tsc_to_cpu_mult;
Packit 577717
#else
Packit 577717
	/* Opened once for all threads. */
Packit 577717
	if ( ( dev = vperfctr_open(  ) ) == NULL ) {
Packit 577717
	   strncpy(_perfctr_vector.cmp_info.disabled_reason,
Packit 577717
		  VOPEN_ERROR,PAPI_MAX_STR_LEN);
Packit 577717
	   return PAPI_ESYS;
Packit 577717
	}
Packit 577717
	SUBDBG( "_perfctr_init_component vperfctr_open = %p\n", dev );
Packit 577717
Packit 577717
	/* Get info from the kernel */
Packit 577717
	retval = vperfctr_info( dev, &info );
Packit 577717
	if ( retval < 0 ) {
Packit 577717
	   strncpy(_perfctr_vector.cmp_info.disabled_reason,
Packit 577717
		  VINFO_ERROR,PAPI_MAX_STR_LEN);
Packit 577717
		return ( PAPI_ESYS );
Packit 577717
	}
Packit 577717
	vperfctr_close( dev );
Packit 577717
#endif
Packit 577717
Packit 577717
	/* Fill in what we can of the papi_system_info. */
Packit 577717
	retval = _papi_os_vector.get_system_info( &_papi_hwi_system_info );
Packit 577717
	if ( retval != PAPI_OK )
Packit 577717
		return ( retval );
Packit 577717
Packit 577717
	/* Setup memory info */
Packit 577717
	retval = _papi_os_vector.get_memory_info( &_papi_hwi_system_info.hw_info,
Packit 577717
						   ( int ) info.cpu_type );
Packit 577717
	if ( retval )
Packit 577717
		return ( retval );
Packit 577717
Packit 577717
	strcpy( _perfctr_vector.cmp_info.name,"perfctr.c" );
Packit 577717
	strcpy( _perfctr_vector.cmp_info.version, "$Revision$" );
Packit 577717
	sprintf( abiv, "0x%08X", info.abi_version );
Packit 577717
	strcpy( _perfctr_vector.cmp_info.support_version, abiv );
Packit 577717
	strcpy( _perfctr_vector.cmp_info.kernel_version, info.driver_version );
Packit 577717
	_perfctr_vector.cmp_info.CmpIdx = cidx;
Packit 577717
	_perfctr_vector.cmp_info.num_cntrs = ( int ) PERFCTR_CPU_NRCTRS( &info );
Packit 577717
        _perfctr_vector.cmp_info.num_mpx_cntrs=_perfctr_vector.cmp_info.num_cntrs;
Packit 577717
	if ( info.cpu_features & PERFCTR_FEATURE_RDPMC )
Packit 577717
		_perfctr_vector.cmp_info.fast_counter_read = 1;
Packit 577717
	else
Packit 577717
		_perfctr_vector.cmp_info.fast_counter_read = 0;
Packit 577717
	_perfctr_vector.cmp_info.fast_real_timer = 1;
Packit 577717
	_perfctr_vector.cmp_info.fast_virtual_timer = 1;
Packit 577717
	_perfctr_vector.cmp_info.attach = 1;
Packit 577717
	_perfctr_vector.cmp_info.attach_must_ptrace = 1;
Packit 577717
	_perfctr_vector.cmp_info.default_domain = PAPI_DOM_USER;
Packit 577717
#if !defined(PPC64)
Packit 577717
	/* AMD and Intel ia386 processors all support unit mask bits */
Packit 577717
	_perfctr_vector.cmp_info.cntr_umasks = 1;
Packit 577717
#endif
Packit 577717
#if defined(PPC64)
Packit 577717
	_perfctr_vector.cmp_info.available_domains =
Packit 577717
		PAPI_DOM_USER | PAPI_DOM_KERNEL | PAPI_DOM_SUPERVISOR;
Packit 577717
#else
Packit 577717
	_perfctr_vector.cmp_info.available_domains = PAPI_DOM_USER | PAPI_DOM_KERNEL;
Packit 577717
#endif
Packit 577717
	_perfctr_vector.cmp_info.default_granularity = PAPI_GRN_THR;
Packit 577717
	_perfctr_vector.cmp_info.available_granularities = PAPI_GRN_THR;
Packit 577717
	if ( info.cpu_features & PERFCTR_FEATURE_PCINT )
Packit 577717
		_perfctr_vector.cmp_info.hardware_intr = 1;
Packit 577717
	else
Packit 577717
		_perfctr_vector.cmp_info.hardware_intr = 0;
Packit 577717
	SUBDBG( "Hardware/OS %s support counter generated interrupts\n",
Packit 577717
			_perfctr_vector.cmp_info.hardware_intr ? "does" : "does not" );
Packit 577717
Packit 577717
	strcpy( _papi_hwi_system_info.hw_info.model_string,
Packit 577717
			PERFCTR_CPU_NAME( &info ) );
Packit 577717
	_papi_hwi_system_info.hw_info.model = ( int ) info.cpu_type;
Packit 577717
#if defined(PPC64)
Packit 577717
	_papi_hwi_system_info.hw_info.vendor = PAPI_VENDOR_IBM;
Packit 577717
	if ( strlen( _papi_hwi_system_info.hw_info.vendor_string ) == 0 )
Packit 577717
		strcpy( _papi_hwi_system_info.hw_info.vendor_string, "IBM" );
Packit 577717
#else
Packit 577717
	_papi_hwi_system_info.hw_info.vendor =
Packit 577717
		xlate_cpu_type_to_vendor( info.cpu_type );
Packit 577717
#endif
Packit 577717
Packit 577717
	/* Setup presets last. Some platforms depend on earlier info */
Packit 577717
#if !defined(PPC64)
Packit 577717
//     retval = setup_p3_vector_table(vtable);
Packit 577717
		if ( !retval )
Packit 577717
				retval = _papi_libpfm_init(&_perfctr_vector, cidx ); 
Packit 577717
#else
Packit 577717
	/* Setup native and preset events */
Packit 577717
//  retval = ppc64_setup_vector_table(vtable);
Packit 577717
	if ( !retval )
Packit 577717
		retval = perfctr_ppc64_setup_native_table(  );
Packit 577717
	if ( !retval )
Packit 577717
	  retval = setup_ppc64_presets( info.cpu_type, cidx );
Packit 577717
#endif
Packit 577717
	if ( retval )
Packit 577717
		return ( retval );
Packit 577717
Packit 577717
	return ( PAPI_OK );
Packit 577717
}
Packit 577717
Packit 577717
static int
Packit 577717
attach( hwd_control_state_t * ctl, unsigned long tid )
Packit 577717
{
Packit 577717
	struct vperfctr_control tmp;
Packit 577717
Packit 577717
#ifdef VPERFCTR_CONTROL_CLOEXEC
Packit 577717
	tmp.flags = VPERFCTR_CONTROL_CLOEXEC;
Packit 577717
#endif
Packit 577717
Packit 577717
	ctl->rvperfctr = rvperfctr_open( ( int ) tid );
Packit 577717
	if ( ctl->rvperfctr == NULL ) {
Packit 577717
		PAPIERROR( VOPEN_ERROR );
Packit 577717
		return ( PAPI_ESYS );
Packit 577717
	}
Packit 577717
	SUBDBG( "_papi_hwd_ctl rvperfctr_open() = %p\n", ctl->rvperfctr );
Packit 577717
Packit 577717
	/* Initialize the per thread/process virtualized TSC */
Packit 577717
	memset( &tmp, 0x0, sizeof ( tmp ) );
Packit 577717
	tmp.cpu_control.tsc_on = 1;
Packit 577717
Packit 577717
	/* Start the per thread/process virtualized TSC */
Packit 577717
	if ( rvperfctr_control( ctl->rvperfctr, &tmp ) < 0 ) {
Packit 577717
		PAPIERROR( RCNTRL_ERROR );
Packit 577717
		return ( PAPI_ESYS );
Packit 577717
	}
Packit 577717
Packit 577717
	return ( PAPI_OK );
Packit 577717
}							 /* end attach() */
Packit 577717
Packit 577717
static int
Packit 577717
detach( hwd_control_state_t * ctl )
Packit 577717
{
Packit 577717
	rvperfctr_close( ctl->rvperfctr );
Packit 577717
	return ( PAPI_OK );
Packit 577717
}							 /* end detach() */
Packit 577717
Packit 577717
static inline int
Packit 577717
round_requested_ns( int ns )
Packit 577717
{
Packit 577717
	if ( ns < _papi_os_info.itimer_res_ns ) {
Packit 577717
		return _papi_os_info.itimer_res_ns;
Packit 577717
	} else {
Packit 577717
		int leftover_ns = ns % _papi_os_info.itimer_res_ns;
Packit 577717
		return ns + leftover_ns;
Packit 577717
	}
Packit 577717
}
Packit 577717
Packit 577717
int
Packit 577717
_perfctr_ctl( hwd_context_t * ctx, int code, _papi_int_option_t * option )
Packit 577717
{
Packit 577717
	( void ) ctx;			 /*unused */
Packit 577717
	switch ( code ) {
Packit 577717
	case PAPI_DOMAIN:
Packit 577717
	case PAPI_DEFDOM:
Packit 577717
#if defined(PPC64)
Packit 577717
		return ( _perfctr_vector.
Packit 577717
				 set_domain( option->domain.ESI, option->domain.domain ) );
Packit 577717
#else
Packit 577717
		return ( _perfctr_vector.
Packit 577717
				 set_domain( option->domain.ESI->ctl_state,
Packit 577717
							 option->domain.domain ) );
Packit 577717
#endif
Packit 577717
	case PAPI_GRANUL:
Packit 577717
	case PAPI_DEFGRN:
Packit 577717
		return PAPI_ECMP;
Packit 577717
	case PAPI_ATTACH:
Packit 577717
		return ( attach( option->attach.ESI->ctl_state, option->attach.tid ) );
Packit 577717
	case PAPI_DETACH:
Packit 577717
		return ( detach( option->attach.ESI->ctl_state ) );
Packit 577717
	case PAPI_DEF_ITIMER:
Packit 577717
	{
Packit 577717
		/* flags are currently ignored, eventually the flags will be able
Packit 577717
		   to specify whether or not we use POSIX itimers (clock_gettimer) */
Packit 577717
		if ( ( option->itimer.itimer_num == ITIMER_REAL ) &&
Packit 577717
			 ( option->itimer.itimer_sig != SIGALRM ) )
Packit 577717
			return PAPI_EINVAL;
Packit 577717
		if ( ( option->itimer.itimer_num == ITIMER_VIRTUAL ) &&
Packit 577717
			 ( option->itimer.itimer_sig != SIGVTALRM ) )
Packit 577717
			return PAPI_EINVAL;
Packit 577717
		if ( ( option->itimer.itimer_num == ITIMER_PROF ) &&
Packit 577717
			 ( option->itimer.itimer_sig != SIGPROF ) )
Packit 577717
			return PAPI_EINVAL;
Packit 577717
		if ( option->itimer.ns > 0 )
Packit 577717
			option->itimer.ns = round_requested_ns( option->itimer.ns );
Packit 577717
		/* At this point, we assume the user knows what he or
Packit 577717
		   she is doing, they maybe doing something arch specific */
Packit 577717
		return PAPI_OK;
Packit 577717
	}
Packit 577717
	case PAPI_DEF_MPX_NS:
Packit 577717
	{
Packit 577717
		option->multiplex.ns =
Packit 577717
			( unsigned long ) round_requested_ns( ( int ) option->multiplex.
Packit 577717
												  ns );
Packit 577717
		return ( PAPI_OK );
Packit 577717
	}
Packit 577717
	case PAPI_DEF_ITIMER_NS:
Packit 577717
	{
Packit 577717
		option->itimer.ns = round_requested_ns( option->itimer.ns );
Packit 577717
		return ( PAPI_OK );
Packit 577717
	}
Packit 577717
	default:
Packit 577717
		return ( PAPI_ENOSUPP );
Packit 577717
	}
Packit 577717
}
Packit 577717
Packit 577717
void
Packit 577717
_perfctr_dispatch_timer( int signal, siginfo_t * si, void *context )
Packit 577717
{
Packit 577717
   ( void ) signal;		 /*unused */
Packit 577717
   _papi_hwi_context_t ctx;
Packit 577717
   ThreadInfo_t *master = NULL;
Packit 577717
   int isHardware = 0;
Packit 577717
   caddr_t address;
Packit 577717
   int cidx = _perfctr_vector.cmp_info.CmpIdx;
Packit 577717
   hwd_context_t *our_context;
Packit 577717
   
Packit 577717
   ctx.si = si;
Packit 577717
   ctx.ucontext = ( ucontext_t * ) context;
Packit 577717
Packit 577717
#define OVERFLOW_MASK si->si_pmc_ovf_mask
Packit 577717
#define GEN_OVERFLOW 0
Packit 577717
Packit 577717
   address = ( caddr_t ) GET_OVERFLOW_ADDRESS( ( ctx ) );
Packit 577717
   _papi_hwi_dispatch_overflow_signal( ( void * ) &ctx, address, &isHardware,
Packit 577717
       	      	      			OVERFLOW_MASK, GEN_OVERFLOW, &master,
Packit 577717
	   	      			_perfctr_vector.cmp_info.CmpIdx );
Packit 577717
Packit 577717
   /* We are done, resume interrupting counters */
Packit 577717
   if ( isHardware ) {
Packit 577717
     our_context=(hwd_context_t *) master->context[cidx];
Packit 577717
      errno = vperfctr_iresume( our_context->perfctr );
Packit 577717
      if ( errno < 0 ) {
Packit 577717
	 PAPIERROR( "vperfctr_iresume errno %d", errno );
Packit 577717
      }
Packit 577717
   }
Packit 577717
}
Packit 577717
Packit 577717
Packit 577717
int
Packit 577717
_perfctr_init_thread( hwd_context_t * ctx )
Packit 577717
{
Packit 577717
	struct vperfctr_control tmp;
Packit 577717
	int error;
Packit 577717
Packit 577717
	/* Initialize our thread/process pointer. */
Packit 577717
	if ( ( ctx->perfctr = vperfctr_open(  ) ) == NULL ) {
Packit 577717
#ifdef VPERFCTR_OPEN_CREAT_EXCL
Packit 577717
		/* New versions of perfctr have this, which allows us to
Packit 577717
		   get a previously created context, i.e. one created after
Packit 577717
		   a fork and now we're inside a new process that has been exec'd */
Packit 577717
		if ( errno ) {
Packit 577717
			if ( ( ctx->perfctr = vperfctr_open_mode( 0 ) ) == NULL ) {
Packit 577717
			   return PAPI_ESYS;
Packit 577717
			}
Packit 577717
		} else {
Packit 577717
			return PAPI_ESYS;
Packit 577717
		}
Packit 577717
#else
Packit 577717
		return PAPI_ESYS;
Packit 577717
#endif
Packit 577717
	}
Packit 577717
	SUBDBG( "_papi_hwd_init vperfctr_open() = %p\n", ctx->perfctr );
Packit 577717
Packit 577717
	/* Initialize the per thread/process virtualized TSC */
Packit 577717
	memset( &tmp, 0x0, sizeof ( tmp ) );
Packit 577717
	tmp.cpu_control.tsc_on = 1;
Packit 577717
Packit 577717
#ifdef VPERFCTR_CONTROL_CLOEXEC
Packit 577717
	tmp.flags = VPERFCTR_CONTROL_CLOEXEC;
Packit 577717
	SUBDBG( "close on exec\t\t\t%u\n", tmp.flags );
Packit 577717
#endif
Packit 577717
Packit 577717
	/* Start the per thread/process virtualized TSC */
Packit 577717
	error = vperfctr_control( ctx->perfctr, &tmp );
Packit 577717
	if ( error < 0 ) {
Packit 577717
		SUBDBG( "starting virtualized TSC; vperfctr_control returns %d\n",
Packit 577717
				error );
Packit 577717
		return PAPI_ESYS;
Packit 577717
	}
Packit 577717
Packit 577717
	return PAPI_OK;
Packit 577717
}
Packit 577717
Packit 577717
/* This routine is for shutting down threads, including the
Packit 577717
   master thread. */
Packit 577717
Packit 577717
int
Packit 577717
_perfctr_shutdown_thread( hwd_context_t * ctx )
Packit 577717
{
Packit 577717
#ifdef DEBUG
Packit 577717
	int retval = vperfctr_unlink( ctx->perfctr );
Packit 577717
	SUBDBG( "_papi_hwd_shutdown vperfctr_unlink(%p) = %d\n", ctx->perfctr,
Packit 577717
			retval );
Packit 577717
#else
Packit 577717
	vperfctr_unlink( ctx->perfctr );
Packit 577717
#endif
Packit 577717
	vperfctr_close( ctx->perfctr );
Packit 577717
	SUBDBG( "_perfctr_shutdown vperfctr_close(%p)\n", ctx->perfctr );
Packit 577717
	memset( ctx, 0x0, sizeof ( hwd_context_t ) );
Packit 577717
	return ( PAPI_OK );
Packit 577717
}