/****************************************************************************** * * * Comments: Get the value of a counter. If it is a rate counter, * * sleep 1 second to get the second raw value. * * * ******************************************************************************/ PDH_STATUS calculate_counter_value(const char *function, const char *counterpath, double *value) { PDH_HQUERY query; PDH_HCOUNTER handle = NULL; PDH_STATUS pdh_status; PDH_RAW_COUNTER rawData, rawData2; PDH_FMT_COUNTERVALUE counterValue; if (ERROR_SUCCESS != (pdh_status = zbx_PdhOpenQuery(function, &query))) return pdh_status; if (ERROR_SUCCESS != (pdh_status = zbx_PdhAddCounter(function, NULL, query, counterpath, &handle))) goto close_query; if (ERROR_SUCCESS != (pdh_status = zbx_PdhCollectQueryData(function, counterpath, query))) goto remove_counter; if (ERROR_SUCCESS != (pdh_status = zbx_PdhGetRawCounterValue(function, counterpath, handle, &rawData))) goto remove_counter; if (PDH_CSTATUS_INVALID_DATA == (pdh_status = PdhCalculateCounterFromRawValue(handle, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &rawData, NULL, &counterValue))) { /* some (e.g., rate) counters require two raw values, MSDN lacks documentation */ /* about what happens but tests show that PDH_CSTATUS_INVALID_DATA is returned */ zbx_sleep(1); if (ERROR_SUCCESS == (pdh_status = zbx_PdhCollectQueryData(function, counterpath, query)) && ERROR_SUCCESS == (pdh_status = zbx_PdhGetRawCounterValue(function, counterpath, handle, &rawData2))) { pdh_status = PdhCalculateCounterFromRawValue(handle, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &rawData2, &rawData, &counterValue); } } if (ERROR_SUCCESS != pdh_status || (PDH_CSTATUS_VALID_DATA != counterValue.CStatus && PDH_CSTATUS_NEW_DATA != counterValue.CStatus)) { if (ERROR_SUCCESS == pdh_status) pdh_status = counterValue.CStatus; zabbix_log(LOG_LEVEL_DEBUG, "%s(): cannot calculate counter value '%s': %s", function, counterpath, strerror_from_module(pdh_status, L"PDH.DLL")); } else { *value = counterValue.doubleValue; } remove_counter: PdhRemoveCounter(handle); close_query: PdhCloseQuery(query); return pdh_status; }
void collect_perfstat() { const char *__function_name = "collect_perfstat"; PERF_COUNTER_DATA *cptr; PDH_STATUS pdh_status; time_t now; PDH_FMT_COUNTERVALUE value; if (SUCCEED != perf_collector_started()) return; if (NULL == ppsd.pPerfCounterList) /* no counters */ return; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name); now = time(NULL); /* refresh unsupported counters */ if (ppsd.nextcheck <= now) { for (cptr = ppsd.pPerfCounterList; NULL != cptr; cptr = cptr->next) { if (PERF_COUNTER_NOTSUPPORTED != cptr->status) continue; zbx_PdhAddCounter(__function_name, cptr, ppsd.pdh_query, cptr->counterpath, &cptr->handle); } ppsd.nextcheck = now + UNSUPPORTED_REFRESH_PERIOD; } /* query for new data */ if (ERROR_SUCCESS != (pdh_status = PdhCollectQueryData(ppsd.pdh_query))) { for (cptr = ppsd.pPerfCounterList; NULL != cptr; cptr = cptr->next) { if (PERF_COUNTER_NOTSUPPORTED != cptr->status) deactivate_perf_counter(cptr); } zabbix_log(LOG_LEVEL_DEBUG, "%s() call to PdhCollectQueryData() failed: %s", __function_name, strerror_from_module(pdh_status, L"PDH.DLL")); goto out; } /* get the raw values */ for (cptr = ppsd.pPerfCounterList; NULL != cptr; cptr = cptr->next) { if (PERF_COUNTER_NOTSUPPORTED == cptr->status) continue; if (ERROR_SUCCESS != zbx_PdhGetRawCounterValue(__function_name, cptr->counterpath, cptr->handle, &cptr->rawValues[cptr->olderRawValue])) { deactivate_perf_counter(cptr); continue; } if (PERF_COUNTER_INITIALIZED < cptr->status) { zabbix_log(LOG_LEVEL_DEBUG, "%s() counterpath:'%s' old first:%I64d second:%I64d", __function_name, cptr->counterpath, cptr->rawValues[(cptr->olderRawValue + 1) & 1].FirstValue, cptr->rawValues[(cptr->olderRawValue + 1) & 1].SecondValue); zabbix_log(LOG_LEVEL_DEBUG, "%s() counterpath:'%s' new first:%I64d second:%I64d", __function_name, cptr->counterpath, cptr->rawValues[cptr->olderRawValue].FirstValue, cptr->rawValues[cptr->olderRawValue].SecondValue); } cptr->olderRawValue = (cptr->olderRawValue + 1) & 1; pdh_status = PdhCalculateCounterFromRawValue(cptr->handle, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &cptr->rawValues[(cptr->olderRawValue + 1) & 1], (PERF_COUNTER_INITIALIZED < cptr->status ? &cptr->rawValues[cptr->olderRawValue] : NULL), &value); if (ERROR_SUCCESS == pdh_status && PDH_CSTATUS_VALID_DATA != value.CStatus && PDH_CSTATUS_NEW_DATA != value.CStatus) { pdh_status = value.CStatus; } if (PDH_CSTATUS_INVALID_DATA == pdh_status) { /* some (e.g., rate) counters require two raw values, MSDN lacks documentation */ /* about what happens but tests show that PDH_CSTATUS_INVALID_DATA is returned */ cptr->status = PERF_COUNTER_GET_SECOND_VALUE; continue; } if (PDH_CALC_NEGATIVE_DENOMINATOR == pdh_status) { /* This counter type shows the average percentage of active time observed during the sample */ /* interval. This is an inverse counter. Inverse counters are calculated by monitoring the */ /* percentage of time that the service was inactive and then subtracting that value from 100 */ /* percent. The formula used to calculate the counter value is: */ /* (1 - (inactive time delta) / (total time delta)) x 100 */ /* For some unknown reason sometimes the collected row values indicate that inactive delta is */ /* greater than total time delta. There must be some kind of bug in how performance counters */ /* work. When this happens function PdhCalculateCounterFromRawValue() returns error */ /* PDH_CALC_NEGATIVE_DENOMINATOR. Basically this means that an item was inactive all the time */ /* so we set the return value to 0.0 and change status to indicate success. */ /* More info: technet.microsoft.com/en-us/library/cc757283(WS.10).aspx */ zabbix_log(LOG_LEVEL_DEBUG, "%s() counterpath:'%s'" " negative denominator error is treated as 0.0", __function_name, cptr->counterpath); pdh_status = ERROR_SUCCESS; value.doubleValue = 0.0; /* 100% inactive */ } if (ERROR_SUCCESS == pdh_status) { zabbix_log(LOG_LEVEL_DEBUG, "%s() '%s' calculated value:" ZBX_FS_DBL, __function_name, cptr->counterpath, value.doubleValue); cptr->status = PERF_COUNTER_ACTIVE; cptr->value_current = (cptr->value_current + 1) % cptr->interval; if (cptr->value_count == cptr->interval) cptr->sum -= cptr->value_array[cptr->value_current]; /* remove the oldest value, value_count will not increase */ cptr->value_array[cptr->value_current] = value.doubleValue; cptr->sum += cptr->value_array[cptr->value_current]; if (cptr->value_count < cptr->interval) cptr->value_count++; } else { zabbix_log(LOG_LEVEL_WARNING, "cannot calculate performance counter value \"%s\": %s", cptr->counterpath, strerror_from_module(pdh_status, L"PDH.DLL")); deactivate_perf_counter(cptr); } } out: zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name); }
/****************************************************************************** * * * Comments: if the specified counter exists or a new is successfully * * added, a pointer to that counter is returned, NULL otherwise * * * ******************************************************************************/ PERF_COUNTER_DATA *add_perf_counter(const char *name, const char *counterpath, int interval) { const char *__function_name = "add_perf_counter"; PERF_COUNTER_DATA *cptr; char *alias_name; PDH_STATUS pdh_status; int result = FAIL; assert(counterpath); zabbix_log(LOG_LEVEL_DEBUG, "In %s() counter:'%s' interval:%d", __function_name, counterpath, interval); if (SUCCEED != perf_collector_started()) { zabbix_log(LOG_LEVEL_WARNING, "PerfCounter '%s' FAILED: collector is not started!", counterpath); return NULL; } if (1 > interval || 900 < interval) { zabbix_log(LOG_LEVEL_WARNING, "PerfCounter '%s' FAILED: interval value out of range", counterpath); return NULL; } for (cptr = ppsd.pPerfCounterList; ; cptr = cptr->next) { /* add new parameters */ if (NULL == cptr) { cptr = (PERF_COUNTER_DATA *)zbx_malloc(cptr, sizeof(PERF_COUNTER_DATA)); /* initialize the counter */ memset(cptr, 0, sizeof(PERF_COUNTER_DATA)); if (NULL != name) cptr->name = strdup(name); cptr->counterpath = strdup(counterpath); cptr->interval = interval; cptr->value_current = -1; cptr->value_array = (double *)zbx_malloc(cptr->value_array, sizeof(double) * interval); /* add the counter to the query */ pdh_status = zbx_PdhAddCounter(__function_name, cptr, ppsd.pdh_query, counterpath, &cptr->handle); zbx_mutex_lock(&perfstat_access); cptr->next = ppsd.pPerfCounterList; ppsd.pPerfCounterList = cptr; zbx_mutex_unlock(&perfstat_access); if (ERROR_SUCCESS != pdh_status && PDH_CSTATUS_NO_INSTANCE != pdh_status) { zabbix_log(LOG_LEVEL_WARNING, "cannot add performance counter \"%s\": invalid format", counterpath); cptr = NULL; /* indicate a failure */ } result = SUCCEED; break; } if (NULL != name && 0 == strcmp(cptr->name, name)) break; if (NULL == name && 0 == strcmp(cptr->counterpath, counterpath) && cptr->interval == interval) break; } if (FAIL == result) { zabbix_log(LOG_LEVEL_DEBUG, "%s() counter '%s' already exists", __function_name, counterpath); } else if (NULL != name) { alias_name = zbx_dsprintf(NULL, "__UserPerfCounter[%s]", name); add_alias(name, alias_name); zbx_free(alias_name); } return cptr; }
/****************************************************************************** * * * Comments: if the specified counter exists or a new is successfully * * added, a pointer to that counter is returned, NULL otherwise * * * ******************************************************************************/ PERF_COUNTER_DATA *add_perf_counter(const char *name, const char *counterpath, int interval, char **error) { const char *__function_name = "add_perf_counter"; PERF_COUNTER_DATA *cptr = NULL; PDH_STATUS pdh_status; int added = FAIL; zabbix_log(LOG_LEVEL_DEBUG, "In %s() counter:'%s' interval:%d", __function_name, counterpath, interval); LOCK_PERFCOUNTERS; if (SUCCEED != perf_collector_started()) { *error = zbx_strdup(*error, "Performance collector is not started."); goto out; } for (cptr = ppsd.pPerfCounterList; ; cptr = cptr->next) { /* add new parameters */ if (NULL == cptr) { cptr = (PERF_COUNTER_DATA *)zbx_malloc(cptr, sizeof(PERF_COUNTER_DATA)); /* initialize the counter */ memset(cptr, 0, sizeof(PERF_COUNTER_DATA)); if (NULL != name) cptr->name = zbx_strdup(NULL, name); cptr->counterpath = zbx_strdup(NULL, counterpath); cptr->interval = interval; cptr->value_current = -1; cptr->value_array = (double *)zbx_malloc(cptr->value_array, sizeof(double) * interval); /* add the counter to the query */ pdh_status = zbx_PdhAddCounter(__function_name, cptr, ppsd.pdh_query, counterpath, &cptr->handle); cptr->next = ppsd.pPerfCounterList; ppsd.pPerfCounterList = cptr; if (ERROR_SUCCESS != pdh_status && PDH_CSTATUS_NO_INSTANCE != pdh_status) { *error = zbx_dsprintf(*error, "Invalid performance counter format."); cptr = NULL; /* indicate a failure */ } added = SUCCEED; break; } if (NULL != name && 0 == strcmp(cptr->name, name)) break; if (NULL == name && 0 == strcmp(cptr->counterpath, counterpath) && cptr->interval == interval) break; } if (FAIL == added) { zabbix_log(LOG_LEVEL_DEBUG, "%s() counter '%s' already exists", __function_name, counterpath); } else if (NULL != name) { char *alias_name; alias_name = zbx_dsprintf(NULL, "__UserPerfCounter[%s]", name); add_alias(name, alias_name); zbx_free(alias_name); } out: UNLOCK_PERFCOUNTERS; zabbix_log(LOG_LEVEL_DEBUG, "End of %s(): %s", __function_name, NULL == cptr ? "FAIL" : "SUCCEED"); return cptr; }