unsigned long long _papi_hwd_get_virt_usec (const hwd_context_t *context) { /* This routine needs to be carefully debugged It currently doesn't seem to work. We'll substitute real_usec instead, since we're measuring system time anyhow... // returns user time per thread. // NOTE: we can also get process times with GetCurrentProcess() // and GetProcessTimes() HANDLE hThread; FILETIME CreationTime; // when the thread was created FILETIME ExitTime; // when the thread was destroyed FILETIME KernelTime; // time the thread has spent in kernel mode FILETIME UserTime; // time the thread has spent in user mode LARGE_INTEGER largeUser, largeKernel; BOOL success; success = DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hThread, 0, 0, DUPLICATE_SAME_ACCESS); if (!success) printf("FAILED DuplicateHandle!\n"); success = GetThreadTimes(hThread, &CreationTime, &ExitTime, &KernelTime, &UserTime); largeKernel.LowPart = KernelTime.dwLowDateTime; largeKernel.HighPart = KernelTime.dwHighDateTime; largeUser.LowPart = UserTime.dwLowDateTime; largeUser.HighPart = UserTime.dwHighDateTime; if (!success) printf("FAILED GetThreadTimes!\n"); printf("largeKernel: %I64d\n", largeKernel.QuadPart); printf("largeUser: %I64d\n", largeUser.QuadPart); return (largeUser.QuadPart/10); // time is in 100 ns increments */ return(_papi_hwd_get_real_usec()); }
static void mpx_handler( int signal ) { int retval; MasterEvent *mev, *head; Threadlist *me = NULL; #ifdef REGENERATE int lastthread; #endif #ifdef MPX_DEBUG_OVERHEAD long long usec; int didwork = 0; usec = PAPI_get_real_usec( ); #endif #ifdef MPX_DEBUG_TIMER long long thiscall; #endif signal = signal; /* unused */ MPXDBG( "Handler in thread\n" ); /* This handler can be invoked either when a timer expires * or when another thread in this handler responding to the * timer signals other threads. We have to distinguish * these two cases so that we don't get infinite loop of * handler calls. To do that, we look at the value of * threads_responding. We assume that only one thread can * be active in this signal handler at a time, since the * invoking signal is blocked while the handler is active. * If threads_responding == 0, the current thread caught * the original timer signal. (This thread may not have * any active event lists itself, though.) This first * thread sends a signal to each of the other threads in * our list of threads that have master events lists. If * threads_responding != 0, then this thread was signaled * by another thread. We decrement that value and look * for an active events. threads_responding should * reach zero when all active threads have handled their * signal. It's probably possible for a thread to die * before it responds to a signal; if that happens, * threads_responding won't reach zero until the next * timer signal happens. Then the signalled thread won't * signal any other threads. If that happens only * occasionally, there should be no harm. Likewise if * a new thread is added that fails to get signalled. * As for locking, we have to lock this list to prevent * another thread from modifying it, but if *this* thread * is trying to update the list (from another function) and * is signaled while it holds the lock, we will have deadlock. * Therefore, noninterrupt functions that update *this* list * must disable the signal that invokes this handler. */ #ifdef PTHREADS _papi_hwi_lock( MULTIPLEX_LOCK ); if ( threads_responding == 0 ) { /* this thread caught the timer sig */ /* Signal the other threads with event lists */ #ifdef MPX_DEBUG_TIMER thiscall = _papi_hwd_get_real_usec( ); MPXDBG( "last signal was %lld usec ago\n", thiscall - lastcall ); lastcall = thiscall; #endif MPXDBG( "%#x caught it, tlist is %p\n", self, tlist ); for ( t = tlist; t != NULL; t = t->next ) { if ( pthread_equal( t->thr, self ) == 0 ) { ++threads_responding; retval = pthread_kill( t->thr, _papi_os_info.itimer_sig ); assert( retval == 0 ); #ifdef MPX_DEBUG_SIGNALS MPXDBG( "%#x signaling %#x\n", self, t->thr ); #endif } } } else { #ifdef MPX_DEBUG_SIGNALS MPXDBG( "%#x was tapped, tr = %d\n", self, threads_responding ); #endif --threads_responding; } #ifdef REGENERATE lastthread = ( threads_responding == 0 ); #endif _papi_hwi_unlock( MULTIPLEX_LOCK ); #endif /* See if this thread has an active event list */ head = get_my_threads_master_event_list( ); if ( head != NULL ) { /* Get the thread header for this master event set. It's * always in the first record of the set (and maybe in others) * if any record in the set is active. */ me = head->mythr; /* Find the event that's currently active, stop and read * it, then start the next event in the list. * No need to lock the list because other functions * disable the timer interrupt before they update the list. */ if ( me != NULL && me->cur_event != NULL ) { long long counts[2]; MasterEvent *cur_event = me->cur_event; long long cycles = 0, total_cycles = 0; retval = PAPI_stop( cur_event->papi_event, counts ); MPXDBG( "retval=%d, cur_event=%p, I'm tid=%lx\n", retval, cur_event, me->tid ); if ( retval == PAPI_OK ) { MPXDBG( "counts[0] = %lld counts[1] = %lld\n", counts[0], counts[1] ); cur_event->count += counts[0]; cycles = ( cur_event->pi.event_type == SCALE_EVENT ) ? counts[0] : counts[1]; me->total_c += cycles; total_cycles = me->total_c - cur_event->prev_total_c; cur_event->prev_total_c = me->total_c; /* If it's a rate, count occurrences & average later */ if ( !cur_event->is_a_rate ) { cur_event->cycles += cycles; if ( cycles >= MPX_MINCYC ) { /* Only update current rate on a decent slice */ cur_event->rate_estimate = ( double ) counts[0] / ( double ) cycles; } cur_event->count_estimate += ( long long ) ( ( double ) total_cycles * cur_event->rate_estimate ); MPXDBG("New estimate = %lld (%lld cycles * %lf rate)\n", cur_event->count_estimate,total_cycles, cur_event->rate_estimate); } else { /* Make sure we ran long enough to get a useful measurement (otherwise * potentially inaccurate rate measurements get averaged in with * the same weight as longer, more accurate ones.) */ if ( cycles >= MPX_MINCYC ) { cur_event->cycles += 1; } else { cur_event->count -= counts[0]; } } } else { MPXDBG( "%lx retval = %d, skipping\n", me->tid, retval ); MPXDBG( "%lx value = %lld cycles = %lld\n\n", me->tid, cur_event->count, cur_event->cycles ); } MPXDBG ( "tid(%lx): value = %lld (%lld) cycles = %lld (%lld) rate = %lf\n\n", me->tid, cur_event->count, cur_event->count_estimate, cur_event->cycles, total_cycles, cur_event->rate_estimate ); /* Start running the next event; look for the * next one in the list that's marked active. * It's possible that this event is the only * one active; if so, we should restart it, * but only after considerating all the other * possible events. */ if ( ( retval != PAPI_OK ) || ( ( retval == PAPI_OK ) && ( cycles >= MPX_MINCYC ) ) ) { for ( mev = ( cur_event->next == NULL ) ? head : cur_event->next; mev != cur_event; mev = ( mev->next == NULL ) ? head : mev->next ) { /* Found the next one to start */ if ( mev->active ) { me->cur_event = mev; break; } } } if ( me->cur_event->active ) { retval = PAPI_start( me->cur_event->papi_event ); } #ifdef MPX_DEBUG_OVERHEAD didwork = 1; #endif } } #ifdef ANY_THREAD_GETS_SIGNAL else { Threadlist *t; for ( t = tlist; t != NULL; t = t->next ) { if ( ( t->tid == _papi_hwi_thread_id_fn( ) ) || ( t->head == NULL ) ) continue; MPXDBG( "forwarding signal to thread %lx\n", t->tid ); retval = ( *_papi_hwi_thread_kill_fn ) ( t->tid, _papi_os_info.itimer_sig ); if ( retval != 0 ) { MPXDBG( "forwarding signal to thread %lx returned %d\n", t->tid, retval ); } } } #endif #ifdef REGENERATE /* Regenerating the signal each time through has the * disadvantage that if any thread ever drops a signal, * the whole time slicing system will stop. Using * an automatically regenerated signal may have the * disadvantage that a new signal can arrive very * soon after all the threads have finished handling * the last one, so the interval may be too small for * accurate data collection. However, using the * MIN_CYCLES check above should alleviate this. */ /* Reset the timer once all threads have responded */ if ( lastthread ) { retval = setitimer( _papi_os_info.itimer_num, &itime, NULL ); assert( retval == 0 ); #ifdef MPX_DEBUG_TIMER MPXDBG( "timer restarted by %lx\n", me->tid ); #endif } #endif #ifdef MPX_DEBUG_OVERHEAD usec = _papi_hwd_get_real_usec( ) - usec; MPXDBG( "handler %#x did %swork in %lld usec\n", self, ( didwork ? "" : "no " ), usec ); #endif }
/* * This function should return the highest resolution wallclock timer available * in cycles */ long long _papi_hwd_get_real_cycles( void ) { return ( _papi_hwd_get_real_usec( ) * ( long long ) _papi_system_info.hw_info.mhz ); }