#include #include #include #include #include #include "cpu.h" netsnmp_feature_child_of(hardware_cpu, libnetsnmpmibs); netsnmp_feature_child_of(hardware_cpu_copy_stats, hardware_cpu); netsnmp_feature_child_of(hardware_cpu_load, hardware_cpu); netsnmp_feature_child_of(hardware_cpu_get_cache, hardware_cpu); netsnmp_feature_child_of(hardware_cpu_get_byName, hardware_cpu); static void _cpu_update_stats( unsigned int, void* ); static int _cpuAutoUpdate = 5; static int _cpuHistoryLen; int cpu_num = 0; static netsnmp_cpu_info *_cpu_head = NULL; static netsnmp_cpu_info *_cpu_tail = NULL; static netsnmp_cache *_cpu_cache = NULL; void init_cpu( void ) { oid nsCPU[] = { 1, 3, 6, 1, 4, 1, 8072, 1, 33 }; /* * If we're sampling the CPU statistics automatically, * then arrange for this to be triggered regularly, * keeping sufficient samples to cover the last minute. * If the system-specific code has already initialised * the list of CPU entries, then retrieve the first set * of stats immediately. * Otherwise, wait until the regular sampling kicks in. * * If we're not sampling these statistics regularly, * create a suitable cache handler instead. */ if ( _cpuAutoUpdate ) { _cpuHistoryLen = 60/_cpuAutoUpdate; snmp_alarm_register( _cpuAutoUpdate, SA_REPEAT, _cpu_update_stats, NULL ); if ( _cpu_head ) _cpu_update_stats( 0, NULL ); } else _cpu_cache = netsnmp_cache_create( 5, netsnmp_cpu_arch_load, NULL, nsCPU, OID_LENGTH(nsCPU)); } void shutdown_cpu( void ) { while ( _cpu_head ) { netsnmp_cpu_info *tmp = _cpu_head; _cpu_head = _cpu_head->next; SNMP_FREE(tmp->history); SNMP_FREE(tmp); } _cpu_tail = NULL; } netsnmp_cpu_info *netsnmp_cpu_get_first( void ) { return _cpu_head; } netsnmp_cpu_info *netsnmp_cpu_get_next( netsnmp_cpu_info *this_ptr ) { return ( this_ptr ? this_ptr->next : NULL ); } /* * Work with a list of CPU entries, indexed numerically */ netsnmp_cpu_info *netsnmp_cpu_get_byIdx( int idx, int create ) { netsnmp_cpu_info *cpu, *cpu2; /* * Find the specified CPU entry */ DEBUGMSGTL(("cpu", "cpu_get_byIdx %d ", idx)); for ( cpu=_cpu_head; cpu; cpu=cpu->next ) { if ( cpu->idx == idx ) { DEBUGMSG(("cpu", "(found)\n")); return cpu; } } if (!create) { DEBUGMSG(("cpu", "(not found)\n")); return NULL; } /* * Create a new CPU entry, and insert it into the list.... */ cpu = SNMP_MALLOC_TYPEDEF( netsnmp_cpu_info ); if (!cpu) { DEBUGMSG(("cpu", "(failed)\n")); return NULL; } DEBUGMSG(("cpu", "(created)\n")); cpu->idx = idx; /* ... either as the first (or only) entry.... */ if ( !_cpu_head || _cpu_head->idx > idx ) { cpu->next = _cpu_head; _cpu_head = cpu; if (!_cpu_tail) _cpu_tail = cpu; return cpu; } /* ... or in the appropriate position */ for ( cpu2=_cpu_head; cpu2; cpu2=cpu2->next ) { if ( !cpu2->next || cpu2->next->idx > idx ) { cpu->next = cpu2->next; cpu2->next = cpu; if (!cpu->next) _cpu_tail = cpu; return cpu; } } SNMP_FREE(cpu); /* just in case */ return NULL; /* Shouldn't happen! */ } /* * Work with a list of CPU entries, indexed by name */ #ifndef NETSNMP_FEATURE_REMOVE_HARDWARE_CPU_GET_BYNAME netsnmp_cpu_info *netsnmp_cpu_get_byName( char *name, int create ) { netsnmp_cpu_info *cpu; /* * Find the specified CPU entry */ for ( cpu=_cpu_head; cpu; cpu=cpu->next ) { if ( !strcmp(cpu->name, name)) return cpu; } if (!create) return NULL; /* * Create a new CPU entry, and append it to the list */ cpu = SNMP_MALLOC_TYPEDEF( netsnmp_cpu_info ); if (!cpu) return NULL; if (strlen(name) >= sizeof(cpu->name)) { free(cpu); snmp_log(LOG_ERR, "Name of CPU is too large: %s\n", name); return NULL; } strlcpy(cpu->name, name, sizeof(cpu)); if ( _cpu_tail ) { cpu->idx = _cpu_tail->idx+1; _cpu_tail->next = cpu; _cpu_tail = cpu; } else { cpu->idx = 0; _cpu_head = cpu; _cpu_tail = cpu; } return cpu; } #endif /* NETSNMP_FEATURE_REMOVE_HARDWARE_CPU_GET_BYNAME */ #ifndef NETSNMP_FEATURE_REMOVE_HARDWARE_CPU_GET_CACHE netsnmp_cache *netsnmp_cpu_get_cache( void ) { return _cpu_cache; } #endif /* NETSNMP_FEATURE_REMOVE_HARDWARE_CPU_GET_CACHE */ #ifndef NETSNMP_FEATURE_REMOVE_HARDWARE_CPU_LOAD int netsnmp_cpu_load( void ) { /* * If we're automatically updating the stats regularly, * then don't invoke the cache handling. */ return ( _cpuAutoUpdate ? 1 : netsnmp_cache_check_and_reload( _cpu_cache )); } #endif /* NETSNMP_FEATURE_REMOVE_HARDWARE_CPU_LOAD */ /* * Call the system-specific load routine regularly, * keeping track of the relevant earlier results. */ static void _cpu_update_stats( unsigned int reg, void* magic ) { netsnmp_cpu_info *cpu; int i; for ( cpu=_cpu_head; cpu; cpu=cpu->next ) { if ( !cpu->history ) { /* * First time through, we need to create buffers * for the historical stats */ cpu->history = (struct netsnmp_cpu_history *)calloc( _cpuHistoryLen, sizeof(struct netsnmp_cpu_history)); } else { /* * Otherwise, rotate these values - in descending order * with the earliest (relevant) statistics in entry 0. * This means that the code to calculate the rolling averages * is independent of the number of historical samples saved. */ for (i=0; i<_cpuHistoryLen-2; i++) { cpu->history[i] = cpu->history[i+1]; } cpu->history[i].user_hist = cpu->user_ticks; cpu->history[i].sys_hist = cpu->sys_ticks; cpu->history[i].idle_hist = cpu->idle_ticks; cpu->history[i].nice_hist = cpu->nice_ticks; cpu->history[i].total_hist = cpu->total_ticks; cpu->history[i].ctx_hist = cpu->nCtxSwitches; cpu->history[i].intr_hist = cpu->nInterrupts; cpu->history[i].swpi_hist = cpu->swapIn; cpu->history[i].swpo_hist = cpu->swapOut; cpu->history[i].pagei_hist = cpu->pageIn; cpu->history[i].pageo_hist = cpu->pageOut; } } /* * Now call the system-specific load routine, to * retrieve the latest set of data. */ netsnmp_cpu_arch_load( NULL, NULL ); for ( cpu=_cpu_head; cpu; cpu=cpu->next ) { cpu->total_ticks = cpu->user_ticks + cpu->nice_ticks + cpu->sys_ticks + cpu->idle_ticks + cpu->wait_ticks + cpu->kern_ticks + cpu->intrpt_ticks + cpu->sirq_ticks + cpu->steal_ticks + cpu->guest_ticks + cpu->guestnice_ticks; } } #ifndef NETSNMP_FEATURE_REMOVE_HARDWARE_CPU_COPY_STATS void _cpu_copy_stats( netsnmp_cpu_info *cpu ) { netsnmp_cpu_info *cpu2; /* * Copy "overall" statistics to the 'cpu0' entry * on single CPU systems where this isn't done automatically */ cpu2 = netsnmp_cpu_get_byIdx( 0, 1 ); if (!cpu || !cpu2) return; cpu2->user_ticks = cpu->user_ticks; cpu2->nice_ticks = cpu->nice_ticks; cpu2->sys_ticks = cpu->sys_ticks; cpu2->sys2_ticks = cpu->sys2_ticks; cpu2->idle_ticks = cpu->idle_ticks; cpu2->wait_ticks = cpu->wait_ticks; cpu2->kern_ticks = cpu->kern_ticks; cpu2->intrpt_ticks = cpu->intrpt_ticks; cpu2->sirq_ticks = cpu->sirq_ticks; cpu2->steal_ticks = cpu->steal_ticks; cpu2->guest_ticks = cpu->guest_ticks; cpu2->guestnice_ticks = cpu->guestnice_ticks; cpu2->nInterrupts = cpu->nInterrupts; cpu2->nCtxSwitches = cpu->nCtxSwitches; cpu2->swapIn = cpu->swapIn; cpu2->swapOut = cpu->swapOut; cpu2->pageIn = cpu->pageIn; cpu2->pageOut = cpu->pageOut; } #endif /* NETSNMP_FEATURE_REMOVE_HARDWARE_CPU_COPY_STATS */