summaryrefslogtreecommitdiff
path: root/src/pmdas/windows/open.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmdas/windows/open.c')
-rw-r--r--src/pmdas/windows/open.c769
1 files changed, 769 insertions, 0 deletions
diff --git a/src/pmdas/windows/open.c b/src/pmdas/windows/open.c
new file mode 100644
index 0000000..65be00d
--- /dev/null
+++ b/src/pmdas/windows/open.c
@@ -0,0 +1,769 @@
+/*
+ * Copyright (c) 2008-2010 Aconex. All Rights Reserved.
+ * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved.
+ * Parts of this file contributed by Ken McDonell
+ * (kenj At internode DoT on DoT net)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "hypnotoad.h"
+#include <winbase.h>
+
+#define roundup(x,y) ((((x) + ((y) - 1)) / (y)) * (y))
+
+char *windows_uname;
+char *windows_build;
+char *windows_machine;
+unsigned long windows_pagesize;
+int windows_indom_setup[NUMINDOMS]; /* initial setup done on instance */
+int windows_indom_reset[NUMINDOMS]; /* instances changed on refresh */
+
+/*
+ * This block of functionality is required to map counter types from
+ * their Windows semantics to equivalent PCP semantics.
+ */
+
+static struct {
+ int type;
+ char *desc;
+} ctypetab[] = {
+ { PERF_100NSEC_MULTI_TIMER, "PERF_100NSEC_MULTI_TIMER" },
+ { PERF_100NSEC_MULTI_TIMER_INV, "PERF_100NSEC_MULTI_TIMER_INV" },
+ { PERF_100NSEC_TIMER, "PERF_100NSEC_TIMER" },
+ { PERF_100NSEC_TIMER_INV, "PERF_100NSEC_TIMER_INV" },
+ { PERF_AVERAGE_BASE, "PERF_AVERAGE_BASE" },
+ { PERF_AVERAGE_BULK, "PERF_AVERAGE_BULK" },
+ { PERF_AVERAGE_TIMER, "PERF_AVERAGE_TIMER" },
+ { PERF_COUNTER_100NS_QUEUELEN_TYPE, "PERF_COUNTER_100NS_QUEUELEN_TYPE" },
+ { PERF_COUNTER_BULK_COUNT, "PERF_COUNTER_BULK_COUNT" },
+ { PERF_COUNTER_COUNTER, "PERF_COUNTER_COUNTER" },
+ { PERF_COUNTER_DELTA, "PERF_COUNTER_DELTA" },
+ { PERF_COUNTER_LARGE_DELTA, "PERF_COUNTER_LARGE_DELTA" },
+ { PERF_COUNTER_LARGE_QUEUELEN_TYPE, "PERF_COUNTER_LARGE_QUEUELEN_TYPE" },
+ { PERF_COUNTER_LARGE_RAWCOUNT, "PERF_COUNTER_LARGE_RAWCOUNT" },
+ { PERF_COUNTER_LARGE_RAWCOUNT_HEX, "PERF_COUNTER_LARGE_RAWCOUNT_HEX" },
+ { PERF_COUNTER_MULTI_BASE, "PERF_COUNTER_MULTI_BASE" },
+ { PERF_COUNTER_MULTI_TIMER, "PERF_COUNTER_MULTI_TIMER" },
+ { PERF_COUNTER_MULTI_TIMER_INV, "PERF_COUNTER_MULTI_TIMER_INV" },
+ { PERF_COUNTER_NODATA, "PERF_COUNTER_NODATA" },
+ { PERF_COUNTER_QUEUELEN_TYPE, "PERF_COUNTER_QUEUELEN_TYPE" },
+ { PERF_COUNTER_RAWCOUNT, "PERF_COUNTER_RAWCOUNT" },
+ { PERF_COUNTER_RAWCOUNT_HEX, "PERF_COUNTER_RAWCOUNT_HEX" },
+ { PERF_COUNTER_TEXT, "PERF_COUNTER_TEXT" },
+ { PERF_COUNTER_TIMER, "PERF_COUNTER_TIMER" },
+ { PERF_COUNTER_TIMER_INV, "PERF_COUNTER_TIMER_INV" },
+ { PERF_ELAPSED_TIME, "PERF_ELAPSED_TIME" },
+ { PERF_LARGE_RAW_BASE, "PERF_LARGE_RAW_BASE" },
+ { PERF_OBJ_TIME_TIMER, "PERF_OBJ_TIME_TIMER" },
+ { PERF_PRECISION_100NS_TIMER, "PERF_PRECISION_100NS_TIMER" },
+ { PERF_PRECISION_OBJECT_TIMER, "PERF_PRECISION_OBJECT_TIMER" },
+ { PERF_PRECISION_SYSTEM_TIMER, "PERF_PRECISION_SYSTEM_TIMER" },
+ { PERF_RAW_BASE, "PERF_RAW_BASE" },
+ { PERF_RAW_FRACTION, "PERF_RAW_FRACTION" },
+ { PERF_LARGE_RAW_FRACTION, "PERF_LARGE_RAW_FRACTION" },
+ { PERF_SAMPLE_BASE, "PERF_SAMPLE_BASE" },
+ { PERF_SAMPLE_COUNTER, "PERF_SAMPLE_COUNTER" },
+ { PERF_SAMPLE_FRACTION, "PERF_SAMPLE_FRACTION" }
+};
+
+static int ctypetab_sz = sizeof(ctypetab) / sizeof(ctypetab[0]);
+
+static char *
+decode_ctype(DWORD ctype)
+{
+ static char unknown[20];
+ int i;
+
+ for (i = 0; i < ctypetab_sz; i++)
+ if (ctype == ctypetab[i].type)
+ return ctypetab[i].desc;
+ sprintf(unknown, "0x%08x unknown", (int)ctype);
+ return unknown;
+}
+
+static char *
+string_append(char *name, char *suff)
+{
+ if (name == NULL) {
+ name = (char *)strdup(suff);
+ }
+ else {
+ name = (char *)realloc(name, strlen(name)+strlen(suff)+1);
+ strcat(name, suff);
+ }
+ return name;
+}
+
+static char *
+_semstr(int sem)
+{
+ static char msg[20];
+ if (sem == PM_SEM_COUNTER)
+ return "COUNTER";
+ else if (sem == PM_SEM_INSTANT)
+ return "INSTANT";
+ else if (sem == PM_SEM_DISCRETE)
+ return "DISCRETE";
+ else {
+ sprintf(msg, "UNKNOWN! (%d)", sem);
+ return msg;
+ }
+}
+
+static char *
+_typestr(int type)
+{
+ static char msg[20];
+ if (type == PM_TYPE_32)
+ return "PM_TYPE_32";
+ else if (type == PM_TYPE_U32)
+ return "PM_TYPE_U32";
+ else if (type == PM_TYPE_64)
+ return "PM_TYPE_64";
+ else if (type == PM_TYPE_U64)
+ return "PM_TYPE_U64";
+ else if (type == PM_TYPE_FLOAT)
+ return "PM_TYPE_FLOAT";
+ else if (type == PM_TYPE_DOUBLE)
+ return "PM_TYPE_DOUBLE";
+ else {
+ sprintf(msg, "UNKNOWN! (%d)", type);
+ return msg;
+ }
+}
+
+#if 0 // debugging
+static char *
+_ctypestr(int ctype)
+{
+ if (ctype == PERF_COUNTER_COUNTER)
+ return "PERF_COUNTER_COUNTER";
+ else if (ctype == PERF_RAW_FRACTION)
+ return "PERF_RAW_FRACTION";
+ else if (ctype == PERF_LARGE_RAW_FRACTION)
+ return "PERF_LARGE_RAW_FRACTION";
+ else if (ctype == PERF_COUNTER_LARGE_RAWCOUNT_HEX)
+ return "PERF_COUNTER_LARGE_RAWCOUNT_HEX";
+ else if (ctype == PERF_COUNTER_LARGE_RAWCOUNT)
+ return "PERF_COUNTER_LARGE_RAWCOUNT";
+ else if (ctype == PERF_PRECISION_100NS_TIMER)
+ return "PERF_PRECISION_100NS_TIMER";
+ else if (ctype == PERF_100NSEC_TIMER)
+ return "PERF_100NSEC_TIMER";
+ else if (ctype == PERF_COUNTER_BULK_COUNT)
+ return "PERF_COUNTER_BULK_COUNT";
+ else if (ctype == PERF_COUNTER_RAWCOUNT_HEX)
+ return "PERF_COUNTER_RAWCOUNT_HEX";
+ else if (ctype == PERF_COUNTER_RAWCOUNT)
+ return "PERF_COUNTER_RAWCOUNT";
+ else if (ctype == PERF_COUNTER_COUNTER)
+ return "PERF_COUNTER_COUNTER";
+ else
+ return "UNKNOWN";
+}
+#endif
+
+/*
+ * Based on documentation from ...
+ * http://msdn.microsoft.com/library/default.asp?
+ * url=/library/en-us/sysinfo/base/osversioninfoex_str.asp
+ */
+static void
+windows_format_uname(OSVERSIONINFOEX osv)
+{
+ char tbuf[80];
+ char *name = NULL;
+
+ switch (osv.dwPlatformId) {
+ case VER_PLATFORM_WIN32_NT:
+ if (osv.dwMajorVersion == 6 && osv.dwMinorVersion == 1) {
+ if (osv.wProductType == VER_NT_WORKSTATION)
+ name = string_append(name, "Windows 7");
+ else
+ name = string_append(name, "Windows Server 2008 R2");
+ }
+ else if (osv.dwMajorVersion == 6 && osv.dwMinorVersion == 0) {
+ if (osv.wProductType == VER_NT_WORKSTATION)
+ name = string_append(name, "Windows Vista");
+ else
+ name = string_append(name, "Windows Server 2008");
+ }
+ else if (osv.dwMajorVersion == 5 && osv.dwMinorVersion == 2)
+ name = string_append(name, "Windows Server 2003");
+ else if (osv.dwMajorVersion == 5 && osv.dwMinorVersion == 1)
+ name = string_append(name, "Windows XP");
+ else if (osv.dwMajorVersion == 5 && osv.dwMinorVersion == 0)
+ name = string_append(name, "Windows 2000");
+ else if (osv.dwMajorVersion <= 4)
+ name = string_append(name, "Windows NT");
+ else {
+ sprintf(tbuf, "Windows Unknown (%ld.%ld)",
+ osv.dwMajorVersion, osv.dwMinorVersion);
+ name = string_append(name, tbuf);
+ }
+
+ /* service pack and build number etc */
+ if (osv.szCSDVersion[0] != '\0') {
+ name = string_append(name, " ");
+ name = string_append(name, osv.szCSDVersion);
+ }
+ sprintf(tbuf, " Build %ld", osv.dwBuildNumber & 0xFFFF);
+ windows_build = name + strlen(name) + 1;
+ windows_uname = string_append(name, tbuf);
+ break;
+
+ default:
+ windows_uname = "Windows - Platform Unknown";
+ windows_build = "Unknown Build";
+ break;
+ }
+}
+
+void
+windows_setup_globals(void)
+{
+ SYSTEM_INFO sysinfo;
+ OSVERSIONINFOEX osversion;
+
+ ZeroMemory(&sysinfo, sizeof(SYSTEM_INFO));
+ GetSystemInfo(&sysinfo);
+ windows_pagesize = sysinfo.dwPageSize;
+
+ switch (sysinfo.wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ windows_machine = "x86_64";
+ break;
+ case PROCESSOR_ARCHITECTURE_IA64:
+ windows_machine = "ia64";
+ break;
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ windows_machine = "i686";
+ break;
+ default:
+ windows_machine = "Unknown";
+ break;
+ }
+
+ ZeroMemory(&osversion, sizeof(OSVERSIONINFOEX));
+ osversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ GetVersionEx((OSVERSIONINFO *)&osversion);
+ windows_format_uname(osversion);
+}
+
+static void
+windows_verify_metric(pdh_metric_t *mp, PDH_COUNTER_INFO_A *infop)
+{
+ char *ctr_type;
+
+ mp->ctype = infop->dwType;
+
+ switch (mp->ctype) {
+ /*
+ * Pdh metric sematics ... from WinPerf.h
+ *
+ * SIZE
+ * DWORD 32-bit
+ * LARGE 64-bit
+ * ZERO no support here
+ * VARIABLE_LEN no support here
+ *
+ * TYPE
+ * NUMBER PM_SEM_INSTANT
+ * HEX display in hex (no support here)
+ * DECIMAL display as decimal
+ * DEC_1000 display as value / 1000
+ * (no support here)
+ * COUNTER PM_SEM_COUNTER
+ * VALUE display value (no support here)
+ * RATE time rate converted
+ * FRACTION divide value by BASE
+ * BASE used for FRACTION
+ * QUEUELEN magic internal queuelen() routines
+ * (you're joking, right?)
+ * HISTOGRAM counter begins or ends a histo (?)
+ * (definitely no support here)
+ * PRECISION divide counter by private clock (?)
+ * (definitely no support here)
+ * TEXT no support here
+ * ZERO no support here
+ */
+
+ /*
+ * Known 32-bit counters
+ */
+ case PERF_COUNTER_COUNTER:
+ /* 32-bit PM_SEM_COUNTER */
+ if (mp->desc.type != PM_TYPE_32 && mp->desc.type != PM_TYPE_U32) {
+ if (!(mp->flags & M_AUTO64))
+ __pmNotifyErr(LOG_ERR,
+ "windows_open: PERF_COUNTER_COUNTER: "
+ "metric %s: rewrite type from %s to PM_TYPE_U32\n",
+ pmIDStr(mp->desc.pmid), _typestr(mp->desc.type));
+ mp->desc.type = PM_TYPE_U32;
+ }
+ if (mp->desc.sem != PM_SEM_COUNTER) {
+ __pmNotifyErr(LOG_ERR, "windows_open: PERF_COUNTER_COUNTER: "
+ "metric %s: semantics %s (expected %s)\n",
+ pmIDStr(mp->desc.pmid), _semstr(mp->desc.sem),
+ _semstr(PM_SEM_COUNTER));
+ }
+ break;
+
+ case PERF_COUNTER_RAWCOUNT:
+ case PERF_COUNTER_RAWCOUNT_HEX:
+ if (mp->ctype == PERF_COUNTER_RAWCOUNT)
+ ctr_type = "PERF_COUNTER_RAWCOUNT";
+ else
+ ctr_type = "PERF_COUNTER_RAWCOUNT_HEX";
+ /* 32-bit PM_SEM_INSTANT or PM_SEM_DISCRETE */
+ if (mp->desc.type != PM_TYPE_32 && mp->desc.type != PM_TYPE_U32) {
+ if (!(mp->flags & M_AUTO64))
+ __pmNotifyErr(LOG_ERR,
+ "windows_open: Warning: %s: metric %s: "
+ "rewrite type from %s to PM_TYPE_U32\n",
+ ctr_type, pmIDStr(mp->desc.pmid),
+ _typestr(mp->desc.type));
+ mp->desc.type = PM_TYPE_U32;
+ }
+ break;
+
+ /*
+ * Known 64-bit counters
+ */
+ case PERF_COUNTER_BULK_COUNT:
+ /* 64-bit PM_SEM_COUNTER */
+ if (mp->desc.type != PM_TYPE_64 && mp->desc.type != PM_TYPE_U64) {
+ if (!(mp->flags & M_AUTO64))
+ __pmNotifyErr(LOG_ERR,
+ "windows_open: PERF_COUNTER_BULK_COUNT:"
+ " metric %s: rewrite type from %s to PM_TYPE_U64\n",
+ pmIDStr(mp->desc.pmid), _typestr(mp->desc.type));
+ mp->desc.type = PM_TYPE_U64;
+ }
+ if (mp->desc.sem != PM_SEM_COUNTER) {
+ __pmNotifyErr(LOG_ERR, "windows_open: PERF_COUNTER_BULK_COUNT:"
+ " metric %s: semantics %s (expected %s)\n",
+ pmIDStr(mp->desc.pmid), _semstr(mp->desc.sem),
+ _semstr(PM_SEM_COUNTER));
+ mp->desc.sem = PM_SEM_COUNTER;
+ }
+ break;
+
+ case PERF_100NSEC_TIMER:
+ case PERF_PRECISION_100NS_TIMER:
+ if (mp->ctype == PERF_100NSEC_TIMER)
+ ctr_type = "PERF_100NSEC_TIMER";
+ else
+ ctr_type = "PERF_PRECISION_100NS_TIMER";
+ /*
+ * 64-bit PM_SEM_COUNTER, units are 100's of nanosecs,
+ * we shall export 'em as microseconds
+ */
+ if (mp->desc.type != PM_TYPE_64 && mp->desc.type != PM_TYPE_U64) {
+ if (!(mp->flags & M_AUTO64))
+ __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s: "
+ "metric %s: rewrite type from %s to PM_TYPE_U64\n",
+ ctr_type, pmIDStr(mp->desc.pmid), _typestr(mp->desc.type));
+ mp->desc.type = PM_TYPE_U64;
+ }
+ if (mp->desc.sem != PM_SEM_COUNTER) {
+ __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s: "
+ "metric %s: semantics %s (expected %s)\n",
+ ctr_type, pmIDStr(mp->desc.pmid), _semstr(mp->desc.sem),
+ _semstr(PM_SEM_COUNTER));
+ mp->desc.sem = PM_SEM_COUNTER;
+ }
+ if (mp->desc.units.dimSpace != 0 ||
+ mp->desc.units.dimTime != 1 ||
+ mp->desc.units.dimCount != 0 ||
+ mp->desc.units.scaleTime != PM_TIME_USEC) {
+ pmUnits units = mp->desc.units;
+ mp->desc.units.dimSpace = mp->desc.units.dimCount = 0;
+ mp->desc.units.scaleSpace = mp->desc.units.scaleCount = 0;
+ mp->desc.units.dimTime = 1;
+ mp->desc.units.scaleTime = PM_TIME_USEC;
+ __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s: "
+ "metric %s: rewrite dimension and scale from %s to %s",
+ ctr_type, pmIDStr(mp->desc.pmid), pmUnitsStr(&units),
+ pmUnitsStr(&mp->desc.units));
+ }
+ break;
+
+ case PERF_COUNTER_LARGE_RAWCOUNT:
+ case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
+ /* 64-bit PM_SEM_INSTANT or PM_SEM_DISCRETE */
+ if (mp->desc.type != PM_TYPE_64 &&
+ mp->desc.type != PM_TYPE_U64) {
+ if (!(mp->flags & M_AUTO64))
+ __pmNotifyErr(LOG_ERR, "windows_open: Warning: "
+ "PERF_COUNTER_LARGE_RAWCOUNT: metric %s: "
+ "rewrite type from %s to PM_TYPE_U64\n",
+ pmIDStr(mp->desc.pmid), _typestr(mp->desc.type));
+ mp->desc.type = PM_TYPE_U64;
+ }
+ break;
+
+ case PERF_RAW_FRACTION:
+ /* Float PM_SEM_INSTANT or PM_SEM_DISCRETE */
+ if (mp->desc.type != PM_TYPE_FLOAT) {
+ if (!(mp->flags & M_AUTO64))
+ __pmNotifyErr(LOG_ERR, "windows_open: Warning: "
+ "PERF_RAW_FRACTION: metric %s: "
+ "rewrite type from %s to PM_TYPE_FLOAT\n",
+ pmIDStr(mp->desc.pmid), _typestr(mp->desc.type));
+ mp->desc.type = PM_TYPE_FLOAT;
+ }
+ break;
+
+ case PERF_LARGE_RAW_FRACTION:
+ /* Double PM_SEM_INSTANT or PM_SEM_DISCRETE */
+ if (mp->desc.type != PM_TYPE_DOUBLE) {
+ if (!(mp->flags & M_AUTO64))
+ __pmNotifyErr(LOG_ERR, "windows_open: Warning: "
+ "PERF_LARGE_RAW_FRACTION: metric %s: "
+ "rewrite type from %s to PM_TYPE_DOUBLE\n",
+ pmIDStr(mp->desc.pmid), _typestr(mp->desc.type));
+ mp->desc.type = PM_TYPE_DOUBLE;
+ }
+ break;
+
+ case PERF_AVERAGE_BULK:
+ case PERF_AVERAGE_TIMER:
+ if (mp->ctype == PERF_AVERAGE_BULK)
+ ctr_type = "PERF_AVERAGE_BULK";
+ else
+ ctr_type = "PERF_AVERAGE_TIMER";
+ /* 64-bit PM_SEM_INSTANT or PM_SEM_DISCRETE */
+ if (mp->desc.sem != PM_SEM_INSTANT && mp->desc.sem != PM_SEM_DISCRETE) {
+ __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s: "
+ "metric %s: semantics %s (expected %s)\n",
+ ctr_type, pmIDStr(mp->desc.pmid),
+ _semstr(mp->desc.sem), _semstr(PM_SEM_INSTANT));
+ mp->desc.sem = PM_SEM_INSTANT;
+ }
+ if (mp->desc.type != PM_TYPE_64 && mp->desc.type != PM_TYPE_U64) {
+ if (!(mp->flags & M_AUTO64))
+ __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s "
+ "metric %s: rewrite type from %s to PM_TYPE_U64\n",
+ ctr_type, pmIDStr(mp->desc.pmid), _typestr(mp->desc.type));
+ mp->desc.type = PM_TYPE_U64;
+ }
+ break;
+
+ case PERF_SAMPLE_COUNTER:
+ ctr_type = "PERF_SAMPLE_COUNTER";
+ /* floating point PM_SEM_INSTANT or PM_SEM_DISCRETE */
+ if (mp->desc.sem != PM_SEM_INSTANT && mp->desc.sem != PM_SEM_DISCRETE) {
+ __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s: "
+ "metric %s: semantics %s (expected %s)\n",
+ ctr_type, pmIDStr(mp->desc.pmid),
+ _semstr(mp->desc.sem), _semstr(PM_SEM_INSTANT));
+ mp->desc.sem = PM_SEM_INSTANT;
+ }
+ if (mp->desc.type != PM_TYPE_FLOAT && mp->desc.type != PM_TYPE_DOUBLE) {
+ __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s "
+ "metric %s: rewrite type from %s to PM_TYPE_FLOAT\n",
+ ctr_type, pmIDStr(mp->desc.pmid), _typestr(mp->desc.type));
+ mp->desc.type = PM_TYPE_FLOAT;
+ }
+ break;
+
+ case PERF_ELAPSED_TIME:
+ ctr_type = "PERF_ELAPSED_TIME";
+ if (mp->desc.units.dimSpace != 0 ||
+ mp->desc.units.dimTime != 1 ||
+ mp->desc.units.dimCount != 0) {
+ pmUnits units = mp->desc.units;
+ mp->desc.units.dimSpace = mp->desc.units.dimCount = 0;
+ mp->desc.units.scaleSpace = mp->desc.units.scaleCount = 0;
+ mp->desc.units.dimTime = 1;
+ __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s: "
+ "metric %s: rewrite dimension and scale from %s to %s",
+ ctr_type, pmIDStr(mp->desc.pmid), pmUnitsStr(&units),
+ pmUnitsStr(&mp->desc.units));
+ }
+ if (mp->desc.type != PM_TYPE_64 && mp->desc.type != PM_TYPE_U64) {
+ if (!(mp->flags & M_AUTO64))
+ __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s "
+ "metric %s: rewrite type from %s to PM_TYPE_U64\n",
+ ctr_type, pmIDStr(mp->desc.pmid),
+ _typestr(mp->desc.type));
+ mp->desc.type = PM_TYPE_U64;
+ }
+ break;
+
+ default:
+ __pmNotifyErr(LOG_ERR, "windows_open: Warning: metric %s: "
+ "unexpected counter type: %s\n",
+ pmIDStr(mp->desc.pmid), decode_ctype(infop->dwType));
+ }
+ mp->flags |= M_EXPANDED;
+}
+
+int
+windows_inform_metric(pdh_metric_t *pmp, LPTSTR p, pdh_value_t *pvp,
+ BOOLEAN getExplainText, pdh_metric_inform_t informer)
+{
+ int sts = -1;
+ PDH_STATUS pdhsts;
+ PDH_HQUERY queryhdl = NULL;
+ PDH_HCOUNTER counterhdl = NULL;
+ DWORD result_sz;
+ static DWORD info_sz = 0;
+ static LPSTR info = NULL;
+
+ pdhsts = PdhOpenQueryA(NULL, 0, &queryhdl);
+ if (pdhsts != ERROR_SUCCESS) {
+ __pmNotifyErr(LOG_ERR, "windows_open: PdhOpenQueryA failed: %s\n",
+ pdherrstr(pdhsts));
+ return sts;
+ }
+
+ pdhsts = PdhAddCounterA(queryhdl, p, pvp->inst, &counterhdl);
+ if (pdhsts != ERROR_SUCCESS) {
+ __pmNotifyErr(LOG_ERR, "windows_open: Warning: PdhAddCounterA "
+ "@ pmid=%s pat=\"%s\": %s\n",
+ pmIDStr(pmp->desc.pmid), p, pdherrstr(pdhsts));
+ PdhCloseQuery(queryhdl);
+ return sts;
+ }
+
+ /*
+ * check PCP metric semantics against PDH info
+ */
+ if (info_sz == 0) {
+ /*
+ * We've observed an initial call to PdhGetCounterInfoA()
+ * hang with a zero sized buffer ... pander to this with
+ * an initial buffer allocation ... (size is a 100% guess).
+ */
+ info_sz = 256;
+ if ((info = (LPSTR)malloc(info_sz)) == NULL) {
+ __pmNotifyErr(LOG_ERR, "windows_open: PdhGetCounterInfoA "
+ "malloc (%d) failed @ metric %s: ",
+ (int)info_sz, pmIDStr(pmp->desc.pmid));
+ goto done;
+ }
+ }
+ result_sz = info_sz;
+ pdhsts = PdhGetCounterInfoA(counterhdl, getExplainText, &result_sz,
+ (PDH_COUNTER_INFO_A *)info);
+ if (pdhsts == PDH_MORE_DATA) {
+ info_sz = result_sz;
+ if ((info = (LPSTR)realloc(info, info_sz)) == NULL) {
+ __pmNotifyErr(LOG_ERR, "windows_open: PdhGetCounterInfoA "
+ "realloc (%d) failed @ metric %s: ",
+ (int)info_sz, pmIDStr(pmp->desc.pmid));
+ goto done;
+ }
+ pdhsts = PdhGetCounterInfoA(counterhdl, getExplainText, &result_sz,
+ (PDH_COUNTER_INFO_A *)info);
+ }
+ if (pdhsts != ERROR_SUCCESS) {
+ __pmNotifyErr(LOG_ERR, "windows_open: PdhGetCounterInfoA "
+ "failed @ metric %s: %s\n",
+ pmIDStr(pmp->desc.pmid), pdherrstr(pdhsts));
+ goto done;
+ }
+ else {
+ informer(pmp, (PDH_COUNTER_INFO_A *)info);
+ sts = 0;
+ }
+
+done:
+ PdhRemoveCounter(counterhdl);
+ PdhCloseQuery(queryhdl);
+ return sts;
+}
+
+void
+windows_verify_callback(pdh_metric_t *pmp, LPSTR pat, pdh_value_t *pvp)
+{
+ int v;
+
+ if (!(pmp->flags & M_VERIFIED)) {
+ v = windows_inform_metric(pmp, pat, pvp, FALSE, windows_verify_metric);
+ if (v == 0)
+ pmp->flags |= M_VERIFIED;
+ }
+}
+
+
+/*
+ * General purpose metric regex iterator, call out on each instance
+ */
+int
+windows_visit_metric(pdh_metric_t *pmp, pdh_metric_visitor_t visitor)
+{
+ size_t size;
+ int index = 0;
+ PDH_STATUS pdhsts;
+ DWORD result_sz;
+ static DWORD pattern_sz = 0;
+ static LPSTR pattern = NULL;
+ LPSTR p;
+
+ if (pmp->desc.indom != PM_INDOM_NULL) {
+ index = pmInDom_serial(pmp->desc.indom);
+ pmdaCacheOp(pmp->desc.indom, PMDA_CACHE_INACTIVE);
+ }
+
+ pmp->flags &= ~(M_EXPANDED|M_NOVALUES);
+ memset(pmp->vals, 0, pmp->num_alloc * sizeof(pdh_value_t));
+ pmp->num_vals = 0;
+
+ result_sz = 0;
+ pdhsts = PdhExpandCounterPathA(pmp->pat, NULL, &result_sz);
+ if (pdhsts == PDH_MORE_DATA) {
+ if (result_sz >= pattern_sz) {
+ pattern_sz = roundup(result_sz, 64);
+ if ((pattern = (LPSTR)realloc(pattern, pattern_sz)) == NULL) {
+ __pmNotifyErr(LOG_ERR, "windows_open: PdhExpandCounterPathA "
+ "realloc (%ld) failed @ metric %s: ",
+ pattern_sz, pmIDStr(pmp->desc.pmid));
+ return -1;
+ }
+ }
+ result_sz = pattern_sz;
+ pdhsts = PdhExpandCounterPathA(pmp->pat, pattern, &result_sz);
+ }
+ if (pdhsts != PDH_CSTATUS_VALID_DATA) {
+ if (pmp->pat[0] != '\\') {
+ /*
+ * Skip metrics that are derived and do not have an explicit
+ * PDH API retrieval needed ... do nothing here.
+ */
+ ;
+ }
+ else if (pmp->flags & M_OPTIONAL) {
+ pmp->flags |= M_NOVALUES;
+ return 0;
+ }
+ else {
+ __pmNotifyErr(LOG_ERR, "windows_open: PdhExpandCounterPathA "
+ "failed @ metric pmid=%s pattern=\"%s\": %s\n",
+ pmIDStr(pmp->desc.pmid), pmp->pat, pdherrstr(pdhsts));
+ }
+ pmp->flags |= M_NOVALUES;
+ return -1;
+ }
+
+ /*
+ * PdhExpandCounterPathA is apparently busted ... the length
+ * returned includes one byte _after_ the last NULL byte
+ * string terminator, but the final byte is apparently
+ * not being set ... force the issue
+ */
+ pattern[result_sz-1] = '\0';
+ for (p = pattern; *p; p += lstrlen(p) + 1) {
+ pdh_value_t *pvp;
+
+ pmp->num_vals++;
+ if (pmp->num_vals > pmp->num_alloc) {
+ size = pmp->num_vals * sizeof(pdh_value_t);
+ if ((pmp->vals = (pdh_value_t *)realloc(pmp->vals, size)) == NULL) {
+ __pmNotifyErr(LOG_ERR, "windows_open: Error: values realloc "
+ "(%d x %d) failed @ metric %s [%s]: ",
+ pmp->num_vals, sizeof(pdh_value_t),
+ pmIDStr(pmp->desc.pmid), p);
+ pmp->num_alloc = 0;
+ return -1;
+ }
+ pmp->num_alloc = pmp->num_vals;
+ }
+
+ pvp = &pmp->vals[pmp->num_vals-1];
+ if (pmp->desc.indom == PM_INDOM_NULL) {
+ /* singular instance */
+ pvp->inst = PM_IN_NULL;
+ if (pmp->num_vals > 1) {
+ char *q;
+ int k;
+
+ /*
+ * report only once per pattern
+ */
+ __pmNotifyErr(LOG_ERR, "windows_open: Warning: singular "
+ "metric %s has more than one instance ...\n",
+ pmIDStr(pmp->desc.pmid));
+ fprintf(stderr, " pattern: \"%s\"\n", pmp->pat);
+ for (k = 0, q = pattern; *q; q += lstrlen(q) + 1, k++)
+ fprintf(stderr, " match[%d]: \"%s\"\n", k, q);
+ fprintf(stderr, "... skip this counter\n");
+
+ /* next realloc() will be a NOP */
+ pmp->num_vals--;
+
+ /* no more we can do here, onto next metric-pattern */
+ break;
+ }
+ }
+ else {
+ /*
+ * if metric has instance domain, parse pattern using
+ * indom type to extract instance name and number, and
+ * add into indom cache data structures as needed.
+ */
+ if ((pvp->inst = windows_lookup_instance(p, pmp)) < 0) {
+ /*
+ * error reported in windows_check_instance() ...
+ * we cannot return any values for this instance if
+ * we don't recognize the name ... skip this one,
+ * the next realloc() (if any) will be a NOP
+ */
+ pmp->num_vals--;
+
+ /* move onto next instance */
+ continue;
+ }
+ windows_indom_setup[index] = 1;
+ }
+
+ if (visitor)
+ visitor(pmp, p, pvp);
+ }
+
+ return 0;
+}
+
+void
+windows_open(int domain)
+{
+ int i;
+
+ windows_setup_globals();
+
+ for (i = 0; i < NUMINDOMS; i++) {
+ if (windows_indom_fixed(i))
+ pmdaCacheOp(INDOM(domain, i), PMDA_CACHE_LOAD);
+ windows_indom_reset[i] = 0;
+ }
+
+ /*
+ * This initialisation can take a long time - we have many metrics
+ * now for Windows. Better to delay this until we need to do it,
+ * and then only for the metrics needed. However, we cannot delay
+ * for those metrics that may change descriptors depending on the
+ * type of platform (64/32 bit, kernel version, etc), so those we
+ * verify up-front.
+ */
+ for (i = 0; i < metricdesc_sz; i++) {
+ if ((metricdesc[i].flags & M_AUTO64) || (pmDebug & DBG_TRACE_LIBPMDA))
+ windows_visit_metric(&metricdesc[i], windows_verify_callback);
+ }
+
+ for (i = 0; i < NUMINDOMS; i++) {
+ /* Do we want to persist this instance domain to disk? */
+ if (windows_indom_reset[i] && windows_indom_fixed(i))
+ pmdaCacheOp(INDOM(domain, i), PMDA_CACHE_SAVE);
+ }
+}