/* * File: solaris-ultra.c * Author: Philip Mucci * mucci@cs.utk.edu * Mods: Kevin London * london@cs.utk.edu * Mods: Min Zhou * min@cs.utk.edu * Mods: Larry Meadows(helped us to build the native table dynamically) * Mods: Brian Sheely * bsheely@eecs.utk.edu * Mods: Vince Weaver * vweaver1@eecs.utk.edu */ /* to understand this program, first you should read the user's manual about UltraSparc II and UltraSparc III, then the man pages about cpc_take_sample(cpc_event_t *event) */ #include "papi.h" #include "papi_internal.h" #include "papi_vector.h" #include "papi_memory.h" #include #include "solaris-common.h" #include "solaris-memory.h" #ifdef CPC_ULTRA3_I #define LASTULTRA3 CPC_ULTRA3_I #else #define LASTULTRA3 CPC_ULTRA3_PLUS #endif #define MAX_ENAME 40 static void action( void *arg, int regno, const char *name, uint8_t bits ); /* Probably could dispense with this and just use native_table */ typedef struct ctr_info { char *name; /* Counter name */ int bits[2]; /* bits for register */ int bitmask; /* 1 = pic0; 2 = pic1; 3 = both */ } ctr_info_t; typedef struct einfo { unsigned int papi_event; char *event_str; } einfo_t; static einfo_t us3info[] = { {PAPI_FP_INS, "FA_pipe_completion+FM_pipe_completion"}, {PAPI_FAD_INS, "FA_pipe_completion"}, {PAPI_FML_INS, "FM_pipe_completion"}, {PAPI_TLB_IM, "ITLB_miss"}, {PAPI_TLB_DM, "DTLB_miss"}, {PAPI_TOT_CYC, "Cycle_cnt"}, {PAPI_TOT_IIS, "Instr_cnt"}, {PAPI_TOT_INS, "Instr_cnt"}, {PAPI_L2_TCM, "EC_misses"}, {PAPI_L2_ICM, "EC_ic_miss"}, {PAPI_L1_ICM, "IC_miss"}, {PAPI_L1_LDM, "DC_rd_miss"}, {PAPI_L1_STM, "DC_wr_miss"}, {PAPI_L2_LDM, "EC_rd_miss"}, {PAPI_BR_MSP, "IU_Stat_Br_miss_taken+IU_Stat_Br_miss_untaken"}, {PAPI_L1_DCR, "DC_rd"}, {PAPI_L1_DCW, "DC_wr"}, {PAPI_L1_ICH, "IC_ref-IC_miss"}, /* Is this really hits only? */ {PAPI_L1_ICA, "IC_ref"}, /* Ditto? */ {PAPI_L2_TCH, "EC_ref-EC_misses"}, {PAPI_L2_TCA, "EC_ref"}, }; static einfo_t us2info[] = { {PAPI_L1_ICM, "IC_ref-IC_hit"}, {PAPI_L2_TCM, "EC_ref-EC_hit"}, {PAPI_CA_SNP, "EC_snoop_cb"}, {PAPI_CA_INV, "EC_snoop_inv"}, {PAPI_L1_LDM, "DC_rd-DC_rd_hit"}, {PAPI_L1_STM, "DC_wr-DC_wr_hit"}, {PAPI_L2_LDM, "EC_rd_miss"}, {PAPI_BR_MSP, "Dispatch0_mispred"}, {PAPI_TOT_IIS, "Instr_cnt"}, {PAPI_TOT_INS, "Instr_cnt"}, {PAPI_LD_INS, "DC_rd"}, {PAPI_SR_INS, "DC_wr"}, {PAPI_TOT_CYC, "Cycle_cnt"}, {PAPI_L1_DCR, "DC_rd"}, {PAPI_L1_DCW, "DC_wr"}, {PAPI_L1_ICH, "IC_hit"}, {PAPI_L2_ICH, "EC_ic_hit"}, {PAPI_L1_ICA, "IC_ref"}, {PAPI_L2_TCH, "EC_hit"}, {PAPI_L2_TCA, "EC_ref"}, }; papi_vector_t _solaris_vector; static native_info_t *native_table; static hwi_search_t *preset_table; static struct ctr_info *ctrs; static int nctrs; static int build_tables( void ); static void add_preset( hwi_search_t * tab, int *np, einfo_t e ); /* Globals used to access the counter registers. */ static int cpuver; static int pcr_shift[2]; hwi_search_t *preset_search_map; #ifdef DEBUG static void dump_cmd( papi_cpc_event_t * t ) { SUBDBG( "cpc_event_t.ce_cpuver %d\n", t->cmd.ce_cpuver ); SUBDBG( "ce_tick %llu\n", t->cmd.ce_tick ); SUBDBG( "ce_pic[0] %llu ce_pic[1] %llu\n", t->cmd.ce_pic[0], t->cmd.ce_pic[1] ); SUBDBG( "ce_pcr %#llx\n", t->cmd.ce_pcr ); SUBDBG( "flags %#x\n", t->flags ); } #endif static void dispatch_emt( int signal, siginfo_t * sip, void *arg ) { int event_counter; _papi_hwi_context_t ctx; caddr_t address; ctx.si = sip; ctx.ucontext = arg; SUBDBG( "%d, %p, %p\n", signal, sip, arg ); if ( sip->si_code == EMT_CPCOVF ) { papi_cpc_event_t *sample; EventSetInfo_t *ESI; ThreadInfo_t *thread = NULL; int t, overflow_vector, readvalue; thread = _papi_hwi_lookup_thread( 0 ); ESI = ( EventSetInfo_t * ) thread->running_eventset; int cidx = ESI->CmpIdx; if ( ( ESI == NULL ) || ( ( ESI->state & PAPI_OVERFLOWING ) == 0 ) ) { OVFDBG( "Either no eventset or eventset not set to overflow.\n" ); return; } if ( ESI->master != thread ) { PAPIERROR ( "eventset->thread %%lx vs. current thread %#lx mismatch", ESI->master, thread ); return; } event_counter = ESI->overflow.event_counter; sample = &( ESI->ctl_state->counter_cmd ); /* GROSS! This is a hack to 'push' the correct values back into the hardware, such that when PAPI handles the overflow and reads the values, it gets the correct ones. */ /* Find which HW counter overflowed */ if ( ESI->EventInfoArray[ESI->overflow.EventIndex[0]].pos[0] == 0 ) t = 0; else t = 1; if ( cpc_take_sample( &sample->cmd ) == -1 ) return; if ( event_counter == 1 ) { /* only one event is set to be the overflow monitor */ /* generate the overflow vector */ overflow_vector = 1 << t; /* reset the threshold */ sample->cmd.ce_pic[t] = UINT64_MAX - ESI->overflow.threshold[0]; } else { /* two events are set to be the overflow monitors */ overflow_vector = 0; readvalue = sample->cmd.ce_pic[0]; if ( readvalue >= 0 ) { /* the first counter overflowed */ /* generate the overflow vector */ overflow_vector = 1; /* reset the threshold */ if ( t == 0 ) sample->cmd.ce_pic[0] = UINT64_MAX - ESI->overflow.threshold[0]; else sample->cmd.ce_pic[0] = UINT64_MAX - ESI->overflow.threshold[1]; } readvalue = sample->cmd.ce_pic[1]; if ( readvalue >= 0 ) { /* the second counter overflowed */ /* generate the overflow vector */ overflow_vector ^= 1 << 1; /* reset the threshold */ if ( t == 0 ) sample->cmd.ce_pic[1] = UINT64_MAX - ESI->overflow.threshold[1]; else sample->cmd.ce_pic[1] = UINT64_MAX - ESI->overflow.threshold[0]; } SUBDBG( "overflow_vector, = %d\n", overflow_vector ); /* something is wrong here */ if ( overflow_vector == 0 ) { PAPIERROR( "BUG! overflow_vector is 0, dropping interrupt" ); return; } } /* Call the regular overflow function in extras.c */ if ( thread->running_eventset[cidx]->overflow. flags & PAPI_OVERFLOW_FORCE_SW ) { address = GET_OVERFLOW_ADDRESS(ctx); _papi_hwi_dispatch_overflow_signal( ( void * ) &ctx, address, NULL, overflow_vector, 0, &thread, cidx ); } else { PAPIERROR( "Additional implementation needed in dispatch_emt!" ); } #if DEBUG dump_cmd( sample ); #endif /* push back the correct values and start counting again */ if ( cpc_bind_event( &sample->cmd, sample->flags ) == -1 ) return; } else { SUBDBG( "dispatch_emt() dropped, si_code = %d\n", sip->si_code ); return; } } static int scan_prtconf( char *cpuname, int len_cpuname, int *hz, int *ver ) { /* This code courtesy of our friends in Germany. Thanks Rudolph Berrendorf! */ /* See the PCL home page for the German version of PAPI. */ /* Modified by Nils Smeds, all new bugs are my fault */ /* The routine now looks for the first "Node" with the following: */ /* "device_type" = 'cpu' */ /* "name" = (Any value) */ /* "sparc-version" = (Any value) */ /* "clock-frequency" = (Any value) */ int ihz, version; char line[256], cmd[80], name[256]; FILE *f = NULL; char cmd_line[PAPI_HUGE_STR_LEN + PAPI_HUGE_STR_LEN], fname[L_tmpnam]; unsigned int matched; /*??? system call takes very long */ /* get system configuration and put output into file */ tmpnam( fname ); SUBDBG( "Temporary name %s\n", fname ); sprintf( cmd_line, "/usr/sbin/prtconf -vp > %s", fname ); SUBDBG( "Executing %s\n", cmd_line ); if ( system( cmd_line ) == -1 ) { remove( fname ); return -1; } f = fopen( fname, "r" ); /* open output file */ if ( f == NULL ) { remove( fname ); return -1; } /* ignore all lines until we reach something with a sparc line */ matched = 0x0; ihz = -1; while ( fgets( line, 256, f ) != NULL ) { /*SUBDBG(">>> %s",line); */ if ( ( sscanf( line, "%s", cmd ) == 1 ) && strstr( line, "Node 0x" ) ) { matched = 0x0; /*SUBDBG("Found 'Node' -- search reset. (%#2.2x)\n",matched); */ } else { if ( strstr( cmd, "device_type:" ) && strstr( line, "'cpu'" ) ) { matched |= 0x1; SUBDBG( "Found 'cpu'. (%#2.2x)\n", matched ); } else if ( !strcmp( cmd, "sparc-version:" ) && ( sscanf( line, "%s %#x", cmd, &version ) == 2 ) ) { matched |= 0x2; SUBDBG( "Found version=%d. (%#2.2x)\n", version, matched ); } else if ( !strcmp( cmd, "clock-frequency:" ) && ( sscanf( line, "%s %#x", cmd, &ihz ) == 2 ) ) { matched |= 0x4; SUBDBG( "Found ihz=%d. (%#2.2x)\n", ihz, matched ); } else if ( !strcmp( cmd, "name:" ) && ( sscanf( line, "%s %s", cmd, name ) == 2 ) ) { matched |= 0x8; SUBDBG( "Found name: %s. (%#2.2x)\n", name, matched ); } } if ( ( matched & 0xF ) == 0xF ) break; } SUBDBG( "Parsing found name=%s, speed=%dHz, version=%d\n", name, ihz, version ); if ( matched ^ 0x0F ) ihz = -1; else { *hz = ( float ) ihz; *ver = version; strncpy( cpuname, name, len_cpuname ); } return ihz; /* End stolen code */ } int _ultra_set_domain( hwd_control_state_t * this_state, int domain ) { papi_cpc_event_t *command = &this_state->counter_cmd; cpc_event_t *event = &command->cmd; uint64_t pcr = event->ce_pcr; int did = 0; pcr = pcr | 0x7; pcr = pcr ^ 0x7; if ( domain & PAPI_DOM_USER ) { pcr = pcr | 1 << CPC_ULTRA_PCR_USR; did = 1; } if ( domain & PAPI_DOM_KERNEL ) { pcr = pcr | 1 << CPC_ULTRA_PCR_SYS; did = 1; } /* DOMAIN ERROR */ if ( !did ) { return ( PAPI_EINVAL ); } event->ce_pcr = pcr; return ( PAPI_OK ); } static int set_granularity( hwd_control_state_t * this_state, int domain ) { switch ( domain ) { case PAPI_GRN_PROCG: case PAPI_GRN_SYS: case PAPI_GRN_SYS_CPU: case PAPI_GRN_PROC: return PAPI_ECMP; case PAPI_GRN_THR: break; default: return ( PAPI_EINVAL ); } return ( PAPI_OK ); } /* Utility functions */ /* This is a wrapper arount fprintf(stderr,...) for cpc_walk_events() */ void print_walk_names( void *arg, int regno, const char *name, uint8_t bits ) { SUBDBG( arg, regno, name, bits ); } static int build_tables( void ) { int i; int regno; int npic; einfo_t *ep; int n; int npresets; npic = cpc_getnpic( cpuver ); nctrs = 0; for ( regno = 0; regno < npic; ++regno ) { cpc_walk_names( cpuver, regno, 0, action ); } SUBDBG( "%d counters\n", nctrs ); if ( ( ctrs = papi_malloc( nctrs * sizeof ( struct ctr_info ) ) ) == 0 ) { return PAPI_ENOMEM; } nctrs = 0; for ( regno = 0; regno < npic; ++regno ) { cpc_walk_names( cpuver, regno, ( void * ) 1, action ); } SUBDBG( "%d counters\n", nctrs ); #if DEBUG if ( ISLEVEL( DEBUG_SUBSTRATE ) ) { for ( i = 0; i < nctrs; ++i ) { SUBDBG( "%s: bits (%#x,%#x) pics %#x\n", ctrs[i].name, ctrs[i].bits[0], ctrs[i].bits[1], ctrs[i].bitmask ); } } #endif /* Build the native event table */ if ( ( native_table = papi_malloc( nctrs * sizeof ( native_info_t ) ) ) == 0 ) { papi_free( ctrs ); return PAPI_ENOMEM; } for ( i = 0; i < nctrs; ++i ) { native_table[i].name[39] = 0; strncpy( native_table[i].name, ctrs[i].name, 39 ); if ( ctrs[i].bitmask & 1 ) native_table[i].encoding[0] = ctrs[i].bits[0]; else native_table[i].encoding[0] = -1; if ( ctrs[i].bitmask & 2 ) native_table[i].encoding[1] = ctrs[i].bits[1]; else native_table[i].encoding[1] = -1; } papi_free( ctrs ); /* Build the preset table */ if ( cpuver <= CPC_ULTRA2 ) { n = sizeof ( us2info ) / sizeof ( einfo_t ); ep = us2info; } else if ( cpuver <= LASTULTRA3 ) { n = sizeof ( us3info ) / sizeof ( einfo_t ); ep = us3info; } else return PAPI_ECMP; preset_table = papi_malloc( ( n + 1 ) * sizeof ( hwi_search_t ) ); npresets = 0; for ( i = 0; i < n; ++i ) { add_preset( preset_table, &npresets, ep[i] ); } memset( &preset_table[npresets], 0, sizeof ( hwi_search_t ) ); #ifdef DEBUG if ( ISLEVEL( DEBUG_SUBSTRATE ) ) { SUBDBG( "Native table: %d\n", nctrs ); for ( i = 0; i < nctrs; ++i ) { SUBDBG( "%40s: %8x %8x\n", native_table[i].name, native_table[i].encoding[0], native_table[i].encoding[1] ); } SUBDBG( "\nPreset table: %d\n", npresets ); for ( i = 0; preset_table[i].event_code != 0; ++i ) { SUBDBG( "%8x: op %2d e0 %8x e1 %8x\n", preset_table[i].event_code, preset_table[i].data.derived, preset_table[i].data.native[0], preset_table[i].data.native[1] ); } } #endif _solaris_vector.cmp_info.num_native_events = nctrs; return PAPI_OK; } static int srch_event( char *e1 ) { int i; for ( i = 0; i < nctrs; ++i ) { if ( strcmp( e1, native_table[i].name ) == 0 ) break; } if ( i >= nctrs ) return -1; return i; } /* we should read from the CSV file and make this all unnecessary */ static void add_preset( hwi_search_t * tab, int *np, einfo_t e ) { /* Parse the event info string and build the PAPI preset. * If parse fails, just return, otherwise increment the table * size. We assume that the table is big enough. */ char *p; char *q; char op; char e1[MAX_ENAME], e2[MAX_ENAME]; int i; int ne; int ne2; p = e.event_str; /* Assume p is the name of a native event, the sum of two * native events, or the difference of two native events. * This could be extended with a real parser (hint). */ while ( isspace( *p ) ) ++p; q = p; i = 0; while ( isalnum( *p ) || ( *p == '_' ) ) { if ( i >= MAX_ENAME - 1 ) break; e1[i] = *p++; ++i; } e1[i] = 0; if ( *p == '+' || *p == '-' ) op = *p++; else op = 0; while ( isspace( *p ) ) ++p; q = p; i = 0; while ( isalnum( *p ) || ( *p == '_' ) ) { if ( i >= MAX_ENAME - 1 ) break; e2[i] = *p++; ++i; } e2[i] = 0; if ( e2[0] == 0 && e1[0] == 0 ) { return; } if ( e2[0] == 0 || op == 0 ) { ne = srch_event( e1 ); if ( ne == -1 ) return; tab[*np].event_code = e.papi_event; tab[*np].data.derived = 0; tab[*np].data.native[0] = PAPI_NATIVE_MASK | ne; tab[*np].data.native[1] = PAPI_NULL; memset( tab[*np].data.operation, 0, sizeof ( tab[*np].data.operation ) ); ++*np; return; } ne = srch_event( e1 ); ne2 = srch_event( e2 ); if ( ne == -1 || ne2 == -1 ) return; tab[*np].event_code = e.papi_event; tab[*np].data.derived = ( op == '-' ) ? DERIVED_SUB : DERIVED_ADD; tab[*np].data.native[0] = PAPI_NATIVE_MASK | ne; tab[*np].data.native[1] = PAPI_NATIVE_MASK | ne2; tab[*np].data.native[2] = PAPI_NULL; memset( tab[*np].data.operation, 0, sizeof ( tab[*np].data.operation ) ); ++*np; } void action( void *arg, int regno, const char *name, uint8_t bits ) { int i; if ( arg == 0 ) { ++nctrs; return; } assert( regno == 0 || regno == 1 ); for ( i = 0; i < nctrs; ++i ) { if ( strcmp( ctrs[i].name, name ) == 0 ) { ctrs[i].bits[regno] = bits; ctrs[i].bitmask |= ( 1 << regno ); return; } } memset( &ctrs[i], 0, sizeof ( ctrs[i] ) ); ctrs[i].name = papi_strdup( name ); ctrs[i].bits[regno] = bits; ctrs[i].bitmask = ( 1 << regno ); ++nctrs; } /* This function should tell your kernel extension that your children inherit performance register information and propagate the values up upon child exit and parent wait. */ static int set_inherit( EventSetInfo_t * global, int arg ) { return PAPI_ECMP; /* hwd_control_state_t *machdep = (hwd_control_state_t *)global->machdep; papi_cpc_event_t *command= &machdep->counter_cmd; return(PAPI_EINVAL); */ #if 0 if ( arg == 0 ) { if ( command->flags & CPC_BIND_LWP_INHERIT ) command->flags = command->flags ^ CPC_BIND_LWP_INHERIT; } else if ( arg == 1 ) { command->flags = command->flags | CPC_BIND_LWP_INHERIT; } else return ( PAPI_EINVAL ); return ( PAPI_OK ); #endif } static int set_default_domain( hwd_control_state_t * ctrl_state, int domain ) { /* This doesn't exist on this platform */ if ( domain == PAPI_DOM_OTHER ) return ( PAPI_EINVAL ); return ( _ultra_set_domain( ctrl_state, domain ) ); } static int set_default_granularity( hwd_control_state_t * current_state, int granularity ) { return ( set_granularity( current_state, granularity ) ); } rwlock_t lock[PAPI_MAX_LOCK]; static void lock_init( void ) { memset( lock, 0x0, sizeof ( rwlock_t ) * PAPI_MAX_LOCK ); } int _ultra_hwd_shutdown_component( void ) { ( void ) cpc_rele( ); return ( PAPI_OK ); } int _ultra_hwd_init_component( int cidx ) { int retval; /* retval = _papi_hwi_setup_vector_table(vtable, _solaris_ultra_table); if ( retval != PAPI_OK ) return(retval); */ /* Fill in what we can of the papi_system_info. */ retval = _solaris_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 ); lock_init( ); 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 ); return ( PAPI_OK ); } /* reset the hardware counter */ int _ultra_hwd_reset( hwd_context_t * ctx, hwd_control_state_t * ctrl ) { int retval; /* reset the hardware counter */ ctrl->counter_cmd.cmd.ce_pic[0] = 0; ctrl->counter_cmd.cmd.ce_pic[1] = 0; /* let's rock and roll */ retval = cpc_bind_event( &ctrl->counter_cmd.cmd, ctrl->counter_cmd.flags ); if ( retval == -1 ) return ( PAPI_ESYS ); return ( PAPI_OK ); } int _ultra_hwd_read( hwd_context_t * ctx, hwd_control_state_t * ctrl, long long **events, int flags ) { int retval; retval = cpc_take_sample( &ctrl->counter_cmd.cmd ); if ( retval == -1 ) return ( PAPI_ESYS ); *events = ( long long * ) ctrl->counter_cmd.cmd.ce_pic; return PAPI_OK; } int _ultra_hwd_ctl( hwd_context_t * ctx, int code, _papi_int_option_t * option ) { switch ( code ) { case PAPI_DEFDOM: return ( set_default_domain ( option->domain.ESI->ctl_state, option->domain.domain ) ); case PAPI_DOMAIN: return ( _ultra_set_domain ( option->domain.ESI->ctl_state, option->domain.domain ) ); case PAPI_DEFGRN: return ( set_default_granularity ( option->domain.ESI->ctl_state, option->granularity.granularity ) ); case PAPI_GRANUL: return ( set_granularity ( option->granularity.ESI->ctl_state, option->granularity.granularity ) ); default: return ( PAPI_EINVAL ); } } void _ultra_hwd_dispatch_timer( int signal, siginfo_t * si, void *context ) { _papi_hwi_context_t ctx; ThreadInfo_t *master = NULL; int isHardware = 0; caddr_t address; int cidx = _solaris_vector.cmp_info.CmpIdx; ctx.si = si; ctx.ucontext = ( ucontext_t * ) context; address = GET_OVERFLOW_ADDRESS( ctx ); _papi_hwi_dispatch_overflow_signal( ( void * ) &ctx, address, &isHardware, 0, 0, &master, _solaris_vector.cmp_info.CmpIdx ); /* We are done, resume interrupting counters */ if ( isHardware ) { // errno = vperfctr_iresume( master->context[cidx]->perfctr ); //if ( errno < 0 ) { // PAPIERROR( "vperfctr_iresume errno %d", errno ); //} } #if 0 EventSetInfo_t *ESI = NULL; ThreadInfo_t *thread = NULL; int overflow_vector = 0; hwd_control_state_t *ctrl = NULL; long_long results[MAX_COUNTERS]; int i; _papi_hwi_context_t ctx; caddr_t address; int cidx = _solaris_vector.cmp_info.CmpIdx; ctx.si = si; ctx.ucontext = ( hwd_ucontext_t * ) info; thread = _papi_hwi_lookup_thread( 0 ); if ( thread == NULL ) { PAPIERROR( "thread == NULL in _papi_hwd_dispatch_timer"); return; } ESI = ( EventSetInfo_t * ) thread->running_eventset[cidx]; if ( ESI == NULL || ESI->master != thread || ESI->ctl_state == NULL || ( ( ESI->state & PAPI_OVERFLOWING ) == 0 ) ) { if ( ESI == NULL ) PAPIERROR( "ESI is NULL\n"); if ( ESI->master != thread ) PAPIERROR( "Thread mismatch, ESI->master=%#x thread=%#x\n", ESI->master, thread ); if ( ESI->ctl_state == NULL ) PAPIERROR( "Counter state invalid\n"); if ( ( ( ESI->state & PAPI_OVERFLOWING ) == 0 ) ) PAPIERROR( "Overflow flag missing"); } ctrl = ESI->ctl_state; if ( thread->running_eventset[cidx]->overflow.flags & PAPI_OVERFLOW_FORCE_SW ) { address = GET_OVERFLOW_ADDRESS( ctx ); _papi_hwi_dispatch_overflow_signal( ( void * ) &ctx, address, NULL, 0, 0, &thread, cidx ); } else { PAPIERROR ( "Need to implement additional code in _papi_hwd_dispatch_timer!" ); } #endif } int _ultra_hwd_set_overflow( EventSetInfo_t * ESI, int EventIndex, int threshold ) { hwd_control_state_t *this_state = ESI->ctl_state; papi_cpc_event_t *arg = &this_state->counter_cmd; int hwcntr; if ( threshold == 0 ) { if ( this_state->overflow_num == 1 ) { arg->flags ^= CPC_BIND_EMT_OVF; if ( sigaction ( _solaris_vector.cmp_info.hardware_intr_sig, NULL, NULL ) == -1 ) return ( PAPI_ESYS ); this_state->overflow_num = 0; } else this_state->overflow_num--; } else { struct sigaction act; /* increase the counter for overflow events */ this_state->overflow_num++; act.sa_sigaction = dispatch_emt; memset( &act.sa_mask, 0x0, sizeof ( act.sa_mask ) ); act.sa_flags = SA_RESTART | SA_SIGINFO; if ( sigaction ( _solaris_vector.cmp_info.hardware_intr_sig, &act, NULL ) == -1 ) return ( PAPI_ESYS ); arg->flags |= CPC_BIND_EMT_OVF; hwcntr = ESI->EventInfoArray[EventIndex].pos[0]; if ( hwcntr == 0 ) arg->cmd.ce_pic[0] = UINT64_MAX - ( uint64_t ) threshold; else if ( hwcntr == 1 ) arg->cmd.ce_pic[1] = UINT64_MAX - ( uint64_t ) threshold; } return ( PAPI_OK ); } _ultra_shutdown( hwd_context_t * ctx ) { return PAPI_OK; } /* int _papi_hwd_stop_profiling(ThreadInfo_t * master, EventSetInfo_t * ESI) { ESI->profile.overflowcount = 0; return (PAPI_OK); } */ void * _ultra_hwd_get_overflow_address( void *context ) { void *location; ucontext_t *info = ( ucontext_t * ) context; location = ( void * ) info->uc_mcontext.gregs[REG_PC]; return ( location ); } int _ultra_hwd_start( hwd_context_t * ctx, hwd_control_state_t * ctrl ) { int retval; /* reset the hardware counter */ if ( ctrl->overflow_num == 0 ) { ctrl->counter_cmd.cmd.ce_pic[0] = 0; ctrl->counter_cmd.cmd.ce_pic[1] = 0; } /* let's rock and roll */ retval = cpc_bind_event( &ctrl->counter_cmd.cmd, ctrl->counter_cmd.flags ); if ( retval == -1 ) return ( PAPI_ESYS ); return ( PAPI_OK ); } int _ultra_hwd_stop( hwd_context_t * ctx, hwd_control_state_t * ctrl ) { cpc_bind_event( NULL, 0 ); return PAPI_OK; } int _ultra_hwd_remove_event( hwd_register_map_t * chosen, unsigned int hardware_index, hwd_control_state_t * out ) { return PAPI_OK; } int _ultra_hwd_encode_native( char *name, int *code ) { return ( PAPI_OK ); } int _ultra_hwd_ntv_enum_events( unsigned int *EventCode, int modifier ) { int index = *EventCode & PAPI_NATIVE_AND_MASK; if ( modifier == PAPI_ENUM_FIRST ) { *EventCode = PAPI_NATIVE_MASK + 1; return PAPI_OK; } if ( cpuver <= CPC_ULTRA2 ) { if ( index < MAX_NATIVE_EVENT_USII - 1 ) { *EventCode = *EventCode + 1; return ( PAPI_OK ); } else return ( PAPI_ENOEVNT ); } else if ( cpuver <= LASTULTRA3 ) { if ( index < MAX_NATIVE_EVENT - 1 ) { *EventCode = *EventCode + 1; return ( PAPI_OK ); } else return ( PAPI_ENOEVNT ); }; return ( PAPI_ENOEVNT ); } int _ultra_hwd_ntv_code_to_name( unsigned int EventCode, char *ntv_name, int len ) { int event_code = EventCode & PAPI_NATIVE_AND_MASK; if ( event_code >= 0 && event_code < nctrs ) { strlcpy( ntv_name, native_table[event_code].name, len ); return PAPI_OK; } return PAPI_ENOEVNT; } int _ultra_hwd_ntv_code_to_descr( unsigned int EventCode, char *hwd_descr, int len ) { return ( _ultra_hwd_ntv_code_to_name( EventCode, hwd_descr, len ) ); } 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; } int _ultra_hwd_ntv_code_to_bits( unsigned int EventCode, hwd_register_t * bits ) { int index = EventCode & PAPI_NATIVE_AND_MASK; if ( cpuver <= CPC_ULTRA2 ) { if ( index >= MAX_NATIVE_EVENT_USII ) { return ( PAPI_ENOEVNT ); } } else if ( cpuver <= LASTULTRA3 ) { if ( index >= MAX_NATIVE_EVENT ) { return ( PAPI_ENOEVNT ); } } else return ( PAPI_ENOEVNT ); bits->event[0] = native_table[index].encoding[0]; bits->event[1] = native_table[index].encoding[1]; return ( PAPI_OK ); } int _ultra_hwd_init_control_state( hwd_control_state_t * ptr ) { ptr->counter_cmd.flags = 0x0; ptr->counter_cmd.cmd.ce_cpuver = cpuver; ptr->counter_cmd.cmd.ce_pcr = 0x0; ptr->counter_cmd.cmd.ce_pic[0] = 0; ptr->counter_cmd.cmd.ce_pic[1] = 0; _ultra_set_domain( ptr, _solaris_vector.cmp_info.default_domain ); set_granularity( ptr, _solaris_vector.cmp_info.default_granularity ); return PAPI_OK; } int _ultra_hwd_update_control_state( hwd_control_state_t * this_state, NativeInfo_t * native, int count, hwd_context_t * zero ) { int nidx1, nidx2, hwcntr; uint64_t tmp = 0; uint64_t pcr; int64_t cmd0, cmd1; /* save the last three bits */ pcr = this_state->counter_cmd.cmd.ce_pcr & 0x7; /* clear the control register */ this_state->counter_cmd.cmd.ce_pcr = pcr; /* no native events left */ if ( count == 0 ) return ( PAPI_OK ); cmd0 = -1; cmd1 = -1; /* one native event */ if ( count == 1 ) { nidx1 = native[0].ni_event & PAPI_NATIVE_AND_MASK; hwcntr = 0; cmd0 = native_table[nidx1].encoding[0]; native[0].ni_position = 0; if ( cmd0 == -1 ) { cmd1 = native_table[nidx1].encoding[1]; native[0].ni_position = 1; } } /* two native events */ if ( count == 2 ) { int avail1, avail2; avail1 = 0; avail2 = 0; nidx1 = native[0].ni_event & PAPI_NATIVE_AND_MASK; nidx2 = native[1].ni_event & PAPI_NATIVE_AND_MASK; if ( native_table[nidx1].encoding[0] != -1 ) avail1 = 0x1; if ( native_table[nidx1].encoding[1] != -1 ) avail1 += 0x2; if ( native_table[nidx2].encoding[0] != -1 ) avail2 = 0x1; if ( native_table[nidx2].encoding[1] != -1 ) avail2 += 0x2; if ( ( avail1 | avail2 ) != 0x3 ) return ( PAPI_ECNFLCT ); if ( avail1 == 0x3 ) { if ( avail2 == 0x1 ) { cmd0 = native_table[nidx2].encoding[0]; cmd1 = native_table[nidx1].encoding[1]; native[0].ni_position = 1; native[1].ni_position = 0; } else { cmd1 = native_table[nidx2].encoding[1]; cmd0 = native_table[nidx1].encoding[0]; native[0].ni_position = 0; native[1].ni_position = 1; } } else { if ( avail1 == 0x1 ) { cmd0 = native_table[nidx1].encoding[0]; cmd1 = native_table[nidx2].encoding[1]; native[0].ni_position = 0; native[1].ni_position = 1; } else { cmd0 = native_table[nidx2].encoding[0]; cmd1 = native_table[nidx1].encoding[1]; native[0].ni_position = 1; native[1].ni_position = 0; } } } /* set the control register */ if ( cmd0 != -1 ) { tmp = ( ( uint64_t ) cmd0 << pcr_shift[0] ); } if ( cmd1 != -1 ) { tmp = tmp | ( ( uint64_t ) cmd1 << pcr_shift[1] ); } this_state->counter_cmd.cmd.ce_pcr = tmp | pcr; #if DEBUG dump_cmd( &this_state->counter_cmd ); #endif return ( PAPI_OK ); } papi_vector_t _solaris_vector = { .cmp_info = { .name = "solaris.ultra", .description = "Solaris CPU counters", .num_cntrs = MAX_COUNTERS, .num_mpx_cntrs = MAX_COUNTERS, .default_domain = PAPI_DOM_USER, .available_domains = PAPI_DOM_USER | PAPI_DOM_KERNEL, .default_granularity = PAPI_GRN_THR, .available_granularities = PAPI_GRN_THR, .fast_real_timer = 1, .fast_virtual_timer = 1, .attach = 1, .attach_must_ptrace = 1, .hardware_intr = 0, .hardware_intr_sig = PAPI_INT_SIGNAL, .precise_intr = 0, } , /* component data structure sizes */ .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 ), } , /* component interface functions */ .init_control_state = _ultra_hwd_init_control_state, .start = _ultra_hwd_start, .stop = _ultra_hwd_stop, .read = _ultra_hwd_read, .shutdown = _ultra_shutdown, .shutdown_component = _ultra_hwd_shutdown_component, .ctl = _ultra_hwd_ctl, .update_control_state = _ultra_hwd_update_control_state, .set_domain = _ultra_set_domain, .reset = _ultra_hwd_reset, .set_overflow = _ultra_hwd_set_overflow, /* .set_profile */ /* .stop_profiling = _papi_hwd_stop_profiling, */ .ntv_enum_events = _ultra_hwd_ntv_enum_events, /* .ntv_name_to_code */ .ntv_code_to_name = _ultra_hwd_ntv_code_to_name, .ntv_code_to_descr = _ultra_hwd_ntv_code_to_descr, .ntv_code_to_bits = _ultra_hwd_ntv_code_to_bits, .init_component = _ultra_hwd_init_component, .dispatch_timer = _ultra_hwd_dispatch_timer, }; papi_os_vector_t _papi_os_vector = { /* OS dependent local routines */ .get_memory_info = _solaris_get_memory_info, .get_dmem_info = _solaris_get_dmem_info, .update_shlib_info = _solaris_update_shlib_info, .get_system_info = _solaris_get_system_info, .get_real_usec = _solaris_get_real_usec, .get_real_cycles = _solaris_get_real_cycles, .get_virt_usec = _solaris_get_virt_usec, };