Stephane,

Here is an example program that fails for me on a Core Duo running
2.6.20 and the 070209 perfmon. The program takes an integer as an
argument and creates that many threads. Each thread enables counter
overflow signals and applies F_SETSIG to its perfmon fd. However, in
some cases the read from the perfmon fd in the signal handler returns
errno 11 (EWOULDBLOCK), which seems like an inconsistency. Furthermore,
inspecting the siginfo structure shows that the fd reported there
actually refers to the context of a different thread than the one
handling the signal.

The program seems to work fine for two or three threads, but with four
or more it fails about half the time. I think our original uC++ version
fails more often than this, but then we have much more going on,
including more code in the signal handler.

Back when I originally brought up this problem last month, Phil Mucci
mentioned that he had noticed similar behavior. I would be interested
to know if he has managed to confirm this.

- Richard

// g++ -Wall -g sigio.cc -lpfm -lpthread

#include <perfmon/perfmon.h>
#include <perfmon/pfmlib.h>
#include <cstdlib>
#include <cstdio>
#include <cerrno>
#include <cstring>
#include <cstdarg>
#include <cassert>
#include <signal.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <pthread.h>
#include <fcntl.h>

#define PERIOD 100000
// #define OLD

__thread int uPerfmon_fd;


void uAbort( const char *fmt, ... ) {
    if ( fmt != NULL ) {
        fprintf( stderr, "Runtime error (UNIX pid:%ld) ", (long int)getpid() );
        va_list args;
        va_start( args, fmt );
        vfprintf( stderr, fmt, args );
        va_end( args );
        fprintf( stderr, "\n" );
    } // if

    _exit( -1 );
} // uAbort


void sigHWOverflowHandler( int, siginfo_t *sfp, void * ) {
    assert( sfp->si_code == POLL_IN );

    pfm_msg_t message;
    int result = read( uPerfmon_fd, &message, sizeof( message ) );
    if ( result == -1 ) {
        uAbort( "HWCounters::getOverflowMask(): Unable to read overflow message 
from fd %d errno %d (%s) si_fd %d.", uPerfmon_fd, errno, strerror( errno ), 
sfp->si_fd );
    } // if

    if ( result < (long)sizeof( message ) ) {
        uAbort( "HWCounters::getOverflowMask(): Incomplete overflow message 
from fd %d errno %d (%s).", uPerfmon_fd, errno, strerror( errno ) );
    } // if

#if defined( OLD )
    if ( perfmonctl( uPerfmon_fd, PFM_RESTART, NULL, 0 ) == -1 ) {
#else
    if ( pfm_restart( uPerfmon_fd ) == -1 ) {
#endif // OLD
        uAbort( "HWCounters::uRestartCounters(): Unable to restart hardware 
counters." );
    } // if
} // HWCounters::getOverflowMask


static char *event_name;


void uStartCounters( bool signalOnOverflow ) {
#ifdef __U_DEBUG_H__
    uBaseTask &task = uThisTask();
    uCluster &cluster = uThisCluster();
    uDebugPrt( "Task calling HWCounters::uStartCounters(): (uBaseTask 
&)0x%p(%s) on cluster 0x%p(%s)\n", &task, task.getName(), &cluster, 
cluster.getName() );
#endif // __U_DEBUG_H__

    pid_t pid = syscall( __NR_gettid );

    // these structures are too large to be allocated on the stack
    pfmlib_input_param_t &events = *new pfmlib_input_param_t;
    pfmlib_output_param_t &output_params = *new pfmlib_output_param_t;
    memset( &output_params, 0, sizeof( output_params ) );
    memset( &events, 0, sizeof( events ) );
    if ( pfm_find_event_byname( event_name, &events.pfp_events[0].event ) != 
PFMLIB_SUCCESS ) {
        uAbort( "HWCounters::uStartCounters(): Couldn't find event %s.", 
event_name );
    }

    // Set the number of events being counted
    events.pfp_event_count = 1;

    // Set profiling privilege levels (PFM_PLM0 is most privileged, PFM_PLM3 is 
least privileged)
    events.pfp_dfl_plm |= PFM_PLM3;

    // Let libpfm figure out how to program the hardware counters
    if ( pfm_dispatch_events( &events, NULL, &output_params, NULL ) != 
PFMLIB_SUCCESS ) {
        uAbort( "HWCounters::uStartCounters(): Illegal event set." );
    }

    // Prepare the argument to initialize the PMCs.
#if defined( OLD )
    pfarg_reg_t pmcs[4];
#else
    pfarg_pmc_t pmcs[4];
#endif
    memset( pmcs, 0, sizeof( pmcs ) );
    for ( unsigned int event = 0; event < output_params.pfp_pmc_count; event += 
1 ) {
        pmcs[event].reg_num   = output_params.pfp_pmcs[event].reg_num;
        pmcs[event].reg_value = output_params.pfp_pmcs[event].reg_value;
    } // for

    // Prepare the argument to initialize the PMDs.  Set it to all zeros so 
that the PMD event
    // counts will be initialized to zero.
    pfarg_pmd_t pmds[4];
    memset( pmds, 0, sizeof( pmds ) );

    // Set appropriate PMD register mappings.  Just take the needed register 
numbers from the
    // `events' structure that libpfm filled in with pfm_dispatch_events().
    for ( unsigned int event = 0; event < events.pfp_event_count; event += 1 ) {
        pmds[event].reg_num = pmcs[event].reg_num;
    }

#if defined( OLD )
    pfarg_context_t context;
#else
    pfarg_ctx_t context;
#endif // OLD
    memset( &context, 0, sizeof( context ) );

    // Create perfmon context
#if defined( OLD )
    if ( perfmonctl( 0, PFM_CREATE_CONTEXT, &context, 1 ) == -1 ) {
#else
    int fd = pfm_create_context( &context, NULL, NULL, 0 );
    if ( fd == -1 ) {
#endif // OLD
        if ( errno == ENOSYS ) {
            uAbort( "HWCounters::uStartCounters(): Kernel does not have 
performance monitoring support enabled." );
        } // if
        uAbort( "HWCounters::uStartCounters(): Unable to create perfmon 
context: %s.", strerror( errno ) );
    } // if

#if defined( OLD )
    int fd = context.ctx_fd;
#endif // OLD
    uPerfmon_fd = fd;

    if ( signalOnOverflow ) {
        // Enable overflow notification for all events.
#if defined( OLD )
        for ( unsigned int event = 0; event < output_params.pfp_pmc_count; 
++event ) {
            pmcs[event].reg_flags |= PFM_REGFL_OVFL_NOTIFY;
        } // for
#endif // OLD

        // Set all events to overflow after the desired number of events.
        for ( unsigned int event = 0; event < events.pfp_event_count; ++event ) 
{
#if ! defined( OLD )
            pmds[event].reg_flags |= PFM_REGFL_OVFL_NOTIFY;
#endif // ! OLD
            pmds[event].reg_value = ~0ULL - PERIOD + 1;
            pmds[event].reg_long_reset = ~0ULL - PERIOD + 1;
            pmds[event].reg_short_reset = ~0ULL - PERIOD + 1;
        } // for
    } // if

    // Program the PMCs (this essentially tells which performance counters to 
measure which event)
#if defined( OLD )
    if ( perfmonctl( fd, PFM_WRITE_PMCS, pmcs, output_params.pfp_pmc_count ) == 
-1 ) {
#else
    if ( pfm_write_pmcs( fd, pmcs, output_params.pfp_pmc_count ) == -1 ) {
#endif // OLD
        uAbort( "HWCounters::uStartCounters(): Unable to program PMCs: %s (%d) 
-- fd %d", strerror( errno ), errno, fd );
    } // if

    // Initialize the PMD event counts to zero.
#if defined( OLD )
    if ( perfmonctl( fd, PFM_WRITE_PMDS, pmds, events.pfp_event_count ) == -1 ) 
{
#else
    if ( pfm_write_pmds( fd, pmds, events.pfp_event_count ) == -1 ) {
#endif // OLD
        uAbort( "HWCounters::uStartCounters(): Unable to initialize PMDs." );
    } // if

    // Attach the perfmon context to the current processor.  The new perfmon 
interface uses file
    // descriptors to represent performance counter contexts.  These statements 
associate the
    // new context with the current kernel thread.
    pfarg_load_t load_args;
    memset( &load_args, 0, sizeof( load_args ) );
    load_args.load_pid = pid;
#if defined( OLD )
    if ( perfmonctl( fd, PFM_LOAD_CONTEXT, &load_args, 1 ) == -1 ) {
#else
    if ( pfm_load_context( fd, &load_args ) == -1 ) {
#endif // OLD
        uAbort( "HWCounters::uStartCounters(): Unable to load perfmon context." 
);
    } // if

    if ( signalOnOverflow ) {
        if ( fcntl( fd, F_SETFL, fcntl( fd, F_GETFL, 0 ) | O_ASYNC | O_NONBLOCK 
) == -1 ) {
            uAbort( "HWCounters::uStartCounters(): Unable to setup asynchronous 
notification of virtual processor's file descriptor." );
        } // if
        if ( fcntl( fd, F_SETOWN, pid ) == -1 ) {
            uAbort( "HWCounters::uStartCounters(): Unable to set ownership of 
virtual processor's file descriptor." );
        } // if
        if ( fcntl( fd, F_SETSIG, SIGIO ) == -1 ) {
            uAbort( "HWCounters::uStartCounters(): Unable to setup additional 
information for asynchronous notification." );
        } // if
    } // if

    // Let 'er rip!!
    pfm_self_start( fd );

    delete &events;
    delete &output_params;
#ifdef __U_DEBUG_H__
    uDebugPrt( "HWCounters::uStartCounters(), exit\n" );
#endif
} // HWCounters::uStartCounters


void dosignal( int sig, void (*handler)(int, siginfo_t*, void*), int flags ) {
    struct sigaction act;
    act.sa_sigaction = handler;
    sigemptyset( &act.sa_mask );
    sigaddset( &act.sa_mask, SIGALRM );                 // disable during 
signal handler
    sigaddset( &act.sa_mask, SIGVTALRM );
    sigaddset( &act.sa_mask, SIGUSR1 );
    sigaddset( &act.sa_mask, SIGIO );
    act.sa_flags = flags;

    if ( sigaction( sig, &act, NULL ) == -1 ) {
        fprintf( stderr, " uSigHandlerModule::signal( sig:%d, handler:%p, 
flags:%d ), problem installing signal handler, error(%d) %s.\n",
                 sig, handler, flags, errno, strerror( errno ) );
        _exit( -1 );
    } // if
} // uSigHandlerModule::signal


void *threadfunc( void * ) {
    dosignal( SIGIO, sigHWOverflowHandler, SA_SIGINFO );

    sigset_t unblock;
    sigemptyset( &unblock );
    sigaddset( &unblock, SIGIO );
    sigprocmask( SIG_UNBLOCK, &unblock, NULL );

    uStartCounters( true );

    for( ; ; )
        ;

    return NULL;
} // threadfunc


int main( int argc, char **argv ) {
    int nthreads = 5;
    if ( argc > 1 ) {
        nthreads = atoi( argv[ 1 ] );
    } // if
    pthread_t threads[ nthreads ];

    if ( pfm_initialize() != PFMLIB_SUCCESS ) {
        uAbort( "Couldn't initialize perfmon" );
    }

    pfmlib_options_t pfmlib_options;
    pfmlib_options.pfm_debug = 0;
    pfmlib_options.pfm_verbose = 0;
    pfm_set_options( &pfmlib_options );

    int pmu_type;
    if ( pfm_get_pmu_type( &pmu_type ) != PFMLIB_SUCCESS ) {
        uAbort( "Couldn't access PMU" );
    }

    // Check which type of PMU is being used
    switch ( pmu_type ) {
      case PFMLIB_ITANIUM2_PMU:
        event_name = "CPU_CYCLES";
        break;
#if defined( PFMLIB_COREDUO_PMU )
      case PFMLIB_COREDUO_PMU:
        event_name = "UNHALTED_CORE_CYCLES";
        break;
#endif // PFMLIB_COREDUO_PMU
      default:                                         // this CPU isn't 
currently supported
        uAbort( "unsupported PMU" );
        break;
    }

    for ( int i = 0; i < nthreads; i += 1 ) {
        pthread_create( &threads[ i ], NULL, threadfunc, NULL );
    } // for

    for ( int i = 0; i < nthreads; i += 1 ) {
        pthread_join( threads[ i ], NULL );
    } // for

    return 0;
} // main
_______________________________________________
perfmon mailing list
[email protected]
http://www.hpl.hp.com/hosted/linux/mail-archives/perfmon/

Reply via email to