/* This file utility reports hardware info and native event availability */
/** file papi_native_avail.c
* @page papi_native_avail
* @brief papi_native_avail utility.
* @section NAME
* papi_native_avail - provides detailed information for PAPI native events.
*
* @section Synopsis
*
* @section Description
* papi_native_avail is a PAPI utility program that reports information
* about the native events available on the current platform.
* A native event is an event specific to a specific hardware platform.
* On many platforms, a specific native event may have a number of optional settings.
* In such cases, the native event and the valid settings are presented,
* rather than every possible combination of those settings.
* For each native event, a name, a description, and specific bit patterns are provided.
*
* @section Options
* <ul>
* <li>--help, -h print this help message
* <li>--check, -c print this help message
* <li>-e EVENTNAME display detailed information about named native event
* <li>-i EVENTSTR include only event names that contain EVENTSTR
* <li>-x EVENTSTR exclude any event names that contain EVENTSTR
* <li>--noqual suppress display of event qualifiers (mask and flag) information\n
* </ul>
*
* Processor-specific options
* <ul>
* <li>--darr display events supporting Data Address Range Restriction
* <li>--dear display Data Event Address Register events only
* <li>--iarr display events supporting Instruction Address Range Restriction
* <li>--iear display Instruction Event Address Register events only
* <li>--opcm display events supporting OpCode Matching
* <li>--nogroups suppress display of Event grouping information
* </ul>
*
* @section Bugs
* There are no known bugs in this utility.
* If you find a bug, it should be reported to the
* PAPI Mailing List at <ptools-perfapi@icl.utk.edu>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "papi.h"
#include "print_header.h"
#define EVT_LINE 80
#define EVT_LINE_BUF_SIZE 4096
typedef struct command_flags
{
int help;
int named;
int include;
int xclude;
int check;
char *name, *istr, *xstr;
int darr;
int dear;
int iarr;
int iear;
int opcm;
int qualifiers;
int groups;
} command_flags_t;
static void
print_help( char **argv )
{
printf( "This is the PAPI native avail program.\n" );
printf( "It provides availability and details about PAPI Native Events.\n" );
printf( "Usage: %s [options]\n", argv[0] );
printf( "Options:\n\n" );
printf( "\nGeneral command options:\n" );
printf( "\t-h, --help print this help message\n" );
printf( "\t-c, --check attempts to add each event\n");
printf( "\t-e EVENTNAME display detailed information about named native event\n" );
printf( "\t-i EVENTSTR include only event names that contain EVENTSTR\n" );
printf( "\t-x EVENTSTR exclude any event names that contain EVENTSTR\n" );
printf( "\t--noqual suppress display of event qualifiers (mask and flag) information\n" );
printf( "\nProcessor-specific options:\n");
printf( "\t--darr display events supporting Data Address Range Restriction\n" );
printf( "\t--dear display Data Event Address Register events only\n" );
printf( "\t--iarr display events supporting Instruction Address Range Restriction\n" );
printf( "\t--iear display Instruction Event Address Register events only\n" );
printf( "\t--opcm display events supporting OpCode Matching\n" );
printf( "\t--nogroups suppress display of Event grouping information\n" );
printf( "\n" );
}
static int
no_str_arg( char *arg )
{
return ( ( arg == NULL ) || ( strlen( arg ) == 0 ) || ( arg[0] == '-' ) );
}
static void
parse_args( int argc, char **argv, command_flags_t * f )
{
int i;
/* Look for all currently defined commands */
memset( f, 0, sizeof ( command_flags_t ) );
f->qualifiers = 1;
f->groups = 1;
for ( i = 1; i < argc; i++ ) {
if ( !strcmp( argv[i], "--darr" ) )
f->darr = 1;
else if ( !strcmp( argv[i], "--dear" ) )
f->dear = 1;
else if ( !strcmp( argv[i], "--iarr" ) )
f->iarr = 1;
else if ( !strcmp( argv[i], "--iear" ) )
f->iear = 1;
else if ( !strcmp( argv[i], "--opcm" ) )
f->opcm = 1;
else if ( !strcmp( argv[i], "--noqual" ) )
f->qualifiers = 0;
else if ( !strcmp( argv[i], "--nogroups" ) )
f->groups = 0;
else if ( !strcmp( argv[i], "-e" ) ) {
f->named = 1;
i++;
f->name = argv[i];
if ( i >= argc || no_str_arg( f->name ) ) {
printf( "Invalid argument for -e\n");
exit(1);
}
} else if ( !strcmp( argv[i], "-i" ) ) {
f->include = 1;
i++;
f->istr = argv[i];
if ( i >= argc || no_str_arg( f->istr ) ) {
printf( "Invalid argument for -i\n");
exit(1);
}
} else if ( !strcmp( argv[i], "-x" ) ) {
f->xclude = 1;
i++;
f->xstr = argv[i];
if ( i >= argc || no_str_arg( f->xstr ) ) {
printf( "Invalid argument for -x\n");
exit(1);
}
} else if ( strstr( argv[i], "-h" ) ) {
f->help = 1;
} else if ( !strcmp( argv[i], "-c" ) || !strcmp( argv[i], "--check" ) ) {
f->check = 1;
} else {
printf( "%s is not supported\n", argv[i] );
exit(1);
}
}
/* if help requested, print and bail */
if ( f->help ) {
print_help( argv);
exit( 1 );
}
}
static void
space_pad( char *str, int spaces )
{
while ( spaces-- > 0 )
strcat( str, " " );
}
unsigned int event_available = 0;
unsigned int event_output_buffer_size = 0;
char *event_output_buffer = NULL;
static void
check_event( PAPI_event_info_t * info )
{
int EventSet = PAPI_NULL;
// if this event has already passed the check test, no need to try this one again
if (event_available) {
return;
}
if (PAPI_create_eventset (&EventSet) == PAPI_OK) {
if (PAPI_add_named_event (EventSet, info->symbol) == PAPI_OK) {
PAPI_remove_named_event (EventSet, info->symbol);
event_available = 1;
} // else printf("********** PAPI_add_named_event( %s ) failed: event could not be added \n", info->symbol);
if ( PAPI_destroy_eventset( &EventSet ) != PAPI_OK ) {
printf("********** Call to destroy eventset failed when trying to check event '%s' **********\n", info->symbol);
}
}
return;
}
static int
format_event_output( PAPI_event_info_t * info, int offset)
{
unsigned int i, j = 0;
char event_line_buffer[EVT_LINE_BUF_SIZE];
char event_line_units[100];
/* indent by offset */
if ( offset ) {
// this one is used for event qualifiers
sprintf(event_line_buffer, "| %-73s|\n", info->symbol);
}
else {
// this one is used for new events
sprintf(event_line_buffer, "| %-73s%4s|\n", info->symbol, "<-->");
}
while ( j <= strlen( info->long_descr ) ) {
// The event_line_buffer is used to collect an event or mask name and its description.
// The description will be folded to keep the length of output lines reasonable. So this
// buffer may contain multiple lines of print output. Check to make sure there is room
// for another line of print output. If there is not enough room for another output line
// just exit the loop and truncate the description field (the buffer is big enough this
// should not happen).
if ((EVT_LINE_BUF_SIZE - strlen(event_line_buffer)) < EVT_LINE) {
printf ("Event or mask description has been truncated.\n");
break;
}
// get amount of description that will fit in an output line
i = EVT_LINE - 12 - 2;
// start of a description line
strcat(event_line_buffer,"| " );
// if we need to copy less than what fits in this line, move it and exit loop
if (i > strlen(&info->long_descr[j])) {
strcat( event_line_buffer, &info->long_descr[j]);
space_pad( event_line_buffer, i - strlen(&info->long_descr[j]));
strcat( event_line_buffer, "|\n" );
break;
}
// move what will fit into the line then loop back to do the rest in a new line
int k = strlen(event_line_buffer);
strncat( event_line_buffer, &info->long_descr[j], i );
event_line_buffer[k+i] = '\0';
strcat( event_line_buffer, "|\n" );
// bump index past what we copied
j += i;
}
// also show the units for this event if a unit name has been set
event_line_units[0] = '\0';
if (info->units[0] != 0) {
sprintf(event_line_units, "| Units: %-66s|\n", info->units );
}
// get the amount of used space in the output buffer
int out_buf_used = 0;
if ((event_output_buffer_size > 0) && (event_output_buffer != NULL)) {
out_buf_used = strlen(event_output_buffer);
}
// if this will not fit in output buffer, make it bigger
if (event_output_buffer_size < out_buf_used + strlen(event_line_buffer) + strlen(event_line_units) + 1) {
if (event_output_buffer_size == 0) {
event_output_buffer_size = 1024;
event_output_buffer = calloc(1, event_output_buffer_size);
} else {
event_output_buffer_size += 1024;
event_output_buffer = realloc(event_output_buffer, event_output_buffer_size);
}
}
// make sure we got the memory we asked for
if (event_output_buffer == NULL) {
fprintf(stderr,"Error! Allocation of output buffer memory failed.\n");
return errno;
}
strcat(event_output_buffer, event_line_buffer);
strcat(event_output_buffer, event_line_units);
return 0;
}
static void
print_event_output(int val_flag)
{
// first we need to update the available flag at the beginning of the buffer
// this needs to reflect if this event name by itself or the event name with one of the qualifiers worked
// if none of the combinations worked then we will show the event as not available
char *val_flag_ptr = strstr(event_output_buffer, "<-->");
if (val_flag_ptr != NULL) {
if ((val_flag) && (event_available == 0)) {
// event is not available, update the place holder (replace the <--> with <NA>)
*(val_flag_ptr+1) = 'N';
*(val_flag_ptr+2) = 'A';
} else {
event_available = 0; // reset this flag for next event
// event is available, just remove the place holder (replace the <--> with spaces)
*val_flag_ptr = ' ';
*(val_flag_ptr+1) = ' ';
*(val_flag_ptr+2) = ' ';
*(val_flag_ptr+3) = ' ';
}
}
// now we can finally send this events output to the user
printf( "%s", event_output_buffer);
// printf( "--------------------------------------------------------------------------------\n" );
event_output_buffer[0] = '\0'; // start the next event with an empty buffer
return;
}
static int
parse_event_qualifiers( PAPI_event_info_t * info )
{
char *pmask,*ptr;
/* handle the PAPI component-style events which have a component:::event type */
if ((ptr=strstr(info->symbol, ":::"))) {
ptr+=3;
/* handle libpfm4-style events which have a pmu::event type event name */
} else if ((ptr=strstr(info->symbol, "::"))) {
ptr+=2;
}
else {
ptr=info->symbol;
}
if ( ( pmask = strchr( ptr, ':' ) ) == NULL ) {
return ( 0 );
}
memmove( info->symbol, pmask, ( strlen(pmask) + 1 ) * sizeof(char) );
// The description field contains the event description followed by a tag 'masks:'
// and then the mask description (if there was a mask with this event). The following
// code isolates the mask description part of this information.
pmask = strstr( info->long_descr, "masks:" );
if ( pmask == NULL ) {
info->long_descr[0] = 0;
} else {
pmask += 6; // bump pointer past 'masks:' identifier in description
memmove( info->long_descr, pmask, (strlen(pmask) + 1) * sizeof(char) );
}
return ( 1 );
}
int
main( int argc, char **argv )
{
int i, k;
int num_events;
int num_cmp_events = 0;
int retval;
PAPI_event_info_t info;
const PAPI_hw_info_t *hwinfo = NULL;
command_flags_t flags;
int enum_modifier;
int numcmp, cid;
/* Initialize before parsing the input arguments */
retval = PAPI_library_init( PAPI_VER_CURRENT );
if ( retval != PAPI_VER_CURRENT ) {
fprintf(stderr, "Error! PAPI_library_init\n");
return retval;
}
/* Parse the command-line arguments */
parse_args( argc, argv, &flags );
/* Set enum modifier mask */
if ( flags.dear )
enum_modifier = PAPI_NTV_ENUM_DEAR;
else if ( flags.darr )
enum_modifier = PAPI_NTV_ENUM_DARR;
else if ( flags.iear )
enum_modifier = PAPI_NTV_ENUM_IEAR;
else if ( flags.iarr )
enum_modifier = PAPI_NTV_ENUM_IARR;
else if ( flags.opcm )
enum_modifier = PAPI_NTV_ENUM_OPCM;
else
enum_modifier = PAPI_ENUM_EVENTS;
retval = PAPI_set_debug( PAPI_VERB_ECONT );
if ( retval != PAPI_OK ) {
fprintf(stderr,"Error! PAPI_set_debug\n");
return retval;
}
retval = papi_print_header( "Available native events and hardware information.\n", &hwinfo );
if ( retval != PAPI_OK ) {
fprintf(stderr,"Error! PAPI_get_hardware_info\n");
return 2;
}
/* Do this code if the event name option was specified on the commandline */
if ( flags.named ) {
if ( PAPI_event_name_to_code( flags.name, &i ) == PAPI_OK ) {
if ( PAPI_get_event_info( i, &info ) == PAPI_OK ) {
printf( "Event name: %s\n", info.symbol);
printf( "Description: %s\n", info.long_descr );
/* handle the PAPI component-style events which have a component:::event type */
char *ptr;
if ((ptr=strstr(flags.name, ":::"))) {
ptr+=3;
/* handle libpfm4-style events which have a pmu::event type event name */
} else if ((ptr=strstr(flags.name, "::"))) {
ptr+=2;
}
else {
ptr=flags.name;
}
/* if event qualifiers exist but none specified, process all */
if ( !strchr( ptr, ':' ) ) {
if ( PAPI_enum_event( &i, PAPI_NTV_ENUM_UMASKS ) == PAPI_OK ) {
printf( "\nQualifiers: Name -- Description\n" );
do {
retval = PAPI_get_event_info( i, &info );
if ( retval == PAPI_OK ) {
if ( parse_event_qualifiers( &info ) ) {
printf( " Info: %10s -- %s\n", info.symbol, info.long_descr );
}
}
} while ( PAPI_enum_event( &i, PAPI_NTV_ENUM_UMASKS ) == PAPI_OK );
}
}
}
} else {
printf("Sorry, an event by the name '%s' could not be found.\n",
flags.name);
printf("Is it typed correctly?\n\n");
exit( 1 );
}
return 0;
}
// Look at all the events and qualifiers and print the information the user has asked for */
numcmp = PAPI_num_components( );
num_events = 0;
for ( cid = 0; cid < numcmp; cid++ ) {
const PAPI_component_info_t *component;
component=PAPI_get_component_info(cid);
/* Skip disabled components */
if (component->disabled) continue;
printf( "===============================================================================\n" );
printf( " Native Events in Component: %s\n",component->name);
printf( "===============================================================================\n" );
// show this component has not found any events yet
num_cmp_events = 0;
/* Always ASK FOR the first event */
/* Don't just assume it'll be the first numeric value */
i = 0 | PAPI_NATIVE_MASK;
retval=PAPI_enum_cmp_event( &i, PAPI_ENUM_FIRST, cid );
if (retval==PAPI_OK) {
do {
memset( &info, 0, sizeof ( info ) );
retval = PAPI_get_event_info( i, &info );
/* This event may not exist */
if ( retval != PAPI_OK ) continue;
/* Bail if event name doesn't contain include string */
if ( flags.include && !strstr( info.symbol, flags.istr ) ) continue;
/* Bail if event name does contain exclude string */
if ( flags.xclude && strstr( info.symbol, flags.xstr ) ) continue;
// if not the first event in this component, put out a divider
if (num_cmp_events) {
printf( "--------------------------------------------------------------------------------\n" );
}
/* count only events that are actually processed */
num_events++;
num_cmp_events++;
if (flags.check){
check_event(&info);
}
format_event_output( &info, 0);
/* modifier = PAPI_NTV_ENUM_GROUPS returns event codes with a
groups id for each group in which this
native event lives, in bits 16 - 23 of event code
terminating with PAPI_ENOEVNT at the end of the list.
*/
/* This is an IBM Power issue */
if ( flags.groups ) {
k = i;
if ( PAPI_enum_cmp_event( &k, PAPI_NTV_ENUM_GROUPS, cid ) == PAPI_OK ) {
printf( "Groups: " );
do {
printf( "%4d", ( ( k & PAPI_NTV_GROUP_AND_MASK ) >>
PAPI_NTV_GROUP_SHIFT ) - 1 );
} while ( PAPI_enum_cmp_event( &k, PAPI_NTV_ENUM_GROUPS, cid ) ==PAPI_OK );
printf( "\n" );
}
}
// If the user has asked us to check the events then we need to
// walk the list of qualifiers and try to check the event with each one.
// Even if the user does not want to display the qualifiers this is necessary
// to be able to correctly report which events can be used on this system.
//
// We also need to walk the list if the user wants to see the qualifiers.
if (flags.qualifiers || flags.check){
k = i;
if ( PAPI_enum_cmp_event( &k, PAPI_NTV_ENUM_UMASKS, cid ) == PAPI_OK ) {
// clear event string using first mask
char first_event_mask_string[PAPI_HUGE_STR_LEN] = "";
do {
retval = PAPI_get_event_info( k, &info );
if ( retval == PAPI_OK ) {
// if first event mask string not set yet, set it now
if (strlen(first_event_mask_string) == 0) {
strcpy (first_event_mask_string, info.symbol);
}
if ( flags.check ) {
check_event(&info);
}
// now test if the event qualifiers should be displayed to the user
if ( flags.qualifiers ) {
if ( parse_event_qualifiers( &info ) )
format_event_output( &info, 2);
}
}
} while ( PAPI_enum_cmp_event( &k, PAPI_NTV_ENUM_UMASKS, cid ) == PAPI_OK );
// if we are validating events and the event_available flag is not set yet, try a few more combinations
if (flags.check && (event_available == 0)) {
// try using the event with the first mask defined for the event and the cpu mask
// this is a kludge but many of the uncore events require an event specific mask (usually
// the first one defined will do) and they all require the cpu mask
strcpy (info.symbol, first_event_mask_string);
strcat (info.symbol, ":cpu=1");
check_event(&info);
}
if (flags.check && (event_available == 0)) {
// an even bigger kludge is that there are 4 snpep_unc_pcu events which require the 'ff' and 'cpu' qualifiers to work correctly.
// if nothing else has worked, this code will try those two qualifiers with the current event name to see if it works
strcpy (info.symbol, first_event_mask_string);
char *wptr = strrchr (info.symbol, ':');
if (wptr != NULL) {
*wptr = '\0';
strcat (info.symbol, ":ff=64:cpu=1");
check_event(&info);
}
}
}
}
print_event_output(flags.check);
} while (PAPI_enum_cmp_event( &i, enum_modifier, cid ) == PAPI_OK );
}
}
if (num_cmp_events != 0) {
printf( "--------------------------------------------------------------------------------\n" );
}
printf( "\nTotal events reported: %d\n", num_events );
return 0;
}