PAPIC:Component Developers Manual
From PAPIDocs
Jump to: navigation, search

Contents

PAPI-C Component Developer's Manual

If you are looking for the old style "Cookbook," it can be found here: Component Development Cookbook

Here you will find what you need to develop your component starting from the example component provided in the PAPI source code component directory.

General Flow of Development

  1. Create a directory within the components directory, giving it a name similar to the rest
  2. Copy the example files to your new_component directory
  3. Rename and Edit the Rules.example file to suit your compile scheme
  4. Build up you functionality from within the example code, again renaming all instances of <example> with <new_component>.

All instances of <new_component> should be replaced with your component name, accordingly, as you go through this manual.

Create a new component directory

First, you will want to create a directory within the components directory, giving it a name similar to the rest.

user@host:~/papi/src/components$ ls -l
total 10K
drwxr-xr-x 3 user group 4.0K 2012-04-01 22:27 example/
-rw-r--r-- 1 user group  168 2012-03-13 14:31 Makefile_comp_tests
-rw-r--r-- 1 user group 6.0K 2012-03-13 14:31 README
-rw-r--r-- 1 user group   90 2012-03-13 14:31 Rules.components
user@host:~/papi/src/components$ mkdir new_component
user@host:~/papi/src/components$ ls -l
total 14K
drwxr-xr-x 3 user group 4.0K 2012-04-01 22:27 example/
-rw-r--r-- 1 user group  168 2012-03-13 14:31 Makefile_comp_tests
drwxr-xr-x 2 user group 4.0K 2012-04-02 00:38 new_component/
-rw-r--r-- 1 user group 6.0K 2012-03-13 14:31 README
-rw-r--r-- 1 user group   90 2012-03-13 14:31 Rules.components

Copy example files to new_component directory for use

You will want to copy all the files from the examples directory into your new component directory to begin development. Then, you will need to rename the source (example.c) file and the header (example.h) file to suit your component. It is common to name these the same as the directory name for simple components, requiring only a single source and header file.

user@host:~/papi/src/components$ cp -r example/* new_component/
user@host:~/papi/src/components$ ls -l new_component/
total 28
-rw-r--r-- 1 user group 18676 2012-04-02 11:10 example.c
-rw-r--r-- 1 user group     0 2012-04-02 11:10 example.h
-rw-r--r-- 1 user group   223 2012-04-02 11:10 Rules.example
drwxr-xr-x 2 user group  4096 2012-04-02 11:10 tests
user@host:~/papi/src/components$ cd new_component/
user@host:~/papi/src/components/new_component$ ls -l
total 28K
-rw-r--r-- 1 user group  19K 2012-04-02 11:10 example.c
-rw-r--r-- 1 user group    0 2012-04-02 11:10 example.h
-rw-r--r-- 1 user group  223 2012-04-02 11:10 Rules.example
drwxr-xr-x 2 user group 4.0K 2012-04-02 11:10 tests/
user@host:~/papi/src/components/new_component$ mv example.c new_component.c
user@host:~/papi/src/components/new_component$ mv example.h new_component.h
user@host:~/papi/src/components/new_component$ ls -l
total 28K
-rw-r--r-- 1 user group  19K 2012-04-02 11:10 new_component.c
-rw-r--r-- 1 user group    0 2012-04-02 11:10 new_component.h
-rw-r--r-- 1 user group 223 2012-04-02 11:10 Rules.example
drwxr-xr-x 2 user group 4.0K 2012-04-02 11:10 tests/

Rename and Edit Rules.example

You will need to rename Rules.example to be Rules.new_component. Then, looking at the Rules.new_component file you can use the global find/replace option in vi (:%s/example/new_component/g) to replace all instances of example with new_component.

user@host:~/papi/src/components/new_component$ mv Rules.example Rules.new_component
user@host:~/papi/src/components/new_component$ vi Rules.new_component
COMPSRCS += components/example/example.c
COMPOBJS += example.o

example.o: components/example/example.c components/example/example.h $(HEADERS)
        $(CC) $(LIBCFLAGS) $(OPTFLAGS) -c components/example/example.c -o example.o
~                                                                                     
:%s/example/new_component/g
user@host:~/papi/src/components/new_component$ cat Rules.new_component
COMPSRCS += components/new_component/new_component.c
COMPOBJS += new_component.o

new_component.o: components/new_component/new_component.c components/new_component/new_component.h $(HEADERS) 
	$(CC) $(LIBCFLAGS) $(OPTFLAGS) -c components/new_component/new_component.c -o new_component.o 

Example of Implementation

Below you will find the example PAPI component code. Included are inline instructions on how to implement a basic PAPI component. For more complex implementation examples you can examine other prewritten, production components in the src/components directory.

//  example.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>

/* Headers required by PAPI */
#include "papi.h"
#include "papi_internal.h"
#include "papi_memory.h"    /* defines papi_malloc(), etc. */

/** This driver supports three counters counting at once      */
/*  This is artificially low to allow testing of multiplexing */
#define EXAMPLE_MAX_SIMULTANEOUS_COUNTERS 3

/* Declare our vector in advance */
/* This allows us to modify the component info */
papi_vector_t _example_vector;

/** Structure that stores private information for each event */
typedef struct example_register
{
    unsigned int selector;
    /**< Signifies which counter slot is being used */
    /**< Indexed from 1 as 0 has a special meaning  */
} example_register_t;

/** This structure is used to build the table of events  */
/*   The contents of this structure will vary based on   */
/*   your component, however having name and description */
/*   fields are probably useful.                         */
typedef struct example_native_event_entry
{
	example_register_t resources;	    /**< Per counter resources       */
	char name[PAPI_MAX_STR_LEN];	    /**< Name of the counter         */
	char description[PAPI_MAX_STR_LEN]; /**< Description of the counter  */
	int writable;			    /**< Whether counter is writable */
	/* any other counter parameters go here */
} example_native_event_entry_t;

/** This structure is used when doing register allocation 
 it possibly is not necessary when there are no 
 register constraints */
typedef struct example_reg_alloc
{
	example_register_t ra_bits;
} example_reg_alloc_t;

/** Holds control flags.  
 *    There's one of these per event-set.
 *    Use this to hold data specific to the EventSet, either hardware
 *      counter settings or things like counter start values 
 */
typedef struct example_control_state
{
    int num_events;
    int domain;
    int multiplexed;
    int overflow;
    int inherit;
    int which_counter[EXAMPLE_MAX_SIMULTANEOUS_COUNTERS]; 
    long long counter[EXAMPLE_MAX_SIMULTANEOUS_COUNTERS];   /**< Copy of counts, holds results when stopped */
} example_control_state_t;

/** Holds per-thread information */
typedef struct example_context
{
    long long autoinc_value;
} example_context_t;

/** This table contains the native events */
static example_native_event_entry_t *example_native_table;

/** number of events in the table*/
static int num_events = 0;


/*************************************************************************/
/* Below is the actual "hardware implementation" of our example counters */
/*************************************************************************/

#define EXAMPLE_ZERO_REG             0
#define EXAMPLE_CONSTANT_REG         1
#define EXAMPLE_AUTOINC_REG          2
#define EXAMPLE_GLOBAL_AUTOINC_REG   3

#define EXAMPLE_TOTAL_EVENTS         4

static long long example_global_autoinc_value = 0;

/** Code that resets the hardware.  */
static void
example_hardware_reset( example_context_t *ctx )
{
    /* reset per-thread count */
    ctx->autoinc_value=0;
    /* reset global count */
    example_global_autoinc_value = 0;
    
}

/** Code that reads event values.                         */
/*   You might replace this with code that accesses       */
/*   hardware or reads values from the operatings system. */
static long long
example_hardware_read( int which_one, example_context_t *ctx )
{
    
	long long old_value;
    
	switch ( which_one ) {
        case EXAMPLE_ZERO_REG:
            return 0;
        case EXAMPLE_CONSTANT_REG:
            return 42;
        case EXAMPLE_AUTOINC_REG:
            old_value = ctx->autoinc_value;
            ctx->autoinc_value++;
            return old_value;
        case EXAMPLE_GLOBAL_AUTOINC_REG:
            old_value = example_global_autoinc_value;
            example_global_autoinc_value++;
            return old_value;
        default:
            perror( "Invalid counter read" );
            return -1;
	}
    
	return 0;
}

/** Code that writes event values.                        */
static int
example_hardware_write( int which_one, 
                       example_context_t *ctx,
                       long long value)
{
    
	switch ( which_one ) {
        case EXAMPLE_ZERO_REG:
        case EXAMPLE_CONSTANT_REG:
            return PAPI_OK; /* can't be written */
        case EXAMPLE_AUTOINC_REG:
            ctx->autoinc_value=value;
            return PAPI_OK;
        case EXAMPLE_GLOBAL_AUTOINC_REG:
	        example_global_autoinc_value=value;
            return PAPI_OK;
        default:
            perror( "Invalid counter write" );
            return -1;
	}
    
	return 0;
}

static int
detect_example(void) {
    
    return PAPI_OK;
}

/********************************************************************/
/* Below are the functions required by the PAPI component interface */
/********************************************************************/


/** Initialize hardware counters, setup the function vector table
 * and get hardware information, this routine is called when the
 * PAPI process is initialized (IE PAPI_library_init)
 */
int
_example_init_substrate( int cidx )
{
    
	SUBDBG( "_example_init_substrate..." );
    
    
    /* First, detect that our hardware is available */
    if (detect_example()!=PAPI_OK) {
        return PAPI_ESBSTR;
	}
    
	/* we know in advance how many events we want                       */
	/* for actual hardware this might have to be determined dynamically */
	num_events = EXAMPLE_TOTAL_EVENTS;
    
	/* Allocate memory for the our native event table */
	example_native_table =
    ( example_native_event_entry_t * )
    papi_calloc( sizeof(example_native_event_entry_t),num_events);
	if ( example_native_table == NULL ) {
		PAPIERROR( "malloc():Could not get memory for events table" );
		return PAPI_ENOMEM;
	}
    
	/* fill in the event table parameters */
	/* for complicated components this will be done dynamically */
	/* or by using an external library                          */
    
	strcpy( example_native_table[0].name, "EXAMPLE_ZERO" );
	strcpy( example_native_table[0].description,
           "This is an example counter, that always returns 0" );
	example_native_table[0].writable = 0;
    
	strcpy( example_native_table[1].name, "EXAMPLE_CONSTANT" );
	strcpy( example_native_table[1].description,
           "This is an example counter, that always returns a constant value of 42" );
	example_native_table[1].writable = 0;
    
	strcpy( example_native_table[2].name, "EXAMPLE_AUTOINC" );
	strcpy( example_native_table[2].description,
           "This is an example counter, that reports a per-thread  auto-incrementing value" );
	example_native_table[2].writable = 1;
    
	strcpy( example_native_table[3].name, "EXAMPLE_GLOBAL_AUTOINC" );
	strcpy( example_native_table[3].description,
           "This is an example counter, that reports a global auto-incrementing value" );
	example_native_table[3].writable = 1;
    
	/* Export the total number of events available */
	_example_vector.cmp_info.num_native_events = num_events;
    
	/* Export the component id */
	_example_vector.cmp_info.CmpIdx = cidx;
    
	
    
	return PAPI_OK;
}

/** This is called whenever a thread is initialized */
int
_example_init( hwd_context_t *ctx )
{
    
    example_context_t *example_context = (example_context_t *)ctx;
    
    example_context->autoinc_value=0;
    
	SUBDBG( "_example_init %p...", ctx );
    
	return PAPI_OK;
}



/** Setup a counter control state.
 *   In general a control state holds the hardware info for an
 *   EventSet.
 */

int
_example_init_control_state( hwd_control_state_t * ctl )
{
    SUBDBG( "example_init_control_state... %p\n", ctl );
    
    example_control_state_t *example_ctl = ( example_control_state_t * ) ctl;
    memset( example_ctl, 0, sizeof ( example_control_state_t ) );
    
    return PAPI_OK;
}


/** Triggered by eventset operations like add or remove */
int
_example_update_control_state( hwd_control_state_t *ctl, 
                              NativeInfo_t *native,
                              int count, 
                              hwd_context_t *ctx )
{
    
    (void) ctx;
    int i, index;
    
    example_control_state_t *example_ctl = ( example_control_state_t * ) ctl;   
    
    SUBDBG( "_example_update_control_state %p %p...", ctl, ctx );
    
    /* if no events, return */
    if (count==0) return PAPI_OK;
    
    for( i = 0; i < count; i++ ) {
        index = native[i].ni_event & PAPI_NATIVE_AND_MASK & PAPI_COMPONENT_AND_MASK;
        
        /* Map counter #i to Measure Event "index" */
        example_ctl->which_counter[i]=index;
        
        /* We have no constraints on event position, so any event */
        /* can be in any slot.                                    */
        native[i].ni_position = i;
    }
    
    example_ctl->num_events=count;
    
    return PAPI_OK;
}

/** Triggered by PAPI_start() */
int
_example_start( hwd_context_t *ctx, hwd_control_state_t *ctl )
{
    
    (void) ctx;
    (void) ctl;
    
	SUBDBG( "example_start %p %p...", ctx, ctl );
    
	/* anything that would need to be set at counter start time */
    
	/* reset counters? */
    /* For hardware that cannot reset counters, store initial        */
    /*     counter state to the ctl and subtract it off at read time */
    
	/* start the counting ?*/
    
	return PAPI_OK;
}


/** Triggered by PAPI_stop() */
int
_example_stop( hwd_context_t *ctx, hwd_control_state_t *ctl )
{
    
    (void) ctx;
    (void) ctl;
    
	SUBDBG( "example_stop %p %p...", ctx, ctl );
    
	/* anything that would need to be done at counter stop time */
    
	
    
	return PAPI_OK;
}


/** Triggered by PAPI_read()     */
/*     flags field is never set? */
int
_example_read( hwd_context_t *ctx, hwd_control_state_t *ctl,
			  long long **events, int flags )
{
    
    (void) flags;
    
    example_context_t *example_ctx = (example_context_t *) ctx;
    example_control_state_t *example_ctl = ( example_control_state_t *) ctl;   
    
    SUBDBG( "example_read... %p %d", ctx, flags );
    
    int i;
    
    /* Read counters into expected slot */
    for(i=0;i<example_ctl->num_events;i++) {
        example_ctl->counter[i] =
		example_hardware_read( example_ctl->which_counter[i], 
                              example_ctx );
    }
    
    /* return pointer to the values we read */
    *events = example_ctl->counter;
    
    return PAPI_OK;
}

/** Triggered by PAPI_write(), but only if the counters are running */
/*    otherwise, the updated state is written to ESI->hw_start      */
int
_example_write( hwd_context_t *ctx, hwd_control_state_t *ctl,
			   long long *events )
{
    
    example_context_t *example_ctx = (example_context_t *) ctx;
    example_control_state_t *example_ctl = ( example_control_state_t *) ctl;   
    
    int i;
    
	SUBDBG( "example_write... %p %p", ctx, ctl );
    
    /* Write counters into expected slot */
    for(i=0;i<example_ctl->num_events;i++) {
        example_hardware_write( example_ctl->which_counter[i],
                               example_ctx,
                               events[i] );
	}
    
	return PAPI_OK;
}


/** Triggered by PAPI_reset() but only if the EventSet is currently running */
/*  If the eventset is not currently running, then the saved value in the   */
/*  EventSet is set to zero without calling this routine.                   */
int
_example_reset( hwd_context_t *ctx, hwd_control_state_t *ctl )
{
    example_context_t *event_ctx = (example_context_t *)ctx;
	(void) ctl;
    
	SUBDBG( "example_reset ctx=%p ctrl=%p...", ctx, ctl );
    
	/* Reset the hardware */
	example_hardware_reset( event_ctx );
    
	return PAPI_OK;
}

/** Triggered by PAPI_shutdown() */
int
_example_shutdown_substrate()
{
    
	SUBDBG( "example_shutdown_substrate..." );
    
    /* Free anything we allocated */
    
	papi_free(example_native_table);
    
	return PAPI_OK;
}

/** Called at thread shutdown */
int
_example_shutdown( hwd_context_t *ctx )
{
    
    (void) ctx;
    
	SUBDBG( "example_shutdown... %p", ctx );
    
	/* Last chance to clean up thread */
    
	return PAPI_OK;
}



/** This function sets various options in the substrate
 @param[in] ctx -- hardware context
 @param[in] code valid are PAPI_SET_DEFDOM, PAPI_SET_DOMAIN, 
 PAPI_SETDEFGRN, PAPI_SET_GRANUL and PAPI_SET_INHERIT
 @param[in] option -- options to be set
 */
int
_example_ctl( hwd_context_t *ctx, int code, _papi_int_option_t *option )
{
    
    (void) ctx;
	(void) code;
	(void) option;
    
	SUBDBG( "example_ctl..." );
    
	return PAPI_OK;
}

/** This function has to set the bits needed to count different domains
 In particular: PAPI_DOM_USER, PAPI_DOM_KERNEL PAPI_DOM_OTHER
 By default return PAPI_EINVAL if none of those are specified
 and PAPI_OK with success
 PAPI_DOM_USER is only user context is counted
 PAPI_DOM_KERNEL is only the Kernel/OS context is counted
 PAPI_DOM_OTHER  is Exception/transient mode (like user TLB misses)
 PAPI_DOM_ALL   is all of the domains
 */
int
_example_set_domain( hwd_control_state_t * cntrl, int domain )
{
    (void) cntrl;
    
	int found = 0;
	SUBDBG( "example_set_domain..." );
    
	if ( PAPI_DOM_USER & domain ) {
		SUBDBG( " PAPI_DOM_USER " );
		found = 1;
	}
	if ( PAPI_DOM_KERNEL & domain ) {
		SUBDBG( " PAPI_DOM_KERNEL " );
		found = 1;
	}
	if ( PAPI_DOM_OTHER & domain ) {
		SUBDBG( " PAPI_DOM_OTHER " );
		found = 1;
	}
	if ( PAPI_DOM_ALL & domain ) {
		SUBDBG( " PAPI_DOM_ALL " );
		found = 1;
	}
	if ( !found )
		return ( PAPI_EINVAL );
    
	return PAPI_OK;
}


/**************************************************************/
/* Naming functions, used to translate event numbers to names */
/**************************************************************/


/** Enumerate Native Events
 *   @param EventCode is the event of interest
 *   @param modifier is one of PAPI_ENUM_FIRST, PAPI_ENUM_EVENTS
 *  If your component has attribute masks then these need to
 *   be handled here as well.
 */
int
_example_ntv_enum_events( unsigned int *EventCode, int modifier )
{
    int cidx,index;
    
    
    switch ( modifier ) {
            
            /* return EventCode of first event */
        case PAPI_ENUM_FIRST:
            /* return the first event that we support */
            
            *EventCode = PAPI_NATIVE_MASK;
            return PAPI_OK;
            
            /* return EventCode of next available event */
        case PAPI_ENUM_EVENTS:
            index = *EventCode & PAPI_NATIVE_AND_MASK & PAPI_COMPONENT_AND_MASK;
            
            /* Make sure we have at least 1 more event after us */
            if ( index < num_events - 1 ) {
                
                /* This assumes a non-sparse mapping of the events */
                *EventCode = *EventCode + 1;
                return PAPI_OK;
            } else {
                return PAPI_ENOEVNT;
            }
            break;
            
        default:
            return PAPI_EINVAL;
    }
    
    return PAPI_EINVAL;
}

/** Takes a native event code and passes back the name 
 * @param EventCode is the native event code
 * @param name is a pointer for the name to be copied to
 * @param len is the size of the name string
 */
int
_example_ntv_code_to_name( unsigned int EventCode, char *name, int len )
{
    int index;
    
    index = EventCode & PAPI_NATIVE_AND_MASK & PAPI_COMPONENT_AND_MASK;
    
    /* Make sure we are in range */
    if (index >= 0 && index < num_events) {
        strncpy( name, example_native_table[index].name, len );  
        return PAPI_OK;
    }
    
    return PAPI_ENOEVNT;
}

/** Takes a native event code and passes back the event description
 * @param EventCode is the native event code
 * @param descr is a pointer for the description to be copied to
 * @param len is the size of the descr string
 */
int
_example_ntv_code_to_descr( unsigned int EventCode, char *descr, int len )
{
    int index;
    index = EventCode & PAPI_NATIVE_AND_MASK & PAPI_COMPONENT_AND_MASK;
    
    /* make sure event is in range */
    if (index >= 0 && index < num_events) {
        strncpy( descr, example_native_table[index].description, len );
        return PAPI_OK;
    }
    
    return PAPI_ENOEVNT;
}

/** Vector that points to entry points for our component */
papi_vector_t _example_vector = {
	.cmp_info = {
		/* default component information */
		/* (unspecified values are initialized to 0) */
        /* we explicitly set them to zero in this example */
        /* to show what settings are available            */
        
		.name = "example.c",
		.short_name = "example",
		.description = "A simple example component",
		.version = "1.15",
		.support_version = "n/a",
		.kernel_version = "n/a",
		.num_cntrs =               EXAMPLE_MAX_SIMULTANEOUS_COUNTERS, 
		.num_mpx_cntrs =           PAPI_MPX_DEF_DEG,
		.default_domain =          PAPI_DOM_USER,
		.available_domains =       PAPI_DOM_USER,
		.default_granularity =     PAPI_GRN_THR,
		.available_granularities = PAPI_GRN_THR,
		.hardware_intr_sig =       PAPI_INT_SIGNAL,
        
		/* component specific cmp_info initializations */
	},
    
	/* sizes of framework-opaque component-private structures */
	.size = {
        /* once per thread */
		.context = sizeof ( example_context_t ),
        /* once per eventset */
		.control_state = sizeof ( example_control_state_t ),
        /* ?? */
		.reg_value = sizeof ( example_register_t ),
        /* ?? */
		.reg_alloc = sizeof ( example_reg_alloc_t ),
	},
    
	/* function pointers */
    /* by default they are set to NULL */
    
	/* Used for general PAPI interactions */
	.start =                _example_start,
	.stop =                 _example_stop,
	.read =                 _example_read,
	.reset =                _example_reset,	
	.write =                _example_write,
	.init_substrate =       _example_init_substrate,	
	.init =                 _example_init,
	.init_control_state =   _example_init_control_state,
	.update_control_state = _example_update_control_state,	
	.ctl =                  _example_ctl,	
	.shutdown =             _example_shutdown,
	.shutdown_substrate =   _example_shutdown_substrate,
	.set_domain =           _example_set_domain,
	/* .cleanup_eventset =     NULL, */
	/* called in add_native_events() */
	/* .allocate_registers =   NULL, */
    
	/* Used for overflow/profiling */
	/* .dispatch_timer =       NULL, */
	/* .get_overflow_address = NULL, */
	/* .stop_profiling =       NULL, */
	/* .set_overflow =         NULL, */
	/* .set_profile =          NULL, */
    
	/* ??? */
	/* .user =                 NULL, */
    
	/* Name Mapping Functions */
	.ntv_enum_events =   _example_ntv_enum_events,
	.ntv_code_to_name =  _example_ntv_code_to_name,
	.ntv_code_to_descr = _example_ntv_code_to_descr,
    /* if .ntv_name_to_code not available, PAPI emulates  */
    /* it by enumerating all events and looking manually  */
   	.ntv_name_to_code  = NULL,
    
    
	/* These are only used by _papi_hwi_get_native_event_info() */
	/* Which currently only uses the info for printing native   */
	/* event info, not for any sort of internal use.            */
	/* .ntv_code_to_bits =  NULL, */
    
};