| #include <net-snmp/net-snmp-config.h> |
| #include <net-snmp/net-snmp-includes.h> |
| #include <net-snmp/agent/net-snmp-agent-includes.h> |
| #include <net-snmp/agent/hardware/cpu.h> |
| |
| extern NetsnmpCacheLoad netsnmp_cpu_arch_load; |
| 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; |
| } |
| } |
| if (cpu) |
| SNMP_FREE(cpu); /* just in case */ |
| return NULL; /* Shouldn't happen! */ |
| } |
| |
| /* |
| * Work with a list of CPU entries, indexed by name |
| */ |
| 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; |
| } |
| |
| strcpy(cpu->name, name); |
| 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; |
| } |
| |
| netsnmp_cache *netsnmp_cpu_get_cache( void ) { |
| return _cpu_cache; |
| } |
| |
| 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 )); |
| } |
| |
| /* |
| * 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; |
| } |
| } |
| |
| 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->nInterrupts = cpu->nInterrupts; |
| cpu2->nCtxSwitches = cpu->nCtxSwitches; |
| cpu2->swapIn = cpu->swapIn; |
| cpu2->swapOut = cpu->swapOut; |
| cpu2->pageIn = cpu->pageIn; |
| cpu2->pageOut = cpu->pageOut; |
| } |