diff options
Diffstat (limited to 'src/pmdas/windows/instance.c')
-rw-r--r-- | src/pmdas/windows/instance.c | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/src/pmdas/windows/instance.c b/src/pmdas/windows/instance.c new file mode 100644 index 0000000..fbbb083 --- /dev/null +++ b/src/pmdas/windows/instance.c @@ -0,0 +1,376 @@ +/* + * 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 <ctype.h> + +int +windows_indom_fixed(int serial) +{ + return (serial != PROCESS_INDOM && serial != THREAD_INDOM); +} + +void +windows_instance_refresh(pmInDom indom) +{ + int i, index, setup; + + index = pmInDom_serial(indom); + windows_indom_reset[index] = 0; + setup = windows_indom_setup[index]; + + for (i = 0; i < metricdesc_sz; i++) { + pdh_metric_t *mp = &metricdesc[i]; + + if (indom != mp->desc.indom || mp->pat[0] != '\\') + continue; + if (!setup || (mp->flags & M_REDO)) + windows_visit_metric(mp, NULL); + break; + } + + /* Do we want to persist this instance domain to disk? */ + if (windows_indom_reset[index] && windows_indom_fixed(index)) + pmdaCacheOp(indom, PMDA_CACHE_SAVE); +} + +int +windows_lookup_instance(char *path, pdh_metric_t *mp) +{ + __pmInDom_int *ip; + static void *seen = (void *)0xfeedbabe; + void *sp; + char *p, *q, *name = NULL; + int sts, ok = 0; + + if (mp->desc.indom == PM_INDOM_NULL) + return PM_IN_NULL; + + ip = (__pmInDom_int *)&mp->desc.indom; + switch (ip->serial) { + /* + * Examples: + * \\WINBUILD\PhysicalDisk(0 C:)\Disk Reads/sec + * \\SOMEHOST\PhysicalDisk(0 C: D: E:)\Disk Transfers/sec + * \\WINBUILD\PhysicalDisk(_Total)\Disk Write Bytes/sec + */ + case DISK_INDOM: + p = strchr(path, '('); // skip hostname and metric name + if (p != NULL) { + p++; + if (strncmp(p, "_Total)", 7) == 0) { + /* + * The totals get enumerated in the per disk path + * expansion, just skip 'em here + */ + return -1; + } + while (isascii((int)*p) && isdigit((int)*p)) p++; + if (*p == ' ') { + p++; + q = strchr(p, ')'); + if (q != NULL) { + name = (char *)malloc(q - p + 1); + if (name != NULL) { + strncpy(name, p, q - p); + name[q - p] = '\0'; + ok = 1; + } + else { + __pmNotifyErr(LOG_ERR, "windows_check_instance: " + "Error: DISK_INDOM malloc[%d] failed " + "path=%s\n", q - p + 1, path); + return -1; + } + /* + * If more than one drive letter maps to the same + * logical disk (e.g. mirrored root),, the name + * contains spaces, e.g. + * "C: D:" ... replace ' ' by '_' to play by the + * PCP rules for instance names + */ + for (p = name; *p; p++) { + if (*p == ' ') *p = '_'; + } + } + } + } + /* + * expecting something like ...\PhysicalDisk(N name)... + * we will just have to ignore this one! (might be due + * to: http://support.microsoft.com/kb/974878 - fail as + * entries like "17" and "17#1" are not useable anyway) + */ + if (!ok) { + if (pmDebug & DBG_TRACE_LIBPMDA) + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "unrecognized disk instance: %s\n", path); + free(name); + return -1; + } + break; + + /* + * Examples: + * \\WINBUILD\Processor(0)\% User Time + * \\WINBUILD\Processor(_Total)\Interrupts/sec + */ + case CPU_INDOM: + p = strchr(path, '('); // skip hostname and metric name + if (p != NULL) { + p++; + if (strncmp(p, "_Total)", 7) == 0) { + /* + * The totals get enumerated in the per cpu path + * expansion, just skip 'em here + */ + return -1; + } + int inst = atoi(p); + name = (char *)malloc(8); // "cpuNNNN" + if (name != NULL) + sprintf(name, "cpu%d", inst); + ok = 1; + } + /* + * expecting something like ...\Processor(N)... + * don't know what to do with this one! + */ + if (!ok) { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "unrecognized cpu instance: %s\n", path); + free(name); + return -1; + } + break; + + /* + * Examples: + * \\WINNT\Network Interface(MS TCP Loopback interface)\Bytes Total/sec + */ + case NETIF_INDOM: + p = strchr(path, '('); // skip hostname and metric name + if (p != NULL) { + p++; + q = strchr(p, ')'); + if (q != NULL) { + name = (char *)malloc(q - p + 1); + if (name != NULL) { + strncpy(name, p, q - p); + name[q - p] = '\0'; + ok = 1; + } + else { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "malloc[%d] failed for NETIF_INDOM " + "path=%s\n", q - p + 1, path); + return -1; + } + /* + * The network interface names have many spaces and are + * not unique up to the first space by any means. So, + * replace ' 's to play by the PCP instance name rules. + */ + for (p = name; *p; p++) { + if (*p == ' ') *p = '_'; + } + } + } + /* + * expecting something like ...\Network Interface(...)... + * don't know what to do with this one! + */ + if (!ok) { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "unrecognized network interface instance: %s\n", path); + free(name); + return -1; + } + break; + + /* + * Examples: + * \\TOWER\LogicalDisk(C:)\% Free Space + */ + case FILESYS_INDOM: + p = strchr(path, '('); // skip hostname and metric name + if (p != NULL) { + p++; + if (strncmp(p, "_Total)", 7) == 0) { + /* + * The totals value makes no semantic sense, + * just skip it here + */ + return -1; + } + while (isascii((int)*p) && isdigit((int)*p)) + p++; + if (*p == ' ') + p++; + q = strchr(p, ')'); + if (q != NULL) { + name = (char *)malloc(q - p + 1); + if (name != NULL) { + strncpy(name, p, q - p); + name[q - p] = '\0'; + ok = 1; + } + else { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "malloc[%d] failed for LDISK_INDOM path=%s\n", + q - p + 1, path); + return -1; + } + } + } + /* + * expecting something like ...\LogicalDisk(C:)... + * don't know what to do with this one! + */ + if (!ok) { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "unrecognized logical disk instance: %s\n", path); + free(name); + return -1; + } + break; + + /* + * SQLServer instance domains all have similar syntax + * + * Examples: + * \\TOWER\SQLServer:Locks(Table)\Average Wait Time (ms) + * \\TOWER\SQLServer:Cache Manager(Cursors)\Cache Hit Ratio + * \\TOWER\SQLServer:Databases(ACONEX_SYS)\Transactions/sec + */ + case SQL_LOCK_INDOM: + case SQL_CACHE_INDOM: + case SQL_DB_INDOM: + case SQL_USER_INDOM: + p = strchr(path, '('); // skip hostname and metric name + if (p != NULL) { + p++; + if (strncmp(p, "_Total)", 7) == 0) { + /* + * The totals are done as independent metrics, + * just skip them here + */ + return -1; + } + q = strchr(p, ')'); + if (q != NULL) { + name = (char *)malloc(q - p + 1); + if (name != NULL) { + strncpy(name, p, q - p); + name[q - p] = '\0'; + ok = 1; + } + else { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "malloc[%d] failed, SQL_INDOM path=%s\n", + q - p + 1, path); + return -1; + } + + /* + * The user counter names have many spaces and are + * not unique up to the first space by any means. So, + * replace ' 's to play by the PCP instance name rules. + */ + if (ip->serial == SQL_USER_INDOM) { + for (p = name; *p; p++) + if (*p == ' ') *p = '_'; + } + } + } + /* + * expecting something like ... \SQLServer:...(...)\... + * don't know what to do with this one! + */ + if (!ok) { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "unrecognized SQLServer instance: %s\n", path); + free(name); + return -1; + } + break; + + /* + * Per-process and per-thread instance domain + * + * Examples: + * \\TOWER\Process(svchost#6)\% Processor Time + * \\TOWER\Thread(svchost/1#1)\% Processor Time + * \\TOWER\Thread(Idle/0)\ID Process + * \\TOWER\Thread(Idle/0)\ID Thread + */ + case PROCESS_INDOM: + case THREAD_INDOM: + p = strchr(path, '('); // skip hostname and Process/Thread + if (p != NULL) { + p++; + if ((strncmp(p, "_Total)", 7) == 0) || + (strncmp(p, "_Total/", 7) == 0)) { + /* + * The totals are done as independent metrics, + * just skip them here + */ + return -1; + } + q = strchr(p, ')'); + if (q != NULL) { + name = (char *)malloc(q - p + 1); + if (name != NULL) { + strncpy(name, p, q - p); + name[q - p] = '\0'; + ok = 1; + } + else { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "malloc[%d] failed, process/thread path=%s\n", + q - p + 1, path); + return -1; + } + } + } + /* + * expecting something like ... \Process(...)\... + * don't know what to do with this one! + */ + if (!ok) { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "unrecognized process/thread name: %s\n", path); + free(name); + return -1; + } + break; + + default: + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "pmInDom %s is unknown for metric %s\n", + pmInDomStr(mp->desc.indom), pmIDStr(mp->desc.pmid)); + return -1; + } + + sts = pmdaCacheLookupName(mp->desc.indom, name, &ok, &sp); + if (sts != PMDA_CACHE_ACTIVE) { + if (sp != seen) /* new instance, never seen before, mark it */ + windows_indom_reset[pmInDom_serial(mp->desc.indom)] = 1; + ok = pmdaCacheStore(mp->desc.indom, PMDA_CACHE_ADD, name, seen); + } + free(name); + return ok; +} |