/** * @file linux-lmsensors.c * @author Daniel Lucio * @author Joachim Protze * @author Heike Jagode * jagode@eecs.utk.edu * * @ingroup papi_components * * * LM_SENSORS component * * Tested version of lm_sensors: 3.1.1 * * @brief * This file has the source code for a component that enables PAPI-C to access * hardware monitoring sensors through the libsensors library. This code will * dynamically create a native events table for all the sensors that can be * accesed by the libsensors library. * In order to learn more about libsensors, visit: (http://www.lm-sensors.org) * * Notes: * - I used the ACPI and MX components to write this component. A lot of the * code in this file mimics what other components already do. * - The return values are scaled by 1000 because PAPI can not return decimals. * - A call of PAPI_read can take up to 2 seconds while using lm_sensors! * - Please remember that libsensors uses the GPL license. */ /* Headers required by libsensors */ #include #include #include #include #include /* Headers required by PAPI */ #include "papi.h" #include "papi_internal.h" #include "papi_vector.h" #include "papi_memory.h" /************************* DEFINES SECTION *********************************** *******************************************************************************/ // time in usecs #define LM_SENSORS_REFRESHTIME 200000 /** Structure that stores private information of each event */ typedef struct _lmsensors_register { /* This is used by the framework.It likes it to be !=0 to do somehting */ unsigned int selector; /* These are the only information needed to locate a libsensors event */ const sensors_chip_name *name; int subfeat_nr; } _lmsensors_register_t; /* * The following structures mimic the ones used by other components. It is more * convenient to use them like that as programming with PAPI makes specific * assumptions for them. */ /** This structure is used to build the table of events */ typedef struct _lmsensors_native_event_entry { _lmsensors_register_t resources; char name[PAPI_MAX_STR_LEN]; char description[PAPI_MAX_STR_LEN]; unsigned int count; } _lmsensors_native_event_entry_t; typedef struct _lmsensors_reg_alloc { _lmsensors_register_t ra_bits; } _lmsensors_reg_alloc_t; typedef struct _lmsensors_control_state { long_long lastupdate; } _lmsensors_control_state_t; typedef struct _lmsensors_context { _lmsensors_control_state_t state; } _lmsensors_context_t; /************************* GLOBALS SECTION *********************************** *******************************************************************************/ /* This table contains the LM_SENSORS native events */ static _lmsensors_native_event_entry_t *lm_sensors_native_table; /* number of events in the table*/ static int num_events = 0; static long_long *cached_counts = NULL; // used for caching readings static int (*sensors_initPtr)(FILE *input); static void (*sensors_cleanupPtr)(void); static int (*sensors_snprintf_chip_namePtr)(char *str, size_t size, const sensors_chip_name *chip); static char *(*sensors_get_labelPtr)(const sensors_chip_name *name, const sensors_feature *feature); static int (*sensors_get_valuePtr)(const sensors_chip_name *name, int subfeat_nr, double *value); static const sensors_chip_name *(*sensors_get_detected_chipsPtr)(const sensors_chip_name *match, int *nr); static const sensors_feature *(*sensors_get_featuresPtr)(const sensors_chip_name *name, int *nr); static const sensors_subfeature *(*sensors_get_all_subfeaturesPtr)(const sensors_chip_name *name, const sensors_feature *feature, int *nr); // file handles used to access lmsensors libraries with dlopen static void* dl1 = NULL; static int link_lmsensors_libraries (); papi_vector_t _lmsensors_vector; /****************************************************************************** ******** BEGIN FUNCTIONS USED INTERNALLY SPECIFIC TO THIS COMPONENT ******** *****************************************************************************/ /* * Counts number of events available in this system */ static unsigned detectSensors( void ) { unsigned id = 0; int chip_nr = 0; const sensors_chip_name *chip_name; /* Loop through all the chips, features, subfeatures found */ while ( ( chip_name = sensors_get_detected_chipsPtr( NULL, &chip_nr ) ) != NULL ) { int a = 0, b; const sensors_feature *feature; while ( ( feature = sensors_get_featuresPtr( chip_name, &a ) ) ) { b = 0; while ( ( sensors_get_all_subfeaturesPtr( chip_name, feature, &b ) ) ) { id++; } } } return id; } /* * Create the native events for particulare component (!= 0) */ static unsigned createNativeEvents( void ) { unsigned id = 0; unsigned int count; int chip_nr = 0; const sensors_chip_name *chip_name; /* component name and description */ strcpy( _lmsensors_vector.cmp_info.short_name, "lm_sensors" ); strcpy( _lmsensors_vector.cmp_info.description, "lm-sensors provides tools for monitoring the hardware health" ); /* Loop through all the chips found */ while ( ( chip_name = sensors_get_detected_chipsPtr( NULL, &chip_nr ) ) != NULL ) { int a, b; const sensors_feature *feature; const sensors_subfeature *sub; char chipnamestring[PAPI_MIN_STR_LEN]; // lm_sensors_native_table[id].count = 0; /* get chip name from its internal representation */ sensors_snprintf_chip_namePtr( chipnamestring, PAPI_MIN_STR_LEN, chip_name ); a = 0; /* Loop through all the features found */ while ( ( feature = sensors_get_featuresPtr( chip_name, &a ) ) ) { char *featurelabel; if ( !( featurelabel = sensors_get_labelPtr( chip_name, feature ))) { fprintf( stderr, "ERROR: Can't get label of feature %s!\n", feature->name ); continue; } b = 0; /* Loop through all the subfeatures found */ while ((sub=sensors_get_all_subfeaturesPtr(chip_name,feature,&b))) { count = 0; /* Save native event data */ sprintf( lm_sensors_native_table[id].name, "%s.%s.%s", chipnamestring, featurelabel, sub->name ); strncpy( lm_sensors_native_table[id].description, lm_sensors_native_table[id].name, PAPI_MAX_STR_LEN ); lm_sensors_native_table[id].description[PAPI_MAX_STR_LEN-1] = '\0'; /* The selector has to be !=0 . Starts with 1 */ lm_sensors_native_table[id].resources.selector = id + 1; /* Save the actual references to this event */ lm_sensors_native_table[id].resources.name = chip_name; lm_sensors_native_table[id].resources.subfeat_nr = sub->number; count = sub->number; /* increment the table index counter */ id++; } // lm_sensors_native_table[id].count = count + 1; free( featurelabel ); } } /* Return the number of events created */ return id; } /* * Returns the value of the event with index 'i' in lm_sensors_native_table * This value is scaled by 1000 to cope with the lack to return decimal numbers * with PAPI */ static long_long getEventValue( unsigned event_id ) { double value; int res; res = sensors_get_valuePtr( lm_sensors_native_table[event_id].resources.name, lm_sensors_native_table[event_id].resources. subfeat_nr, &value ); if ( res < 0 ) { fprintf( stderr, "libsensors(): Could not read event #%d!\n", event_id ); return -1; } return ( ( long_long ) ( value * 1000 ) ); } /***************************************************************************** ******************* BEGIN PAPI's COMPONENT REQUIRED FUNCTIONS ************* *****************************************************************************/ /* * This is called whenever a thread is initialized */ static int _lmsensors_init_thread( hwd_context_t *ctx ) { ( void ) ctx; return PAPI_OK; } /* 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) */ static int _lmsensors_init_component( int cidx ) { int res; (void) cidx; /* link in all the lmsensor libraries and resolve the symbols we need to use */ if (link_lmsensors_libraries() != PAPI_OK) { SUBDBG ("Dynamic link of lmsensors libraries failed, component will be disabled.\n"); SUBDBG ("See disable reason in papi_component_avail output for more details.\n"); return (PAPI_ENOSUPP); } /* Initialize libsensors library */ if ( ( res = sensors_initPtr( NULL ) ) != 0 ) { strncpy(_lmsensors_vector.cmp_info.disabled_reason, "Cannot enable libsensors",PAPI_MAX_STR_LEN); return res; } /* Create dyanmic events table */ num_events = detectSensors( ); SUBDBG("Found %d sensors\n",num_events); _lmsensors_vector.cmp_info.num_mpx_cntrs = num_events; _lmsensors_vector.cmp_info.num_cntrs = num_events; if ( ( lm_sensors_native_table = calloc( num_events, sizeof ( _lmsensors_native_event_entry_t ))) == NULL ) { strncpy(_lmsensors_vector.cmp_info.disabled_reason, "Could not malloc room",PAPI_MAX_STR_LEN); return PAPI_ENOMEM; } cached_counts = (long long*) calloc(num_events, sizeof(long long)); if (cached_counts == NULL) { strncpy(_lmsensors_vector.cmp_info.disabled_reason, "Could not malloc room",PAPI_MAX_STR_LEN); return PAPI_ENOMEM; } if ( ( unsigned ) num_events != createNativeEvents( ) ) { strncpy(_lmsensors_vector.cmp_info.disabled_reason, "LM_SENSOR number mismatch",PAPI_MAX_STR_LEN); return PAPI_ECMP; } _lmsensors_vector.cmp_info.num_native_events=num_events; _lmsensors_vector.cmp_info.num_cntrs=num_events; return PAPI_OK; } /* * Link the necessary lmsensors libraries to use the lmsensors * component. If any of them can not be found, then the lmsensors * component will just be disabled. This is done at runtime so that a * version of PAPI built with the Infiniband component can be * installed and used on systems which have the lmsensors libraries * installed and on systems where these libraries are not installed. */ static int link_lmsensors_libraries () { /* Need to link in the lmsensors libraries, if not found disable the component */ dl1 = dlopen("libsensors.so", RTLD_NOW | RTLD_GLOBAL); if (!dl1) { strncpy(_lmsensors_vector.cmp_info.disabled_reason, "lmsensors library libsensors.so not found.",PAPI_MAX_STR_LEN); return ( PAPI_ENOSUPP ); } sensors_initPtr = dlsym(dl1, "sensors_init"); if (dlerror() != NULL) { strncpy(_lmsensors_vector.cmp_info.disabled_reason, "lmsensor function sensors_init.",PAPI_MAX_STR_LEN); return ( PAPI_ENOSUPP ); } sensors_cleanupPtr = dlsym(dl1, "sensors_cleanup"); if (dlerror() != NULL) { strncpy(_lmsensors_vector.cmp_info.disabled_reason, "lmsensor function sensors_cleanup.",PAPI_MAX_STR_LEN); return ( PAPI_ENOSUPP ); } sensors_snprintf_chip_namePtr = dlsym(dl1, "sensors_snprintf_chip_name"); if (dlerror() != NULL) { strncpy(_lmsensors_vector.cmp_info.disabled_reason, "lmsensor function sensors_snprintf_chip_name.",PAPI_MAX_STR_LEN); return ( PAPI_ENOSUPP ); } sensors_get_labelPtr = dlsym(dl1, "sensors_get_label"); if (dlerror() != NULL) { strncpy(_lmsensors_vector.cmp_info.disabled_reason, "lmsensor function sensors_get_label.",PAPI_MAX_STR_LEN); return ( PAPI_ENOSUPP ); } sensors_get_valuePtr = dlsym(dl1, "sensors_get_value"); if (dlerror() != NULL) { strncpy(_lmsensors_vector.cmp_info.disabled_reason, "lmsensor function sensors_get_value.",PAPI_MAX_STR_LEN); return ( PAPI_ENOSUPP ); } sensors_get_detected_chipsPtr = dlsym(dl1, "sensors_get_detected_chips"); if (dlerror() != NULL) { strncpy(_lmsensors_vector.cmp_info.disabled_reason, "lmsensor function sensors_get_detected_chips.",PAPI_MAX_STR_LEN); return ( PAPI_ENOSUPP ); } sensors_get_featuresPtr = dlsym(dl1, "sensors_get_features"); if (dlerror() != NULL) { strncpy(_lmsensors_vector.cmp_info.disabled_reason, "lmsensor function sensors_get_features.",PAPI_MAX_STR_LEN); return ( PAPI_ENOSUPP ); } sensors_get_all_subfeaturesPtr = dlsym(dl1, "sensors_get_all_subfeatures"); if (dlerror() != NULL) { strncpy(_lmsensors_vector.cmp_info.disabled_reason, "lmsensor function sensors_get_all_subfeatures.",PAPI_MAX_STR_LEN); return ( PAPI_ENOSUPP ); } return ( PAPI_OK ); } /* * Control of counters (Reading/Writing/Starting/Stopping/Setup) * functions */ static int _lmsensors_init_control_state( hwd_control_state_t *ctl ) { int i; for ( i = 0; i < num_events; i++ ) cached_counts[i] = getEventValue( i ); ( ( _lmsensors_control_state_t * ) ctl )->lastupdate = PAPI_get_real_usec( ); return PAPI_OK; } /* * */ static int _lmsensors_start( hwd_context_t *ctx, hwd_control_state_t *ctl ) { ( void ) ctx; ( void ) ctl; return PAPI_OK; } /* * */ static int _lmsensors_stop( hwd_context_t *ctx, hwd_control_state_t *ctl ) { ( void ) ctx; ( void ) ctl; return PAPI_OK; } /* * */ static int _lmsensors_read( hwd_context_t *ctx, hwd_control_state_t *ctl, long_long ** events, int flags ) { ( void ) ctx; ( void ) flags; long long start = PAPI_get_real_usec( ); int i; _lmsensors_control_state_t *control=(_lmsensors_control_state_t *)ctl; if ( start - control->lastupdate > 200000 ) { // cache refresh for ( i = 0; i < num_events; i++ ) { cached_counts[i] = getEventValue( i ); } control->lastupdate = PAPI_get_real_usec( ); } *events = cached_counts; return PAPI_OK; } static int _lmsensors_shutdown_component( void ) { if (cached_counts) free(cached_counts); /* Call the libsensors cleaning function before leaving */ sensors_cleanupPtr( ); return PAPI_OK; } static int _lmsensors_shutdown_thread( hwd_context_t *ctx ) { ( void ) ctx; return PAPI_OK; } /* This function sets various options in the component * The valid codes being passed in are PAPI_SET_DEFDOM, * PAPI_SET_DOMAIN, PAPI_SETDEFGRN, PAPI_SET_GRANUL * and PAPI_SET_INHERIT */ static int _lmsensors_ctl( hwd_context_t *ctx, int code, _papi_int_option_t *option ) { ( void ) ctx; ( void ) code; ( void ) option; return PAPI_OK; } static int _lmsensors_update_control_state( hwd_control_state_t *ctl, NativeInfo_t * native, int count, hwd_context_t *ctx ) { int i, index; ( void ) ctx; ( void ) ctl; for ( i = 0; i < count; i++ ) { index = native[i].ni_event; native[i].ni_position = lm_sensors_native_table[index].resources.selector - 1; } return PAPI_OK; } /* * As I understand it, all data reported by these interfaces will be system wide */ static int _lmsensors_set_domain( hwd_control_state_t *ctl, int domain ) { (void) ctl; if ( PAPI_DOM_ALL != domain ) return ( PAPI_EINVAL ); return ( PAPI_OK ); } /* * */ static int _lmsensors_reset( hwd_context_t *ctx, hwd_control_state_t *ctl ) { ( void ) ctx; ( void ) ctl; return PAPI_OK; } /* * Native Event functions */ static int _lmsensors_ntv_enum_events( unsigned int *EventCode, int modifier ) { switch ( modifier ) { case PAPI_ENUM_FIRST: *EventCode = 0; return PAPI_OK; break; case PAPI_ENUM_EVENTS: { int index = *EventCode; if ( index < num_events - 1 ) { *EventCode = *EventCode + 1; return PAPI_OK; } else return PAPI_ENOEVNT; break; } default: return PAPI_EINVAL; } return PAPI_EINVAL; } /* * */ static int _lmsensors_ntv_code_to_name( unsigned int EventCode, char *name, int len ) { int index = EventCode; if (index>=0 && index=0 && index