/* 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 #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, };