/* This file handles the OS dependent part of the POWER5 and POWER6 architectures.
It supports both AIX 4 and AIX 5. The switch between AIX 4 and 5 is driven by the
system defined value _AIX_VERSION_510.
Other routines also include minor conditionally compiled differences.
*/
#include <sys/utsname.h>
#include "papi.h"
#include "papi_internal.h"
#include "papi_lock.h"
#include "papi_memory.h"
#include "extras.h"
#include "aix.h"
#include "papi_vector.h"
/* Advance declarations */
papi_vector_t _aix_vector;
/* Locking variables */
volatile int lock_var[PAPI_MAX_LOCK] = { 0 };
atomic_p lock[PAPI_MAX_LOCK];
/*
some heap information, start_of_text, start_of_data .....
ref: http://publibn.boulder.ibm.com/doc_link/en_US/a_doc_lib/aixprggd/genprogc/sys_mem_alloc.htm#HDRA9E4A4C9921SYLV
*/
#define START_OF_TEXT &_text
#define END_OF_TEXT &_etext
#define START_OF_DATA &_data
#define END_OF_DATA &_edata
#define START_OF_BSS &_edata
#define END_OF_BSS &_end
static int maxgroups = 0;
struct utsname AixVer;
native_event_entry_t native_table[PAPI_MAX_NATIVE_EVENTS];
hwd_pminfo_t pminfo;
pm_groups_info_t pmgroups;
native_event_entry_t native_table[PAPI_MAX_NATIVE_EVENTS];
PPC64_native_map_t native_name_map[PAPI_MAX_NATIVE_EVENTS];
hwd_groups_t group_map[MAX_GROUPS] = { 0 };
/* to initialize the native_table */
void
aix_initialize_native_table( )
{
int i, j;
memset( native_table, 0,
PAPI_MAX_NATIVE_EVENTS * sizeof ( native_event_entry_t ) );
memset( native_name_map, 0,
PAPI_MAX_NATIVE_EVENTS * sizeof ( PPC64_native_map_t ) );
for ( i = 0; i < PAPI_MAX_NATIVE_EVENTS; i++ ) {
native_name_map[i].index = -1;
for ( j = 0; j < MAX_COUNTERS; j++ )
native_table[i].resources.counter_cmd[j] = -1;
}
}
/* to setup native_table group value */
static void
aix_ppc64_setup_gps( int total )
{
int i, j, gnum;
for ( i = 0; i < total; i++ ) {
for ( j = 0; j < MAX_COUNTERS; j++ ) {
/* native_table[i].resources.rgg[j]=-1; */
if ( native_table[i].resources.selector & ( 1 << j ) ) {
for ( gnum = 0; gnum < pmgroups.maxgroups; gnum++ ) {
if ( native_table[i].resources.counter_cmd[j] ==
pmgroups.event_groups[gnum].events[j] ) {
/* could use gnum instead of pmgroups.event_groups[gnum].group_id */
native_table[i].resources.group[pmgroups.
event_groups[gnum].
group_id / 32] |=
1 << ( pmgroups.event_groups[gnum].group_id % 32 );
}
}
}
}
}
for ( gnum = 0; gnum < pmgroups.maxgroups; gnum++ ) {
for ( i = 0; i < MAX_COUNTERS; i++ ) {
/*group_map[gnum].counter_cmd[i] = pmgroups.event_groups[gnum].events[i]; */
if (pmgroups.event_groups[gnum].group_id >=MAX_GROUPS) {
fprintf(stderr,"ERROR, group number trying to go past MAX GROUPS\n");
continue;
}
group_map[pmgroups.event_groups[gnum].group_id].counter_cmd[i] =
pmgroups.event_groups[gnum].events[i];
}
}
}
/* to setup native_table values, and return number of entries */
int
aix_ppc64_setup_native_table( )
{
hwd_pmevents_t *wevp;
hwd_pminfo_t *info;
int pmc, ev, i, j, index;
info = &pminfo;
index = 0;
aix_initialize_native_table( );
for ( pmc = 0; pmc < info->maxpmcs; pmc++ ) {
wevp = info->list_events[pmc];
for ( ev = 0; ev < info->maxevents[pmc]; ev++, wevp++ ) {
for ( i = 0; i < index; i++ ) {
if ( strcmp( wevp->short_name, native_table[i].name ) == 0 ) {
native_table[i].resources.selector |= 1 << pmc;
native_table[i].resources.counter_cmd[pmc] = wevp->event_id;
break;
}
}
if ( i == index ) {
/*native_table[i].index=i; */
native_table[i].resources.selector |= 1 << pmc;
native_table[i].resources.counter_cmd[pmc] = wevp->event_id;
native_table[i].name = wevp->short_name;
native_table[i].description = wevp->description;
native_name_map[i].name = native_table[i].name;
native_name_map[i].index = i;
index++;
}
}
}
aix_ppc64_setup_gps( index );
return index;
}
/* Reports the elements of the hwd_register_t struct as an array of names and a matching array of values.
Maximum string length is name_len; Maximum number of values is count.
*/
static void
copy_value( unsigned int val, char *nam, char *names, unsigned int *values,
int len )
{
*values = val;
strncpy( names, nam, len );
names[len - 1] = '\0';
}
/* this function recusively does Modified Bipartite Graph counter allocation
success return 1
fail return 0
*/
static int
do_counter_allocation( ppc64_reg_alloc_t * event_list, int size )
{
int i, j, group = -1;
unsigned int map[GROUP_INTS];
for ( i = 0; i < GROUP_INTS; i++ )
map[i] = event_list[0].ra_group[i];
for ( i = 1; i < size; i++ ) {
for ( j = 0; j < GROUP_INTS; j++ )
map[j] &= event_list[i].ra_group[j];
}
for ( i = 0; i < GROUP_INTS; i++ ) {
if ( map[i] ) {
group = ffs( map[i] ) - 1 + i * 32;
break;
}
}
if ( group < 0 )
return group; /* allocation fail */
else {
for ( i = 0; i < size; i++ ) {
for ( j = 0; j < MAX_COUNTERS; j++ ) {
if ( event_list[i].ra_counter_cmd[j] >= 0
&& event_list[i].ra_counter_cmd[j] ==
group_map[group].counter_cmd[j] )
event_list[i].ra_position = j;
}
}
return group;
}
}
/* this function will be called when there are counters available
success return 1
fail return 0
*/
int
_aix_allocate_registers( EventSetInfo_t * ESI )
{
hwd_control_state_t *this_state = ESI->ctl_state;
unsigned char selector;
int i, j, natNum, index;
ppc64_reg_alloc_t event_list[MAX_COUNTERS];
int position, group;
/* not yet successfully mapped, but have enough slots for events */
/* Initialize the local structure needed
for counter allocation and optimization. */
natNum = ESI->NativeCount;
for ( i = 0; i < natNum; i++ ) {
/* CAUTION: Since this is in the hardware layer, it's ok
to access the native table directly, but in general this is a bad idea */
event_list[i].ra_position = -1;
/* calculate native event rank, which is number of counters it can live on, this is power3 specific */
for ( j = 0; j < MAX_COUNTERS; j++ ) {
if ( ( index =
native_name_map[ESI->NativeInfoArray[i].
ni_event & PAPI_NATIVE_AND_MASK].index ) <
0 )
return PAPI_ECNFLCT;
event_list[i].ra_counter_cmd[j] =
native_table[index].resources.counter_cmd[j];
}
for ( j = 0; j < GROUP_INTS; j++ ) {
if ( ( index =
native_name_map[ESI->NativeInfoArray[i].
ni_event & PAPI_NATIVE_AND_MASK].index ) <
0 )
return PAPI_ECNFLCT;
event_list[i].ra_group[j] = native_table[index].resources.group[j];
}
/*event_list[i].ra_mod = -1; */
}
if ( ( group = do_counter_allocation( event_list, natNum ) ) >= 0 ) { /* successfully mapped */
/* copy counter allocations info back into NativeInfoArray */
this_state->group_id = group;
for ( i = 0; i < natNum; i++ )
ESI->NativeInfoArray[i].ni_position = event_list[i].ra_position;
/* update the control structure based on the NativeInfoArray */
/*_papi_hwd_update_control_state(this_state, ESI->NativeInfoArray, natNum);*/
return PAPI_OK;
} else {
return PAPI_ECNFLCT;
}
}
int
_aix_init_control_state( hwd_control_state_t * ptr )
{
int i;
for ( i = 0; i < _aix_vector.cmp_info.num_cntrs; i++ ) {
ptr->counter_cmd.events[i] = COUNT_NOTHING;
}
ptr->counter_cmd.mode.b.is_group = 1;
_aix_vector.set_domain( ptr, _aix_vector.cmp_info.default_domain );
_aix_set_granularity( ptr, _aix_vector.cmp_info.default_granularity );
/*setup_native_table(); */
return ( PAPI_OK );
}
/* This function updates the control structure with whatever resources are allocated
for all the native events in the native info structure array. */
int
_aix_update_control_state( hwd_control_state_t * this_state,
NativeInfo_t * native, int count,
hwd_context_t * context )
{
this_state->counter_cmd.events[0] = this_state->group_id;
return PAPI_OK;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* The following is for any POWER hardware */
/* Trims trailing blank space and line endings from a string (in place).
Returns pointer to start address */
static char *
trim_string( char *in )
{
int len, i = 0;
char *start = in;
if ( in == NULL )
return ( in );
len = strlen( in );
if ( len == 0 )
return ( in );
/* Trim right */
i = strlen( start ) - 1;
while ( i >= 0 ) {
if ( isblank( start[i] ) || ( start[i] == '\r' ) ||
( start[i] == '\n' ) )
start[i] = '\0';
else
break;
i--;
}
return ( start );
}
/* Routines to support an opaque native event table */
int
_aix_ntv_code_to_name( unsigned int EventCode, char *ntv_name, int len )
{
if ( ( EventCode & PAPI_NATIVE_AND_MASK ) >=
_aix_vector.cmp_info.num_native_events )
return ( PAPI_ENOEVNT );
strncpy( ntv_name,
native_name_map[EventCode & PAPI_NATIVE_AND_MASK].name, len );
trim_string( ntv_name );
if ( strlen( native_name_map[EventCode & PAPI_NATIVE_AND_MASK].name ) >
len - 1 )
return ( PAPI_EBUF );
return ( PAPI_OK );
}
int
_aix_ntv_code_to_descr( unsigned int EventCode, char *ntv_descr, int len )
{
if ( ( EventCode & PAPI_NATIVE_AND_MASK ) >=
_aix_vector.cmp_info.num_native_events )
return ( PAPI_ENOEVNT );
strncpy( ntv_descr,
native_table[native_name_map[EventCode & PAPI_NATIVE_AND_MASK].
index].description, len );
trim_string( ntv_descr );
if ( strlen
( native_table
[native_name_map[EventCode & PAPI_NATIVE_AND_MASK].index].
description ) > len - 1 )
return ( PAPI_EBUF );
return ( PAPI_OK );
}
int
_aix_ntv_code_to_bits( unsigned int EventCode, hwd_register_t * bits )
{
bits = &native_table[EventCode & PAPI_NATIVE_AND_MASK].resources; /* it is not right, different type */
return ( PAPI_OK );
}
/* this function return the next native event code.
modifier = PAPI_ENUM_FIRST returns first native event code
modifier = PAPI_ENUM_EVENTS returns next native event code
modifier = PAPI_NTV_ENUM_GROUPS return groups in which this
native event lives, in bits 16 - 23 of event code
terminating with PAPI_ENOEVNT at the end of the list.
function return value:
PAPI_OK successful, event code is valid
PAPI_EINVAL bad modifier
PAPI_ENOEVNT end of list or fail, event code is invalid
*/
int
_aix_ntv_enum_events( unsigned int *EventCode, int modifier )
{
if ( modifier == PAPI_ENUM_FIRST ) {
*EventCode = PAPI_NATIVE_MASK;
return ( PAPI_OK );
}
if ( modifier == PAPI_ENUM_EVENTS ) {
int index = *EventCode & PAPI_NATIVE_AND_MASK;
if ( native_table[index + 1].resources.selector ) {
*EventCode = *EventCode + 1;
return ( PAPI_OK );
} else
return ( PAPI_ENOEVNT );
} else if ( modifier == PAPI_NTV_ENUM_GROUPS ) {
#if defined(_POWER5) || defined(_POWER6)
unsigned int group =
( *EventCode & PAPI_NTV_GROUP_AND_MASK ) >> PAPI_NTV_GROUP_SHIFT;
int index = *EventCode & 0x000000FF;
int i;
unsigned int tmpg;
*EventCode = *EventCode & ( ~PAPI_NTV_GROUP_SHIFT );
for ( i = 0; i < GROUP_INTS; i++ ) {
tmpg = native_table[index].resources.group[i];
if ( group != 0 ) {
while ( ( ffs( tmpg ) + i * 32 ) <= group && tmpg != 0 )
tmpg = tmpg ^ ( 1 << ( ffs( tmpg ) - 1 ) );
}
if ( tmpg != 0 ) {
group = ffs( tmpg ) + i * 32;
*EventCode = *EventCode | ( group << PAPI_NTV_GROUP_SHIFT );
return ( PAPI_OK );
}
}
#endif
return ( PAPI_ENOEVNT );
} else
return ( PAPI_EINVAL );
}
static void
set_config( hwd_control_state_t * ptr, int arg1, int arg2 )
{
ptr->counter_cmd.events[arg1] = arg2;
}
static void
unset_config( hwd_control_state_t * ptr, int arg1 )
{
ptr->counter_cmd.events[arg1] = 0;
}
int
init_domain( )
{
int domain = 0;
domain = PAPI_DOM_USER | PAPI_DOM_KERNEL | PAPI_DOM_OTHER;
#ifdef PM_INITIALIZE
#ifdef _AIXVERSION_510
if ( pminfo.proc_feature.b.hypervisor ) {
domain |= PAPI_DOM_SUPERVISOR;
}
#endif
#endif
return ( domain );
}
static int
_aix_set_domain( hwd_control_state_t * this_state, int domain )
{
pm_mode_t *mode = &( this_state->counter_cmd.mode );
int did = 0;
mode->b.user = 0;
mode->b.kernel = 0;
if ( domain & PAPI_DOM_USER ) {
did++;
mode->b.user = 1;
}
if ( domain & PAPI_DOM_KERNEL ) {
did++;
mode->b.kernel = 1;
}
#ifdef PM_INITIALIZE
#ifdef _AIXVERSION_510
if ( ( domain & PAPI_DOM_SUPERVISOR ) && pminfo.proc_feature.b.hypervisor ) {
did++;
mode->b.hypervisor = 1;
}
#endif
#endif
if ( did )
return ( PAPI_OK );
else
return ( PAPI_EINVAL );
/*
switch (domain)
{
case PAPI_DOM_USER:
mode->b.user = 1;
mode->b.kernel = 0;
break;
case PAPI_DOM_KERNEL:
mode->b.user = 0;
mode->b.kernel = 1;
break;
case PAPI_DOM_ALL:
mode->b.user = 1;
mode->b.kernel = 1;
break;
default:
return(PAPI_EINVAL);
}
return(PAPI_OK);
*/
}
int
_aix_set_granularity( hwd_control_state_t * this_state, int domain )
{
pm_mode_t *mode = &( this_state->counter_cmd.mode );
switch ( domain ) {
case PAPI_GRN_THR:
mode->b.process = 0;
mode->b.proctree = 0;
break;
/* case PAPI_GRN_PROC:
mode->b.process = 1;
mode->b.proctree = 0;
break;
case PAPI_GRN_PROCG:
mode->b.process = 0;
mode->b.proctree = 1;
break; */
default:
return ( PAPI_EINVAL );
}
return ( PAPI_OK );
}
static int
set_default_domain( EventSetInfo_t * zero, int domain )
{
hwd_control_state_t *current_state = zero->ctl_state;
return ( _aix_set_domain( current_state, domain ) );
}
static int
set_default_granularity( EventSetInfo_t * zero, int granularity )
{
hwd_control_state_t *current_state = zero->ctl_state;
return ( _aix_set_granularity( current_state, granularity ) );
}
/* Initialize the system-specific settings */
/* Machine info structure. -1 is unused. */
int
_aix_mdi_init( )
{
int retval;
if ( ( retval = uname( &AixVer ) ) < 0 )
return ( PAPI_ESYS );
if ( AixVer.version[0] == '4' ) {
_papi_hwi_system_info.exe_info.address_info.text_start =
( caddr_t ) START_OF_TEXT;
_papi_hwi_system_info.exe_info.address_info.text_end =
( caddr_t ) END_OF_TEXT;
_papi_hwi_system_info.exe_info.address_info.data_start =
( caddr_t ) START_OF_DATA;
_papi_hwi_system_info.exe_info.address_info.data_end =
( caddr_t ) END_OF_DATA;
_papi_hwi_system_info.exe_info.address_info.bss_start =
( caddr_t ) START_OF_BSS;
_papi_hwi_system_info.exe_info.address_info.bss_end =
( caddr_t ) END_OF_BSS;
} else {
_aix_update_shlib_info( &_papi_hwi_system_info );
}
/* _papi_hwi_system_info.supports_64bit_counters = 1;
_papi_hwi_system_info.supports_real_usec = 1;
_papi_hwi_system_info.sub_info.fast_real_timer = 1;
_papi_hwi_system_info.sub_info->available_domains = init_domain();*/
return ( PAPI_OK );
}
static int
_aix_get_system_info( papi_mdi_t *mdi )
{
int retval;
/* pm_info_t pminfo; */
struct procsinfo psi = { 0 };
pid_t pid;
char maxargs[PAPI_HUGE_STR_LEN];
char pname[PAPI_HUGE_STR_LEN];
pid = getpid( );
if ( pid == -1 )
return ( PAPI_ESYS );
_papi_hwi_system_info.pid = pid;
psi.pi_pid = pid;
retval = getargs( &psi, sizeof ( psi ), maxargs, PAPI_HUGE_STR_LEN );
if ( retval == -1 )
return ( PAPI_ESYS );
if ( realpath( maxargs, pname ) )
strncpy( _papi_hwi_system_info.exe_info.fullname, pname,
PAPI_HUGE_STR_LEN );
else
strncpy( _papi_hwi_system_info.exe_info.fullname, maxargs,
PAPI_HUGE_STR_LEN );
strcpy( _papi_hwi_system_info.exe_info.address_info.name,
basename( maxargs ) );
#ifdef _POWER7
/* we pass PM_POWER7 for the same reasons as below (power6 case) */
retval = pm_initialize( PM_INIT_FLAGS , &pminfo, &pmgroups, PM_POWER7);
#elif defined(_POWER6)
/* problem with pm_initialize(): it cannot be called multiple times with
PM_CURRENT; use instead the actual proc type - here PM_POWER6 -
and multiple invocations are no longer a problem */
retval = pm_initialize( PM_INIT_FLAGS, &pminfo, &pmgroups, PM_POWER6 );
#else
#ifdef _AIXVERSION_510
#ifdef PM_INITIALIZE
SUBDBG( "Calling AIX 5 version of pm_initialize...\n" );
/*#if defined(_POWER5)
retval = pm_initialize(PM_INIT_FLAGS, &pminfo, &pmgroups, PM_POWER5);
#endif*/
retval = pm_initialize( PM_INIT_FLAGS, &pminfo, &pmgroups, PM_CURRENT );
#else
SUBDBG( "Calling AIX 5 version of pm_init...\n" );
retval = pm_init( PM_INIT_FLAGS, &pminfo, &pmgroups );
#endif
#else
SUBDBG( "Calling AIX 4 version of pm_init...\n" );
retval = pm_init( PM_INIT_FLAGS, &pminfo );
#endif
#endif
SUBDBG( "...Back from pm_init\n" );
if ( retval > 0 )
return ( retval );
_aix_mdi_init( );
_papi_hwi_system_info.hw_info.nnodes = 1;
_papi_hwi_system_info.hw_info.ncpu = _system_configuration.ncpus;
_papi_hwi_system_info.hw_info.totalcpus =
_papi_hwi_system_info.hw_info.ncpu *
_papi_hwi_system_info.hw_info.nnodes;
_papi_hwi_system_info.hw_info.vendor = -1;
strcpy( _papi_hwi_system_info.hw_info.vendor_string, "IBM" );
_papi_hwi_system_info.hw_info.model = _system_configuration.implementation;
strcpy( _papi_hwi_system_info.hw_info.model_string, pminfo.proc_name );
_papi_hwi_system_info.hw_info.revision =
( float ) _system_configuration.version;
_papi_hwi_system_info.hw_info.mhz = ( float ) ( pm_cycles( ) / 1000000.0 );
_papi_hwi_system_info.hw_info.cpu_max_mhz=_papi_hwi_system_info.hw_info.mhz;
_papi_hwi_system_info.hw_info.cpu_min_mhz=_papi_hwi_system_info.hw_info.mhz;
/* _papi_hwi_system_info.num_gp_cntrs = pminfo.maxpmcs;*/
_aix_vector.cmp_info.num_cntrs = pminfo.maxpmcs;
_aix_vector.cmp_info.num_mpx_cntrs = MAX_MPX_COUNTERS; // pminfo.maxpmcs,
_aix_vector.cmp_info.available_granularities = PAPI_GRN_THR;
/* This field doesn't appear to exist in the PAPI 3.0 structure
_papi_hwi_system_info.cpunum = mycpu();
*/
_aix_vector.cmp_info.available_domains = init_domain( );
return PAPI_OK;
}
/* Low level functions, should not handle errors, just return codes. */
/* At init time, the higher level library should always allocate and
reserve EventSet zero. */
long long
_aix_get_real_usec( void )
{
timebasestruct_t t;
long long retval;
read_real_time( &t, TIMEBASE_SZ );
time_base_to_time( &t, TIMEBASE_SZ );
retval = ( t.tb_high * 1000000 ) + t.tb_low / 1000;
return ( retval );
}
long long
_aix_get_real_cycles( void )
{
return ( _aix_get_real_usec( ) *
( long long ) _papi_hwi_system_info.hw_info.cpu_max_mhz );
}
long long
_aix_get_virt_usec( void )
{
long long retval;
struct tms buffer;
times( &buffer );
SUBDBG( "user %d system %d\n", ( int ) buffer.tms_utime,
( int ) buffer.tms_stime );
retval =
( long long ) ( ( buffer.tms_utime + buffer.tms_stime ) *
( 1000000 / CLK_TCK ) );
return ( retval );
}
static void
_aix_lock_init( void )
{
int i;
for ( i = 0; i < PAPI_MAX_LOCK; i++ )
lock[i] = ( int * ) ( lock_var + i );
}
int
_aix_shutdown_thread( hwd_context_t * ctx )
{
return ( PAPI_OK );
}
int
_aix_init_component( int cidx )
{
int retval = PAPI_OK, procidx;
/* Fill in what we can of the papi_system_info. */
retval = _papi_os_vector.get_system_info( &_papi_hwi_system_info );
if ( retval )
return ( retval );
/* Setup memory info */
retval = _papi_os_vector.get_memory_info( &_papi_hwi_system_info.hw_info, 0 );
if ( retval )
return ( retval );
SUBDBG( "Found %d %s %s CPUs at %d Mhz.\n",
_papi_hwi_system_info.hw_info.totalcpus,
_papi_hwi_system_info.hw_info.vendor_string,
_papi_hwi_system_info.hw_info.model_string,
_papi_hwi_system_info.hw_info.cpu_max_mhz );
_aix_vector.cmp_info.CmpIdx = cidx;
_aix_vector.cmp_info.num_native_events = aix_ppc64_setup_native_table( );
procidx = pm_get_procindex( );
switch ( procidx ) {
case PM_POWER5:
_papi_load_preset_table( "POWER5", 0, cidx );
break;
case PM_POWER5_II:
_papi_load_preset_table( "POWER5+", 0, cidx );
break;
case PM_POWER6:
_papi_load_preset_table( "POWER6", 0, cidx );
break;
case PM_PowerPC970:
_papi_load_preset_table( "PPC970", 0, cidx );
break;
case PM_POWER7:
_papi_load_preset_table( "POWER7", 0, cidx );
break;
default:
fprintf( stderr, "%s is not supported!\n", pminfo.proc_name );
return PAPI_ENOIMPL;
}
_aix_lock_init( );
return ( retval );
}
int
_aix_init_thread( hwd_context_t * context )
{
int retval;
/* Initialize our global control state. */
_aix_init_control_state( &context->cntrl );
}
/* Go from highest counter to lowest counter. Why? Because there are usually
more counters on #1, so we try the least probable first. */
static int
get_avail_hwcntr_bits( int cntr_avail_bits )
{
int tmp = 0, i = 1 << ( POWER_MAX_COUNTERS - 1 );
while ( i ) {
tmp = i & cntr_avail_bits;
if ( tmp )
return ( tmp );
i = i >> 1;
}
return ( 0 );
}
static void
set_hwcntr_codes( int selector, unsigned char *from, int *to )
{
int useme, i;
for ( i = 0; i < _aix_vector.cmp_info.num_cntrs; i++ ) {
useme = ( 1 << i ) & selector;
if ( useme ) {
to[i] = from[i];
}
}
}
#ifdef DEBUG
void
dump_cmd( pm_prog_t * t )
{
SUBDBG( "mode.b.threshold %d\n", t->mode.b.threshold );
SUBDBG( "mode.b.spare %d\n", t->mode.b.spare );
SUBDBG( "mode.b.process %d\n", t->mode.b.process );
SUBDBG( "mode.b.kernel %d\n", t->mode.b.kernel );
SUBDBG( "mode.b.user %d\n", t->mode.b.user );
SUBDBG( "mode.b.count %d\n", t->mode.b.count );
SUBDBG( "mode.b.proctree %d\n", t->mode.b.proctree );
SUBDBG( "events[0] %d\n", t->events[0] );
SUBDBG( "events[1] %d\n", t->events[1] );
SUBDBG( "events[2] %d\n", t->events[2] );
SUBDBG( "events[3] %d\n", t->events[3] );
SUBDBG( "events[4] %d\n", t->events[4] );
SUBDBG( "events[5] %d\n", t->events[5] );
SUBDBG( "events[6] %d\n", t->events[6] );
SUBDBG( "events[7] %d\n", t->events[7] );
SUBDBG( "reserved %d\n", t->reserved );
}
void
dump_data( long long *vals )
{
int i;
for ( i = 0; i < MAX_COUNTERS; i++ ) {
SUBDBG( "counter[%d] = %lld\n", i, vals[i] );
}
}
#endif
int
_aix_reset( hwd_context_t * ESI, hwd_control_state_t * zero )
{
int retval = pm_reset_data_mythread( );
if ( retval > 0 ) {
if ( _papi_hwi_error_level != PAPI_QUIET )
pm_error( "PAPI Error: pm_reset_data_mythread", retval );
return ( retval );
}
return ( PAPI_OK );
}
int
_aix_read( hwd_context_t * ctx, hwd_control_state_t * spc,
long long **vals, int flags )
{
int retval;
retval = pm_get_data_mythread( &spc->state );
if ( retval > 0 ) {
if ( _papi_hwi_error_level != PAPI_QUIET )
pm_error( "PAPI Error: pm_get_data_mythread", retval );
return ( retval );
}
*vals = spc->state.accu;
#ifdef DEBUG
if ( ISLEVEL( DEBUG_SUBSTRATE ) )
dump_data( *vals );
#endif
return ( PAPI_OK );
}
static int
round_requested_ns( int ns )
{
if ( ns <= _papi_os_info.itimer_res_ns ) {
return _papi_os_info.itimer_res_ns;
} else {
int leftover_ns = ns % _papi_os_info.itimer_res_ns;
return ( ns - leftover_ns + _papi_os_info.itimer_res_ns );
}
}
int
_aix_ctl( hwd_context_t * ctx, int code, _papi_int_option_t * option )
{
switch ( code ) {
/* I don't understand what it means to set the default domain
case PAPI_DEFDOM:
return(set_default_domain(zero, option->domain.domain));
*/
case PAPI_DOMAIN:
return ( _aix_set_domain
( option->domain.ESI->ctl_state, option->domain.domain ) );
/* I don't understand what it means to set the default granularity
case PAPI_DEFGRN:
return(set_default_granularity(zero, option->granularity.granularity));
*/
case PAPI_GRANUL:
return ( _aix_set_granularity
( option->domain.ESI->ctl_state,
option->granularity.granularity ) );
#if 0
case PAPI_INHERIT:
return ( set_inherit( option->inherit.inherit ) );
#endif
case PAPI_DEF_ITIMER:
{
/* flags are currently ignored, eventually the flags will be able
to specify whether or not we use POSIX itimers (clock_gettimer) */
if ( ( option->itimer.itimer_num == ITIMER_REAL ) &&
( option->itimer.itimer_sig != SIGALRM ) )
return PAPI_EINVAL;
if ( ( option->itimer.itimer_num == ITIMER_VIRTUAL ) &&
( option->itimer.itimer_sig != SIGVTALRM ) )
return PAPI_EINVAL;
if ( ( option->itimer.itimer_num == ITIMER_PROF ) &&
( option->itimer.itimer_sig != SIGPROF ) )
return PAPI_EINVAL;
if ( option->itimer.ns > 0 )
option->itimer.ns = round_requested_ns( option->itimer.ns );
/* At this point, we assume the user knows what he or
she is doing, they maybe doing something arch specific */
return PAPI_OK;
}
case PAPI_DEF_MPX_NS:
{
option->multiplex.ns = round_requested_ns( option->multiplex.ns );
return ( PAPI_OK );
}
case PAPI_DEF_ITIMER_NS:
{
option->itimer.ns = round_requested_ns( option->itimer.ns );
return ( PAPI_OK );
}
default:
return ( PAPI_ENOSUPP );
}
}
void
_aix_dispatch_timer( int signal, siginfo_t * si, void *i )
{
_papi_hwi_context_t ctx;
ThreadInfo_t *t = NULL;
caddr_t address;
ctx.si = si;
ctx.ucontext = ( hwd_ucontext_t * ) i;
address = ( caddr_t ) GET_OVERFLOW_ADDRESS( ( &ctx ) );
_papi_hwi_dispatch_overflow_signal( ( void * ) &ctx, address, NULL, 0, 0,
&t, _aix_vector.cmp_info.CmpIdx );
}
int
_aix_set_overflow( EventSetInfo_t * ESI, int EventIndex, int threshold )
{
hwd_control_state_t *this_state = ESI->ctl_state;
return ( PAPI_OK );
}
void *
_aix_get_overflow_address( void *context )
{
void *location;
struct sigcontext *info = ( struct sigcontext * ) context;
location = ( void * ) info->sc_jmpbuf.jmp_context.iar;
return ( location );
}
/* Copy the current control_state into the new thread context */
/*int _papi_hwd_start(EventSetInfo_t *ESI, EventSetInfo_t *zero)*/
int
_aix_start( hwd_context_t * ctx, hwd_control_state_t * cntrl )
{
int i, retval;
hwd_control_state_t *current_state = &ctx->cntrl;
/* If we are nested, merge the global counter structure
with the current eventset */
SUBDBG( "Start\n" );
/* Copy the global counter structure to the current eventset */
SUBDBG( "Copying states\n" );
memcpy( current_state, cntrl, sizeof ( hwd_control_state_t ) );
retval = pm_set_program_mythread( ¤t_state->counter_cmd );
if ( retval > 0 ) {
if ( retval == 13 ) {
retval = pm_delete_program_mythread( );
if ( retval > 0 ) {
if ( _papi_hwi_error_level != PAPI_QUIET )
pm_error( "PAPI Error: pm_delete_program_mythread",
retval );
return ( retval );
}
retval = pm_set_program_mythread( ¤t_state->counter_cmd );
if ( retval > 0 ) {
if ( _papi_hwi_error_level != PAPI_QUIET )
pm_error( "PAPI Error: pm_set_program_mythread", retval );
return ( retval );
}
} else {
if ( _papi_hwi_error_level != PAPI_QUIET )
pm_error( "PAPI Error: pm_set_program_mythread", retval );
return ( retval );
}
}
/* Set up the new merged control structure */
#if 0
dump_cmd( ¤t_state->counter_cmd );
#endif
/* Start the counters */
retval = pm_start_mythread( );
if ( retval > 0 ) {
if ( _papi_hwi_error_level != PAPI_QUIET )
pm_error( "pm_start_mythread()", retval );
return ( retval );
}
return ( PAPI_OK );
}
int
_aix_stop( hwd_context_t * ctx, hwd_control_state_t * cntrl )
{
int retval;
retval = pm_stop_mythread( );
if ( retval > 0 ) {
if ( _papi_hwi_error_level != PAPI_QUIET )
pm_error( "pm_stop_mythread()", retval );
return ( retval );
}
retval = pm_delete_program_mythread( );
if ( retval > 0 ) {
if ( _papi_hwi_error_level != PAPI_QUIET )
pm_error( "pm_delete_program_mythread()", retval );
return ( retval );
}
return ( PAPI_OK );
}
int
_aix_update_shlib_info( papi_mdi_t *mdi )
{
#if ( ( defined( _AIXVERSION_510) || defined(_AIXVERSION_520)))
struct ma_msg_s
{
long flag;
char *name;
} ma_msgs[] = {
{
MA_MAINEXEC, "MAINEXEC"}, {
MA_KERNTEXT, "KERNTEXT"}, {
MA_READ, "READ"}, {
MA_WRITE, "WRITE"}, {
MA_EXEC, "EXEC"}, {
MA_SHARED, "SHARED"}, {
MA_BREAK, "BREAK"}, {
MA_STACK, "STACK"},};
char fname[80], name[PAPI_HUGE_STR_LEN];
prmap_t newp;
int count, t_index, retval, i, j, not_first_flag_bit;
FILE *map_f;
void *vaddr;
prmap_t *tmp1 = NULL;
PAPI_address_map_t *tmp2 = NULL;
sprintf( fname, "/proc/%d/map", getpid( ) );
map_f = fopen( fname, "r" );
if ( !map_f ) {
PAPIERROR( "fopen(%s) returned < 0", fname );
return ( PAPI_OK );
}
/* count the entries we need */
count = 0;
t_index = 0;
while ( ( retval = fread( &newp, sizeof ( prmap_t ), 1, map_f ) ) > 0 ) {
if ( newp.pr_pathoff > 0 && newp.pr_mapname[0] != '\0' ) {
if ( newp.pr_mflags & MA_STACK )
continue;
count++;
SUBDBG( "count=%d offset=%ld map=%s\n", count,
newp.pr_pathoff, newp.pr_mapname );
if ( ( newp.pr_mflags & MA_READ ) && ( newp.pr_mflags & MA_EXEC ) )
t_index++;
}
}
rewind( map_f );
tmp1 = ( prmap_t * ) papi_calloc( ( count + 1 ), sizeof ( prmap_t ) );
if ( tmp1 == NULL )
return ( PAPI_ENOMEM );
tmp2 =
( PAPI_address_map_t * ) papi_calloc( t_index,
sizeof ( PAPI_address_map_t ) );
if ( tmp2 == NULL )
return ( PAPI_ENOMEM );
i = 0;
t_index = -1;
while ( ( retval = fread( &tmp1[i], sizeof ( prmap_t ), 1, map_f ) ) > 0 ) {
if ( tmp1[i].pr_pathoff > 0 && tmp1[i].pr_mapname[0] != '\0' )
if ( !( tmp1[i].pr_mflags & MA_STACK ) )
i++;
}
for ( i = 0; i < count; i++ ) {
char c;
int cc = 0;
retval = fseek( map_f, tmp1[i].pr_pathoff, SEEK_SET );
if ( retval != 0 )
return ( PAPI_ESYS );
while ( fscanf( map_f, "%c", &c ) != EOF ) {
name[cc] = c;
/* how many char are hold in /proc/xxxx/map */
cc++;
if ( c == '\0' )
break;
}
/* currently /proc/xxxx/map file holds only 33 char per line (incl NULL char);
* if executable name > 32 char, compare first 32 char only */
if ( strncmp( _papi_hwi_system_info.exe_info.address_info.name,
basename( name ), cc - 1 ) == 0 ) {
if ( strlen( _papi_hwi_system_info.exe_info.address_info.name ) !=
cc - 1 )
PAPIERROR
( "executable name too long (%d char). Match of first %d char only",
strlen( _papi_hwi_system_info.exe_info.address_info.
name ), cc - 1 );
if ( tmp1[i].pr_mflags & MA_READ ) {
if ( tmp1[i].pr_mflags & MA_EXEC ) {
_papi_hwi_system_info.exe_info.address_info.
text_start = ( caddr_t ) tmp1[i].pr_vaddr;
_papi_hwi_system_info.exe_info.address_info.
text_end =
( caddr_t ) ( tmp1[i].pr_vaddr + tmp1[i].pr_size );
} else if ( tmp1[i].pr_mflags & MA_WRITE ) {
_papi_hwi_system_info.exe_info.address_info.
data_start = ( caddr_t ) tmp1[i].pr_vaddr;
_papi_hwi_system_info.exe_info.address_info.
data_end =
( caddr_t ) ( tmp1[i].pr_vaddr + tmp1[i].pr_size );
}
}
} else {
if ( ( _papi_hwi_system_info.exe_info.address_info.text_start == 0 )
&& ( _papi_hwi_system_info.exe_info.address_info.text_end ==
0 ) &&
( _papi_hwi_system_info.exe_info.address_info.data_start == 0 )
&& ( _papi_hwi_system_info.exe_info.address_info.data_end ==
0 ) )
PAPIERROR( "executable name not recognized" );
if ( tmp1[i].pr_mflags & MA_READ ) {
if ( tmp1[i].pr_mflags & MA_EXEC ) {
t_index++;
tmp2[t_index].text_start = ( caddr_t ) tmp1[i].pr_vaddr;
tmp2[t_index].text_end =
( caddr_t ) ( tmp1[i].pr_vaddr + tmp1[i].pr_size );
strncpy( tmp2[t_index].name, name, PAPI_MAX_STR_LEN );
} else if ( tmp1[i].pr_mflags & MA_WRITE ) {
tmp2[t_index].data_start = ( caddr_t ) tmp1[i].pr_vaddr;
tmp2[t_index].data_end =
( caddr_t ) ( tmp1[i].pr_vaddr + tmp1[i].pr_size );
}
}
}
}
fclose( map_f );
if ( _papi_hwi_system_info.shlib_info.map )
papi_free( _papi_hwi_system_info.shlib_info.map );
_papi_hwi_system_info.shlib_info.map = tmp2;
_papi_hwi_system_info.shlib_info.count = t_index + 1;
papi_free( tmp1 );
return PAPI_OK;
#else
return PAPI_ENOIMPL;
#endif
}
int
_aix_ntv_name_to_code( const char *name, unsigned int *evtcode )
{
int i;
for ( i = 0; i < PAPI_MAX_NATIVE_EVENTS; i++ )
if ( strcmp( name, native_name_map[i].name ) == 0 ) {
*evtcode = native_name_map[i].index | PAPI_NATIVE_MASK;
return PAPI_OK;
}
return PAPI_ENOEVNT;
}
PAPI_os_info_t _papi_os_info;
int
_papi_hwi_init_os(void) {
struct utsname uname_buffer;
uname(&uname_buffer);
strncpy(_papi_os_info.name,uname_buffer.sysname,PAPI_MAX_STR_LEN);
strncpy(_papi_os_info.version,uname_buffer.release,PAPI_MAX_STR_LEN);
_papi_os_info.itimer_sig = PAPI_INT_MPX_SIGNAL;
_papi_os_info.itimer_num = PAPI_INT_ITIMER;
_papi_os_info.itimer_res_ns = 1;
_papi_os_info.itimer_ns = 1000 * PAPI_INT_MPX_DEF_US;
return PAPI_OK;
}
papi_vector_t _aix_vector = {
.cmp_info = {
/* default component information (unspecified values are initialized to 0) */
.name = "aix",
.description = "AIX pmapi CPU counters",
.default_domain = PAPI_DOM_USER,
.available_domains = PAPI_DOM_USER | PAPI_DOM_KERNEL,
.default_granularity = PAPI_GRN_THR,
.available_granularities = PAPI_GRN_THR,
.hardware_intr_sig = PAPI_INT_SIGNAL,
/* component specific cmp_info initializations */
.fast_real_timer = 1,
.fast_virtual_timer = 1,
.attach = 1,
.attach_must_ptrace = 1,
.cntr_umasks = 1,
}
,
/* sizes of framework-opaque component-private structures
these are remapped in pmapi_ppc64.h, ppc64_events.h */
.size = {
.context = sizeof ( hwd_context_t ),
.control_state = sizeof ( hwd_control_state_t ),
.reg_value = sizeof ( hwd_register_t ),
.reg_alloc = sizeof ( hwd_reg_alloc_t ),
}
,
/* function pointers in this component */
.init_control_state = _aix_init_control_state,
.start = _aix_start,
.stop = _aix_stop,
.read = _aix_read,
.allocate_registers = _aix_allocate_registers,
.update_control_state = _aix_update_control_state,
.set_domain = _aix_set_domain,
.reset = _aix_reset,
.set_overflow = _aix_set_overflow,
/* .stop_profiling = _aix_stop_profiling, */
.ntv_enum_events = _aix_ntv_enum_events,
.ntv_name_to_code = _aix_ntv_name_to_code,
.ntv_code_to_name = _aix_ntv_code_to_name,
.ntv_code_to_descr = _aix_ntv_code_to_descr,
.ntv_code_to_bits = _aix_ntv_code_to_bits,
.init_component = _aix_init_component,
.ctl = _aix_ctl,
.dispatch_timer = _aix_dispatch_timer,
.init_thread = _aix_init_thread,
.shutdown_thread = _aix_shutdown_thread,
};
papi_os_vector_t _papi_os_vector = {
.get_memory_info = _aix_get_memory_info,
.get_dmem_info = _aix_get_dmem_info,
.get_real_usec = _aix_get_real_usec,
.get_real_cycles = _aix_get_real_cycles,
.get_virt_usec = _aix_get_virt_usec,
.update_shlib_info = _aix_update_shlib_info,
.get_system_info = _aix_get_system_info,
};