diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
commit | 47e6e7c84f008a53061e661f31ae96629bc694ef (patch) | |
tree | 648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmdas/etw | |
download | pcp-47e6e7c84f008a53061e661f31ae96629bc694ef.tar.gz |
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmdas/etw')
-rw-r--r-- | src/pmdas/etw/GNUmakefile | 78 | ||||
-rw-r--r-- | src/pmdas/etw/event.c | 251 | ||||
-rw-r--r-- | src/pmdas/etw/event.h | 94 | ||||
-rw-r--r-- | src/pmdas/etw/help | 68 | ||||
-rw-r--r-- | src/pmdas/etw/pcp.xml | bin | 0 -> 107596 bytes | |||
-rw-r--r-- | src/pmdas/etw/pmda.c | 513 | ||||
-rw-r--r-- | src/pmdas/etw/pmns | 132 | ||||
-rw-r--r-- | src/pmdas/etw/root | 10 | ||||
-rw-r--r-- | src/pmdas/etw/tdhconsume.c | 923 | ||||
-rw-r--r-- | src/pmdas/etw/tdhlist.c | 267 | ||||
-rw-r--r-- | src/pmdas/etw/util.c | 190 | ||||
-rw-r--r-- | src/pmdas/etw/util.h | 31 |
12 files changed, 2557 insertions, 0 deletions
diff --git a/src/pmdas/etw/GNUmakefile b/src/pmdas/etw/GNUmakefile new file mode 100644 index 0000000..2951076 --- /dev/null +++ b/src/pmdas/etw/GNUmakefile @@ -0,0 +1,78 @@ +# +# Copyright (c) 2011, Nathan Scott. All Rights Reserved. +# +# 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. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs +WINDIR = $(TOPDIR)/src/win32ctl + +IAM = etw +DOMAIN = ETW +CFILES = util.c event.c pmda.c +HFILES = util.h event.h +LSRCFILES = tdhlist.c tdhconsume.c pcp.xml pmns root help +LCFLAGS = -I$(WINDIR)/include -D_WIN32_WINNT=0x0600 -DFORCEINLINE="static inline" +LLDFLAGS = -L$(WINDIR)/lib +LLDLIBS = $(PCP_PMDALIB) -lpcp_tdh + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LIBTARGET = pmda_etw.dll +EXTRATARGETS = tdhlist.exe tdhconsume.exe help.dir help.pag +LDIRT = $(EXTRATARGETS) root_etw domain.h $(IAM).log + +CONF_LINE = "etw 87 dso etw_init $(PCP_PMDAS_DIR)/etw/pmda_etw.dll" + +default: build-me + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "mingw" +build-me: root_etw $(LSRCFILES) $(LIBTARGET) $(EXTRATARGETS) + @if [ `grep -c $(CONF_LINE) ../pmcd.conf` -eq 0 ]; then \ + echo $(CONF_LINE) >> ../pmcd.conf ; \ + fi + +install: build-me + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 tdhlist.exe $(PMDADIR)/tdhlist.exe + $(INSTALL) -m 644 pmns $(PMDADIR)/root + $(INSTALL) -m 644 domain.h help.dir help.pag help $(PMDADIR) + $(INSTALL) -m 644 root_etw $(PCP_VAR_DIR)/pmns/root_etw +else +build-me: +install: +endif + +help.dir help.pag : help root_etw + $(RUN_IN_BUILD_ENV) $(TOPDIR)/src/newhelp/newhelp -n root_etw -v 2 -o help < help + +default_pcp: default + +install_pcp: install + +root_etw: ../../pmns/stdpmid $(PMNS) + rm -f root_etw + sed -e 's;<stdpmid>;"../../pmns/stdpmid";' <root \ + | $(RUN_IN_BUILD_ENV) $(TOPDIR)/src/pmcpp/pmcpp \ + | sed -e '/^#/d' -e '/^$$/d' >root_etw + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +$(OBJECTS): domain.h util.h + +tdhlist.exe: tdhlist.o util.o + $(CCF) -o tdhlist.exe tdhlist.o util.o $(LLDFLAGS) -lpcp_tdh + +tdhconsume.exe: tdhconsume.o util.o + $(CCF) -o tdhconsume.exe tdhconsume.o util.o $(LLDFLAGS) -lpcp_tdh -lwsock32 diff --git a/src/pmdas/etw/event.c b/src/pmdas/etw/event.c new file mode 100644 index 0000000..f07aa5e --- /dev/null +++ b/src/pmdas/etw/event.c @@ -0,0 +1,251 @@ +/* + * Event queue support for the ETW PMDA + * + * Copyright (c) 2011 Nathan Scott. All rights reserved. + * + * 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 "event.h" +#include "util.h" + +static struct { + TRACEHANDLE session; + EVENT_TRACE_PROPERTIES properties; + EVENT_TRACE_LOGFILE tracemode; + BOOL sequence; + ULONG enabled; + + /* session stats */ + ULONG buffer_count; + ULONG buffer_size; + ULONG buffer_bytes; + ULONG buffer_reads; + ULONG events_lost; +} sys; + +ULONG WINAPI +event_buffer_callback(PEVENT_TRACE_LOGFILE mode) +{ + sys.buffer_count++; + sys.buffer_size = mode->BufferSize; + sys.buffer_reads += mode->BuffersRead; + sys.buffer_bytes += mode->Filled; + sys.events_lost += mode->EventsLost; + return TRUE; +} + +/* Difference in seconds between 1/1/1970 and 1/1/1601 */ +#define EPOCH_DELTA_IN_MICROSEC 11644473600000000ULL + +static void +event_decode_timestamp(PEVENT_RECORD event, struct timeval *tv) +{ + FILETIME ft; /* 100-nanosecond intervals since 1/1/1601 */ + ft.dwHighDateTime = event->EventHeader.TimeStamp.HighPart; + ft.dwLowDateTime = event->EventHeader.TimeStamp.LowPart; + + __uint64_t tmp = 0; + tmp |= ft.dwHighDateTime; + tmp <<= 32; + tmp |= ft.dwLowDateTime; + tmp /= 10; /* convert to microseconds */ + tmp -= EPOCH_DELTA_IN_MICROSEC; /* convert Win32 -> Unix epoch */ + + tv->tv_sec = tmp / 1000000UL; + tv->tv_usec = tmp % 1000000UL; +} + +void +event_duplicate_record(PEVENT_RECORD source, PEVENT_RECORD target) +{ + memcpy(source, target, sizeof(EVENT_RECORD)); +} + +void +event_duplicate_buffer(PEVENT_RECORD source, char *buffer, size_t *bytes) +{ + PTRACE_EVENT_INFO pinfo = (PTRACE_EVENT_INFO)buffer; + DWORD size = DEFAULT_MAXMEM, sts; + + sts = TdhGetEventInformation(source, 0, NULL, pinfo, &size); + if (sts == ERROR_INSUFFICIENT_BUFFER) { + *bytes = 0; /* too large for us, too bad, bail out */ + } else { + *bytes = size; + } +} + +/* + * This is the main event callback routine. It uses a fixed + * maximally sized buffer to capture and do minimal decoding + * of the event, before passing it into a queue. Avoid any + * memory allocation and/or copying here where possible, as + * this is the event arrival fast path. + * Note: if there are no clients, the queuing code will drop + * this event (within pmdaEventQueueAppend), so don't waste + * time here - basically just lookup the queue and dispatch. + */ +VOID WINAPI +event_record_callback(PEVENT_RECORD event) +{ + size_t bytes; + struct timeval timestamp; + struct { + EVENT_RECORD record; + char buffer[DEFAULT_MAXMEM]; + } localevent; + etw_event_t *entry; + + LPGUID guid = &event->EventHeader.ProviderId; + int eventid = event->EventHeader.EventDescriptor.Id; + int version = event->EventHeader.EventDescriptor.Version; + + if ((entry = event_table_lookup(guid, eventid, version)) != NULL) { + + event_decode_timestamp(event, ×tamp); + event_duplicate_record(event, &localevent.record); + event_duplicate_buffer(event, &localevent.buffer[0], &bytes); + bytes += sizeof(EVENT_RECORD); + + event_queue_lock(entry); + pmdaEventQueueAppend(entry->queueid, &localevent, bytes, ×tamp); + event_queue_unlock(entry); + + } else { + __pmNotifyErr(LOG_ERR, "failed to enqueue event %d:%d from provider %s", + eventid, version, strguid(guid)); + } +} + +static void +event_sys_setup(void) +{ + ULONG size = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(KERNEL_LOGGER_NAME); + + sys.properties.Wnode.BufferSize = size; + sys.properties.Wnode.Flags = WNODE_FLAG_TRACED_GUID; + sys.properties.Wnode.ClientContext = 1; + sys.properties.Wnode.Guid = SystemTraceControlGuid; + sys.properties.EnableFlags = sys.enabled; + sys.properties.LogFileMode = EVENT_TRACE_REAL_TIME_MODE; + if (sys.sequence) + sys.properties.LogFileMode |= EVENT_TRACE_USE_GLOBAL_SEQUENCE; + sys.properties.LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + + sys.tracemode.LoggerName = KERNEL_LOGGER_NAME; + sys.tracemode.BufferCallback = event_buffer_callback; + sys.tracemode.EventRecordCallback = event_record_callback; + sys.tracemode.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME; +} + +/* Kernel tracing thread main() */ +static DWORD WINAPI +event_trace_sys(LPVOID ptr) +{ + ULONG sts, retried = 0; + TRACEHANDLE trace; + +retry_session: + event_sys_setup(); + + sts = StartTrace(&sys.session, KERNEL_LOGGER_NAME, &sys.properties); + if (sts != ERROR_SUCCESS) { + if (retried) { + __pmNotifyErr(LOG_ERR, "Cannot start session (in use)"); + } else if (sts == ERROR_ALREADY_EXISTS) { + __pmNotifyErr(LOG_WARNING, "%s session in use, retry.. (flags=%lx)", + KERNEL_LOGGER_NAME, sys.enabled); + sys.properties.EnableFlags = 0; + ControlTrace(sys.session, KERNEL_LOGGER_NAME, + &sys.properties, EVENT_TRACE_CONTROL_STOP); + retried = 1; + goto retry_session; + } + return sts; + } + + trace = OpenTrace(&sys.tracemode); + if (trace == INVALID_PROCESSTRACE_HANDLE) { + sts = GetLastError(); + __pmNotifyErr(LOG_ERR, "failed to open kernel trace: %s (%lu)", + tdherror(sts), sts); + return sts; + } + + sts = ProcessTrace(&trace, 1, NULL, NULL); /* blocks, awaiting events */ + if (sts == ERROR_CANCELLED) + sts = ERROR_SUCCESS; + if (sts != ERROR_SUCCESS) + __pmNotifyErr(LOG_ERR, "failed to process kernel traces: %s (%lu)", + tdherror(sts), sts); + return sts; +} + +void +event_queue_lock(etw_event_t *entry) +{ + WaitForSingleObject(entry->mutex, INFINITE); +} + +void +event_queue_unlock(etw_event_t *entry) +{ + ReleaseMutex(entry->mutex); +} + +int +event_init(void) +{ + HANDLE thread; + + __pmNotifyErr(LOG_INFO, "%s: Starting up tracing ...", __FUNCTION__); + thread = CreateThread(NULL, 0, event_trace_sys, &sys, 0, NULL); + if (thread == NULL) + return -ECHILD; + CloseHandle(thread); /* no longer need the handle */ + return 0; +} + +void __attribute__((constructor)) +event_startup(void) +{ + sys.session = INVALID_PROCESSTRACE_HANDLE; +} + +void __attribute__((destructor)) +event_shutdown(void) +{ + __pmNotifyErr(LOG_INFO, "%s: Shutting down tracing ...", __FUNCTION__); + if (sys.session != INVALID_PROCESSTRACE_HANDLE) { + sys.properties.EnableFlags = 0; + ControlTrace(sys.session, 0, &sys.properties, EVENT_TRACE_CONTROL_STOP); + sys.session = INVALID_PROCESSTRACE_HANDLE; + } +} + +int +event_decoder(int eventarray, void *buffer, size_t size, + struct timeval *timestamp, void *data) +{ + int sts; /* , handle = *(int *)data; */ + pmAtomValue atom; + pmID pmid = 0; /* TODO */ + + sts = pmdaEventAddRecord(eventarray, timestamp, PM_EVENT_FLAG_POINT); + if (sts < 0) + return sts; + atom.cp = buffer; + sts = pmdaEventAddParam(eventarray, pmid, PM_TYPE_STRING, &atom); + if (sts < 0) + return sts; + return 1; /* simple decoder, added just one event array */ +} diff --git a/src/pmdas/etw/event.h b/src/pmdas/etw/event.h new file mode 100644 index 0000000..1f39ef0 --- /dev/null +++ b/src/pmdas/etw/event.h @@ -0,0 +1,94 @@ +/* + * Event queue support for the ETW PMDA + * + * Copyright (c) 2011 Nathan Scott. All rights reversed. + * + * 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. + */ +#ifndef _EVENT_H +#define _EVENT_H + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include <tdh.h> +#include <evntrace.h> + +enum { + CLUSTER_KERNEL_PROCESS = 0, + CLUSTER_KERNEL_THREAD = 1, + CLUSTER_KERNEL_IMAGE_LOAD = 2, + CLUSTER_KERNEL_DISK_IO = 3, + CLUSTER_KERNEL_DISK_FILE_IO = 4, + CLUSTER_KERNEL_MEMORY_PAGE_FAULTS = 5, + CLUSTER_KERNEL_MEMORY_HARD_FAULTS = 6, + CLUSTER_KERNEL_NETWORK_TCPIP = 7, + CLUSTER_KERNEL_REGISTRY = 8, + CLUSTER_KERNEL_DBGPRINT = 9, + CLUSTER_KERNEL_PROCESS_COUNTERS = 10, + CLUSTER_KERNEL_CSWITCH = 11, + CLUSTER_KERNEL_DPC = 12, + CLUSTER_KERNEL_INTERRUPT = 13, + CLUSTER_KERNEL_SYSTEMCALL = 14, + CLUSTER_KERNEL_DISK_IO_INIT = 15, + CLUSTER_KERNEL_ALPC = 16, + CLUSTER_KERNEL_SPLIT_IO = 17, + CLUSTER_KERNEL_DRIVER = 18, + CLUSTER_KERNEL_PROFILE = 19, + CLUSTER_KERNEL_FILE_IO = 20, + CLUSTER_KERNEL_FILE_IO_INIT = 21, + CLUSTER_KERNEL_DISPATCHER = 22, + CLUSTER_KERNEL_VIRTUAL_ALLOC = 23, + CLUSTER_KERNEL_EXTENSION = 24, + CLUSTER_KERNEL_FORWARD_WMI = 25, + CLUSTER_KERNEL_ENABLE_RESERVE = 26, + + CLUSTER_SQLSERVER_RPC_STARTING = 30, + CLUSTER_SQLSERVER_BATCH_STARTING = 31, + + CLUSTER_CONFIGURATION = 250 +}; + +enum { + EVENT_PROCESS_START, + EVENT_PROCESS_EXIT, + EVENT_THREAD_START, + EVENT_THREAD_STOP, + EVENT_IMAGE_LOAD, + EVENT_IMAGE_UNLOAD, +}; + +#define DEFAULT_MAXMEM (128 * 1024) /* 128K per event queue */ + +typedef struct { + int queueid; + int eventid; + int version; + ULONG flags; + HANDLE mutex; + char pmnsname[64]; + const GUID *provider; + LPTSTR pname; + ULONG plen; +} etw_event_t; + +extern etw_event_t eventtab[]; +extern int event_init(void); +extern void event_shutdown(void); +extern int event_decoder(int arrayid, void *buffer, size_t size, + struct timeval *timestamp, void *data); + +extern etw_event_t *event_table_lookup(LPGUID guid, int eventid, int version); + +void event_queue_lock(etw_event_t *entry); +void event_queue_unlock(etw_event_t *entry); + +#endif /* _EVENT_H */ diff --git a/src/pmdas/etw/help b/src/pmdas/etw/help new file mode 100644 index 0000000..a48a3fc --- /dev/null +++ b/src/pmdas/etw/help @@ -0,0 +1,68 @@ +/* + * Event Tracing for Windows PMDA + * + * Copyright (c) 2011 Nathan Scott. All Rights Reserved. + * + * 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. + */ + +@ etw.kernel.process.start.count +@ etw.kernel.process.start.records +@ etw.kernel.process.start.numclients +@ etw.kernel.process.start.queuemem +@ etw.kernel.process.start.params.activityid +@ etw.kernel.process.start.params.pid +@ etw.kernel.process.start.params.parentid +@ etw.kernel.process.start.params.starttime +@ etw.kernel.process.start.params.session +@ etw.kernel.process.start.params.image_name +@ etw.kernel.process.exit.count +@ etw.kernel.process.exit.numclients +@ etw.kernel.process.exit.queuemem +@ etw.kernel.process.exit.params.activityid +@ etw.kernel.process.exit.params.pid +@ etw.kernel.process.exit.params.parentid +@ etw.kernel.process.exit.params.starttime +@ etw.kernel.process.exit.params.exittime +@ etw.kernel.process.exit.params.exitcode +@ etw.kernel.process.exit.params.handle_count +@ etw.kernel.process.exit.params.commit_charge +@ etw.kernel.process.exit.params.commit_peak +@ etw.kernel.process.exit.params.image_name +@ etw.kernel.process.thread.start.count +@ etw.kernel.process.thread.start.records +@ etw.kernel.process.thread.start.numclients +@ etw.kernel.process.thread.start.queuemem +@ etw.kernel.process.thread.start.params.tid +@ etw.kernel.process.thread.start.params.pid +@ etw.kernel.process.thread.stop.count +@ etw.kernel.process.thread.stop.records +@ etw.kernel.process.thread.stop.numclients +@ etw.kernel.process.thread.stop.queuemem +@ etw.kernel.process.thread.stop.params.activityid +@ etw.kernel.process.thread.stop.params.tid +@ etw.kernel.process.thread.stop.params.pid +@ etw.kernel.process.image_load.count +@ etw.kernel.process.image_load.records +@ etw.kernel.process.image_load.numclients +@ etw.kernel.process.image_load.queuemem +@ etw.kernel.process.image_load.params.activityid +@ etw.kernel.process.image_load.params.pid +@ etw.kernel.process.image_load.params.name +@ etw.kernel.process.image_load.params.size +@ etw.kernel.process.image_unload.count +@ etw.kernel.process.image_unload.records +@ etw.kernel.process.image_unload.numclients +@ etw.kernel.process.image_unload.queuemem +@ etw.kernel.process.image_unload.params.activityid +@ etw.kernel.process.image_unload.params.pid +@ etw.kernel.process.image_unload.params.name +@ etw.kernel.process.image_unload.params.size diff --git a/src/pmdas/etw/pcp.xml b/src/pmdas/etw/pcp.xml Binary files differnew file mode 100644 index 0000000..d87f6a0 --- /dev/null +++ b/src/pmdas/etw/pcp.xml diff --git a/src/pmdas/etw/pmda.c b/src/pmdas/etw/pmda.c new file mode 100644 index 0000000..5a802bf --- /dev/null +++ b/src/pmdas/etw/pmda.c @@ -0,0 +1,513 @@ +/* + * Event Tracing for Windows PMDA + * + * Copyright (c) 2011 Nathan Scott. All Rights Reserved. + * + * 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 "event.h" +#include "domain.h" +#include "util.h" + +etw_event_t eventtab[] = { + { EVENT_PROCESS_START, 0, 1, EVENT_TRACE_FLAG_PROCESS, NULL, + "process.start", &SystemTraceControlGuid, + KERNEL_LOGGER_NAME, sizeof(KERNEL_LOGGER_NAME) }, + { EVENT_PROCESS_EXIT, 2, 1, EVENT_TRACE_FLAG_PROCESS, NULL, + "process.exit", &SystemTraceControlGuid, + KERNEL_LOGGER_NAME, sizeof(KERNEL_LOGGER_NAME) }, + { EVENT_THREAD_START, 3, 1, EVENT_TRACE_FLAG_THREAD, NULL, + "thread.start", &SystemTraceControlGuid, + KERNEL_LOGGER_NAME, sizeof(KERNEL_LOGGER_NAME) }, + { EVENT_THREAD_STOP, 4, 1, EVENT_TRACE_FLAG_THREAD, NULL, + "thread.stop", &SystemTraceControlGuid, + KERNEL_LOGGER_NAME, sizeof(KERNEL_LOGGER_NAME) }, + { EVENT_IMAGE_LOAD, 5, 1, EVENT_TRACE_FLAG_IMAGE_LOAD, NULL, + "image.load", &SystemTraceControlGuid, + KERNEL_LOGGER_NAME, sizeof(KERNEL_LOGGER_NAME) }, + { EVENT_IMAGE_UNLOAD, 6, 1, EVENT_TRACE_FLAG_IMAGE_LOAD, NULL, + "image.unload", &SystemTraceControlGuid, + KERNEL_LOGGER_NAME, sizeof(KERNEL_LOGGER_NAME) }, +}; + +#if 0 +static etw_event_t etwevents[] = { + { CLUSTER_KERNEL_PROCESS, 0, EVENT_TRACE_FLAG_PROCESS, + { CLUSTER_KERNEL_THREAD, 0, + EVENT_TRACE_FLAG_THREAD, "kernel.thread" }, + { CLUSTER_KERNEL_IMAGE_LOAD, 0, + EVENT_TRACE_FLAG_IMAGE_LOAD, "kernel.image_load" }, + { CLUSTER_KERNEL_DISK_IO, 0, + EVENT_TRACE_FLAG_DISK_IO, "kernel.disk_io" }, + { CLUSTER_KERNEL_DISK_FILE_IO, 0, + EVENT_TRACE_FLAG_DISK_FILE_IO, "kernel.disk_file_io" }, + { CLUSTER_KERNEL_MEMORY_PAGE_FAULTS, 0, + EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS, "kernel.memory_page_faults" }, + { CLUSTER_KERNEL_MEMORY_HARD_FAULTS, 0, + EVENT_TRACE_FLAG_MEMORY_HARD_FAULTS, "kernel.memory_hard_faults" }, + { CLUSTER_KERNEL_NETWORK_TCPIP, 0, + EVENT_TRACE_FLAG_NETWORK_TCPIP, "kernel.network_tcpip" }, + { CLUSTER_KERNEL_REGISTRY, 0, + EVENT_TRACE_FLAG_REGISTRY, "kernel.registry" }, + { CLUSTER_KERNEL_DBGPRINT, 0, + EVENT_TRACE_FLAG_DBGPRINT, "kernel.dbgprint" }, + { CLUSTER_KERNEL_PROCESS_COUNTERS, 0, + EVENT_TRACE_FLAG_PROCESS_COUNTERS, "kernel.process_counters" }, + { CLUSTER_KERNEL_CSWITCH, 0, + EVENT_TRACE_FLAG_CSWITCH, "kernel.cswitch" }, + { CLUSTER_KERNEL_DPC, 0, + EVENT_TRACE_FLAG_DPC, "kernel.dpc" }, + { CLUSTER_KERNEL_INTERRUPT, 0, + EVENT_TRACE_FLAG_INTERRUPT, "kernel.interrupt" }, + { CLUSTER_KERNEL_SYSTEMCALL, 0, + EVENT_TRACE_FLAG_SYSTEMCALL, "kernel.syscall" }, + { CLUSTER_KERNEL_DISK_IO_INIT, 0, + EVENT_TRACE_FLAG_DISK_IO_INIT, "kernel.disk_io_init" }, + { CLUSTER_KERNEL_ALPC, 0, + EVENT_TRACE_FLAG_ALPC, "kernel.alpc" }, + { CLUSTER_KERNEL_SPLIT_IO, 0, + EVENT_TRACE_FLAG_SPLIT_IO, "kernel.split_io" }, + { CLUSTER_KERNEL_DRIVER, 0, + EVENT_TRACE_FLAG_DRIVER, "kernel.driver" }, + { CLUSTER_KERNEL_PROFILE, 0, + EVENT_TRACE_FLAG_PROFILE, "kernel.profile" }, + { CLUSTER_KERNEL_FILE_IO, 0, + EVENT_TRACE_FLAG_FILE_IO, "kernel.file_io" }, + { CLUSTER_KERNEL_FILE_IO_INIT, 0, + EVENT_TRACE_FLAG_FILE_IO_INIT, "kernel.file_io_init" }, + { CLUSTER_KERNEL_DISPATCHER, 0, + EVENT_TRACE_FLAG_DISPATCHER, "kernel.dispatcher" }, + { CLUSTER_KERNEL_VIRTUAL_ALLOC, 0, + EVENT_TRACE_FLAG_VIRTUAL_ALLOC, "kernel.virtual_alloc" }, + { CLUSTER_KERNEL_EXTENSION, 0, + EVENT_TRACE_FLAG_EXTENSION, "kernel.extension" }, + { CLUSTER_KERNEL_FORWARD_WMI, 0, + EVENT_TRACE_FLAG_FORWARD_WMI, "kernel.forward_wmi" }, + { CLUSTER_KERNEL_ENABLE_RESERVE, 0, + EVENT_TRACE_FLAG_ENABLE_RESERVE, "kernel.enable_reserve" }, + + { CLUSTER_SQLSERVER_RPC_STARTING, 0, 0, "sqlserver.rpc.starting" }, + { CLUSTER_SQLSERVER_BATCH_STARTING, 0, 0, "sqlserver.sql.batch_starting" }, +}; +static int numqueues = sizeof(etwevents)/sizeof(etw_event_t); +#endif + + +static pmdaMetric metrictab[] = { +/* etw.kernel.process.start.count */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,0), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.start.records */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,1), PM_TYPE_EVENT, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.start.numclients */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,2), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.start.queuemem */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,3), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* etw.kernel.process.start.params.activityid */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,10), PM_TYPE_AGGREGATE, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.start.params.pid */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,11), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.start.params.parentid */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,12), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.start.params.starttime */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,13), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_USEC,0) }, }, +/* etw.kernel.process.start.params.session */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,14), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.start.params.image_name */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,15), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* etw.kernel.process.exit.count */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,20), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.exit.records */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,21), PM_TYPE_EVENT, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.exit.numclients */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,22), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.exit.queuemem */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,23), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* etw.kernel.process.exit.params.activityid */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,30), PM_TYPE_AGGREGATE, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.exit.params.pid */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,31), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.exit.params.parentid */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,32), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.exit.params.starttime */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,33), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_USEC,0) }, }, +/* etw.kernel.process.exit.params.exittime */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,34), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_USEC,0) }, }, +/* etw.kernel.process.exit.params.exitcode */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,35), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.exit.params.handle_count */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,36), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.exit.params.commit_charge */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,37), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.exit.params.commit_peak */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,38), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.exit.params.image_name */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,39), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* etw.kernel.process.thread.start.count */ + { &eventtab[EVENT_THREAD_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,50), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.thread.start.records */ + { &eventtab[EVENT_THREAD_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,51), PM_TYPE_EVENT, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.thread.start.numclients */ + { &eventtab[EVENT_THREAD_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,52), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.thread.start.queuemem */ + { &eventtab[EVENT_THREAD_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,53), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* etw.kernel.process.thread.start.params.activityid */ + { &eventtab[EVENT_THREAD_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,60), PM_TYPE_AGGREGATE, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.thread.start.params.tid */ + { &eventtab[EVENT_THREAD_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,61), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.thread.start.params.pid */ + { &eventtab[EVENT_THREAD_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,62), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* etw.kernel.process.thread.stop.count */ + { &eventtab[EVENT_THREAD_STOP], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,70), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.thread.stop.records */ + { &eventtab[EVENT_THREAD_STOP], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,71), PM_TYPE_EVENT, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.thread.stop.numclients */ + { &eventtab[EVENT_THREAD_STOP], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,72), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.thread.stop.queuemem */ + { &eventtab[EVENT_THREAD_STOP], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,73), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* etw.kernel.process.thread.stop.params.activityid */ + { &eventtab[EVENT_THREAD_STOP], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,80), PM_TYPE_AGGREGATE, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.thread.stop.params.tid */ + { &eventtab[EVENT_THREAD_STOP], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,81), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.thread.stop.params.pid */ + { &eventtab[EVENT_THREAD_STOP], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,82), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* etw.kernel.process.image_load.count */ + { &eventtab[EVENT_IMAGE_LOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,90), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.image_load.records */ + { &eventtab[EVENT_IMAGE_LOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,91), PM_TYPE_EVENT, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.image_load.numclients */ + { &eventtab[EVENT_IMAGE_LOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,92), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.image_load.queuemem */ + { &eventtab[EVENT_IMAGE_LOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,93), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* etw.kernel.process.image_load.params.activityid */ + { &eventtab[EVENT_IMAGE_LOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,100), PM_TYPE_AGGREGATE, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.image_load.params.pid */ + { &eventtab[EVENT_IMAGE_LOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,101), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.image_load.params.name */ + { &eventtab[EVENT_IMAGE_LOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,102), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.image_load.params.size */ + { &eventtab[EVENT_IMAGE_LOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,103), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* etw.kernel.process.image_unload.count */ + { &eventtab[EVENT_IMAGE_UNLOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,110), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.image_unload.records */ + { &eventtab[EVENT_IMAGE_UNLOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,111), PM_TYPE_EVENT, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.image_unload.numclients */ + { &eventtab[EVENT_IMAGE_UNLOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,112), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.image_unload.queuemem */ + { &eventtab[EVENT_IMAGE_UNLOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,113), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* etw.kernel.process.image_unload.params.activityid */ + { &eventtab[EVENT_IMAGE_UNLOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,120), PM_TYPE_AGGREGATE, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.image_unload.params.pid */ + { &eventtab[EVENT_IMAGE_UNLOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,121), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.image_unload.params.name */ + { &eventtab[EVENT_IMAGE_UNLOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,122), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.image_unload.params.size */ + { &eventtab[EVENT_IMAGE_UNLOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,123), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +}; + +static int +etw_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + etw_event_t *etw; + int sts = PMDA_FETCH_STATIC; + + __pmNotifyErr(LOG_WARNING, "called %s, mdesc=%p", __FUNCTION__, mdesc); + + switch (idp->cluster) { + case CLUSTER_KERNEL_PROCESS: + if ((etw = ((mdesc != NULL) ? mdesc->m_user : NULL)) == NULL) + return PM_ERR_PMID; + + switch (idp->item) { + case 0: /* etw.kernel.process.start.count */ + case 20: /* etw.kernel.process.exit.count */ + case 50: /* etw.kernel.thread.start.count */ + case 70: /* etw.kernel.thread.stop.count */ + case 90: /* etw.kernel.process.image_load.count */ + case 110: /* etw.kernel.process.image_unload.count */ + sts = pmdaEventQueueCounter(etw->queueid, atom); + break; + case 1: /* etw.kernel.process.start.records */ + case 21: /* etw.kernel.process.exit.records */ + case 51: /* etw.kernel.thread.start.records */ + case 71: /* etw.kernel.thread.stop.records */ + case 91: /* etw.kernel.process.image_load.records */ + case 111: /* etw.kernel.process.image_unload.records */ + event_queue_lock(etw); + sts = pmdaEventQueueRecords(etw->queueid, atom, + pmdaGetContext(), + event_decoder, &etw); + event_queue_unlock(etw); + break; + case 2: /* etw.kernel.process.start.numclients */ + case 22: /* etw.kernel.process.exit.numclients */ + case 52: /* etw.kernel.thread.start.numclients */ + case 72: /* etw.kernel.thread.stop.numclients */ + case 92: /* etw.kernel.process.image_load.numclients */ + case 112: /* etw.kernel.process.image_unload.numclients */ + sts = pmdaEventQueueClients(etw->queueid, atom); + break; + case 3: /* etw.kernel.process.start.queuemem */ + case 23: /* etw.kernel.process.exit.queuemem */ + case 53: /* etw.kernel.thread.start.queuemem */ + case 73: /* etw.kernel.thread.stop.queuemem */ + case 93: /* etw.kernel.process.image_load.queuemem */ + case 113: /* etw.kernel.process.image_unload.queuemem */ + sts = pmdaEventQueueMemory(etw->queueid, atom); + break; + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_CONFIGURATION: + switch (idp->item) { + case 0: /* etw.numclients */ + sts = pmdaEventClients(atom); + break; + default: + return PM_ERR_PMID; + } + break; + + default: + break; + } + + return sts; +} + +static int +etw_profile(__pmProfile *prof, pmdaExt *pmda) +{ + pmdaEventNewClient(pmda->e_context); + return 0; +} + +static int +etw_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + __pmNotifyErr(LOG_WARNING, "called %s", __FUNCTION__); + pmdaEventNewClient(pmda->e_context); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +static int +etw_store(pmResult *result, pmdaExt *pmda) +{ + pmdaEventNewClient(pmda->e_context); + return PM_ERR_PERMISSION; +} + +static void +etw_end_contextCallBack(int context) +{ + __pmNotifyErr(LOG_WARNING, "called %s", __FUNCTION__); + pmdaEventEndClient(context); +} + +static int +etw_text(int ident, int type, char **buffer, pmdaExt *pmda) +{ + pmdaEventNewClient(pmda->e_context); + return pmdaText(ident, type, buffer, pmda); +} + +static int +event_equal(etw_event_t *event, LPGUID provider, int eventid, int version) +{ + if (memcmp(event->provider, provider, sizeof(GUID)) != 0) + return 0; + return (event->eventid == eventid && event->version == version); +} + +etw_event_t * +event_table_lookup(LPGUID guid, int eventid, int version) +{ + static int last; /* punt on previous type of event reoccurring */ + int i = last; + + if (event_equal(&eventtab[i], guid, eventid, version)) + return &eventtab[i]; + + for (i = 0; i < sizeof(eventtab)/sizeof(eventtab[0]); i++) { + etw_event_t *e = &eventtab[i]; + if (event_equal(e, guid, eventid, version)) { + last = i; + return e; + } + } + return NULL; +} + +int +event_table_init(void) +{ + int i, id; + + for (i = 0; i < sizeof(eventtab)/sizeof(eventtab[0]); i++) { + HANDLE mutex = CreateMutex(NULL, FALSE, NULL); + etw_event_t *e = &eventtab[i]; + + if (!mutex) { + __pmNotifyErr(LOG_WARNING, "failed to create mutex for event %s", + e->pmnsname); + } else if ((id = pmdaEventNewQueue(e->pmnsname, DEFAULT_MAXMEM)) < 0) { + __pmNotifyErr(LOG_WARNING, "failed to create queue for event %s", + e->pmnsname); + } else { + e->queueid = id; + e->mutex = mutex; + } + } + return 0; +} + +void +etw_init(pmdaInterface *dp, const char *configfile) +{ + char helppath[MAXPATHLEN]; + int sep = __pmPathSeparator(); + + snprintf(helppath, sizeof(helppath), "%s%c" "etw" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_5, "etw DSO", helppath); + if (dp->status != 0) + return; + if (event_table_init() < 0) + return; + if (event_init() < 0) + return; + + dp->version.four.fetch = etw_fetch; + dp->version.four.store = etw_store; + dp->version.four.profile = etw_profile; + dp->version.four.text = etw_text; + + pmdaSetFetchCallBack(dp, etw_fetchCallBack); + pmdaSetEndContextCallBack(dp, etw_end_contextCallBack); + + pmdaInit(dp, NULL, 0, metrictab, sizeof(metrictab)/sizeof(metrictab[0])); +} diff --git a/src/pmdas/etw/pmns b/src/pmdas/etw/pmns new file mode 100644 index 0000000..7fc1b06 --- /dev/null +++ b/src/pmdas/etw/pmns @@ -0,0 +1,132 @@ +/* + * Event Tracing for Windows performance metrics namespace + * + * Copyright (c) 2011 Nathan Scott. All Rights Reserved. + * + * 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. + */ + +etw { + kernel +} + +etw.kernel { + process +} + +etw.kernel.process { + start + exit + thread + image_load + image_unload +} + +etw.kernel.process.start { + count ETW:0:0 + records ETW:0:1 + numclients ETW:0:2 + queuemem ETW:0:3 + params +} + +etw.kernel.process.start.params { + activityid ETW:0:10 + pid ETW:0:11 + parentid ETW:0:12 + starttime ETW:0:13 + session ETW:0:14 + image_name ETW:0:15 +} + +etw.kernel.process.exit { + count ETW:0:20 + records ETW:0:21 + numclients ETW:0:22 + queuemem ETW:0:23 + params +} + +etw.kernel.process.exit.params { + activityid ETW:0:30 + pid ETW:0:31 + parentid ETW:0:32 + starttime ETW:0:33 + exittime ETW:0:34 + exitcode ETW:0:35 + handle_count ETW:0:36 + commit_charge ETW:0:37 + commit_peak ETW:0:38 + image_name ETW:0:39 +} + +etw.kernel.process.thread { + start + stop +} + +etw.kernel.process.thread.start { + count ETW:0:50 + records ETW:0:51 + numclients ETW:0:52 + queuemem ETW:0:53 + params +} + +etw.kernel.process.thread.start.params { + activityid ETW:0:60 + tid ETW:0:61 + pid ETW:0:62 +} + +etw.kernel.process.thread.stop { + count ETW:0:70 + records ETW:0:71 + numclients ETW:0:72 + queuemem ETW:0:73 + params +} + +etw.kernel.process.thread.stop.params { + activityid ETW:0:80 + tid ETW:0:81 + pid ETW:0:82 +} + +etw.kernel.process.image_load { + count ETW:0:90 + records ETW:0:91 + numclients ETW:0:92 + queuemem ETW:0:93 + params +} + +etw.kernel.process.image_load.params { + activityid ETW:0:100 + pid ETW:0:101 + name ETW:0:102 + size ETW:0:103 +} + +etw.kernel.process.image_unload { + count ETW:0:110 + records ETW:0:111 + numclients ETW:0:112 + queuemem ETW:0:113 + params +} + +etw.kernel.process.image_unload.params { + activityid ETW:0:120 + pid ETW:0:121 + name ETW:0:122 + size ETW:0:123 +} diff --git a/src/pmdas/etw/root b/src/pmdas/etw/root new file mode 100644 index 0000000..9cfae6d --- /dev/null +++ b/src/pmdas/etw/root @@ -0,0 +1,10 @@ +/*
+ * fake "root" for validating the local PMNS subtree
+ */
+
+#include <stdpmid>
+
+root { etw }
+
+#include "pmns"
+
diff --git a/src/pmdas/etw/tdhconsume.c b/src/pmdas/etw/tdhconsume.c new file mode 100644 index 0000000..6ee3224 --- /dev/null +++ b/src/pmdas/etw/tdhconsume.c @@ -0,0 +1,923 @@ +/* + * Event Trace Consumer for Windows events on the current platform. + * + * Copyright (c) 2011, Nathan Scott. All Rights Reserved. + * + * 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 <pmapi.h> +#include <impl.h> +#include <tdh.h> +#include <tdhmsg.h> +#include <evntrace.h> +#include <inttypes.h> +#include "util.h" + +#define PROPERTY_BUFFER 1024 +#define MAX_SESSIONS 64 +#define PCP_SESSION "PCP Collector Set" + +static int verbose; + +/* Get the event metadata */ +static DWORD +GetEventInformation(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO *pInfoPointer) +{ + PTRACE_EVENT_INFO pInfo = NULL; + DWORD sts = ERROR_SUCCESS; + DWORD size = 0; + + sts = TdhGetEventInformation(pEvent, 0, NULL, pInfo, &size); + if (sts == ERROR_INSUFFICIENT_BUFFER) { + pInfo = (TRACE_EVENT_INFO *)malloc(size); + if (pInfo == NULL) { + fprintf(stderr, + "Failed to allocate memory for event info (size=%lu).\n", size); + sts = ERROR_OUTOFMEMORY; + } else { + /* Retrieve event metadata */ + sts = TdhGetEventInformation(pEvent, 0, NULL, pInfo, &size); + } + } else if (sts != ERROR_SUCCESS && verbose) { + fprintf(stderr, "TdhGetEventInformation failed: %s (%lu)\n", + tdherror(sts), sts); + } + *pInfoPointer = pInfo; + return sts; +} + +static void +PrintMapString(PEVENT_MAP_INFO pMapInfo, PBYTE pData) +{ + BOOL MatchFound = FALSE; + DWORD i; + + if ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_MANIFEST_VALUEMAP) == + EVENTMAP_INFO_FLAG_MANIFEST_VALUEMAP || + ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_VALUEMAP) == + EVENTMAP_INFO_FLAG_WBEM_VALUEMAP && + (pMapInfo->Flag & (~EVENTMAP_INFO_FLAG_WBEM_VALUEMAP)) != + EVENTMAP_INFO_FLAG_WBEM_FLAG)) { + if ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_NO_MAP) == + EVENTMAP_INFO_FLAG_WBEM_NO_MAP) { + printf("%s\n", ((PBYTE)pMapInfo + + pMapInfo->MapEntryArray[*(PULONG)pData].OutputOffset)); + } else { + for (i = 0; i < pMapInfo->EntryCount; i++) { + if (pMapInfo->MapEntryArray[i].Value == *(PULONG)pData) { + printf("%s\n", ((PBYTE)pMapInfo + + pMapInfo->MapEntryArray[i].OutputOffset)); + MatchFound = TRUE; + break; + } + } + + if (MatchFound == FALSE) + printf("%lu\n", *(PULONG)pData); + } + } + else if ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_MANIFEST_BITMAP) == + EVENTMAP_INFO_FLAG_MANIFEST_BITMAP || + (pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_BITMAP) == + EVENTMAP_INFO_FLAG_WBEM_BITMAP || + ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_VALUEMAP) == + EVENTMAP_INFO_FLAG_WBEM_VALUEMAP && + (pMapInfo->Flag & (~EVENTMAP_INFO_FLAG_WBEM_VALUEMAP)) == + EVENTMAP_INFO_FLAG_WBEM_FLAG)) { + if ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_NO_MAP) == + EVENTMAP_INFO_FLAG_WBEM_NO_MAP) { + DWORD BitPosition = 0; + + for (i = 0; i < pMapInfo->EntryCount; i++) { + BitPosition = (1 << i); + if ((*(PULONG)pData & BitPosition) == BitPosition) { + printf("%s%s", (MatchFound) ? " | " : "", + ((PBYTE)pMapInfo + + pMapInfo->MapEntryArray[i].OutputOffset)); + MatchFound = TRUE; + } + } + } else { + for (i = 0; i < pMapInfo->EntryCount; i++) { + if ((pMapInfo->MapEntryArray[i].Value & *(PULONG)pData) == + pMapInfo->MapEntryArray[i].Value) { + printf("%s%s", (MatchFound) ? " | " : "", + ((PBYTE)pMapInfo + + pMapInfo->MapEntryArray[i].OutputOffset)); + MatchFound = TRUE; + } + } + } + + if (MatchFound) { + printf("\n"); + } else { + printf("%lu\n", *(PULONG)pData); + } + } +} + +static DWORD +FormatAndPrintData(PEVENT_RECORD pEvent, USHORT InType, USHORT OutType, + PBYTE pData, DWORD DataSize, PEVENT_MAP_INFO pMapInfo) +{ + DWORD i, sts = ERROR_SUCCESS; + size_t StringLength = 0; + + switch (InType) { + case TDH_INTYPE_UNICODESTRING: + case TDH_INTYPE_COUNTEDSTRING: + case TDH_INTYPE_REVERSEDCOUNTEDSTRING: + case TDH_INTYPE_NONNULLTERMINATEDSTRING: + if (TDH_INTYPE_COUNTEDSTRING == InType) + StringLength = *(PUSHORT)pData; + else if (TDH_INTYPE_REVERSEDCOUNTEDSTRING == InType) + StringLength = MAKEWORD( + HIBYTE((PUSHORT)pData), LOBYTE((PUSHORT)pData)); + else if (TDH_INTYPE_NONNULLTERMINATEDSTRING == InType) + StringLength = DataSize; + else + StringLength = wcslen((LPWSTR)pData); + printf("%.*s\n", StringLength, pData); + break; + + case TDH_INTYPE_ANSISTRING: + case TDH_INTYPE_COUNTEDANSISTRING: + case TDH_INTYPE_REVERSEDCOUNTEDANSISTRING: + case TDH_INTYPE_NONNULLTERMINATEDANSISTRING: + if (TDH_INTYPE_COUNTEDANSISTRING == InType) + StringLength = *(PUSHORT)pData; + else if (TDH_INTYPE_REVERSEDCOUNTEDANSISTRING == InType) + StringLength = MAKEWORD( + HIBYTE((PUSHORT)pData), LOBYTE((PUSHORT)pData)); + else if (TDH_INTYPE_NONNULLTERMINATEDANSISTRING == InType) + StringLength = DataSize; + else + StringLength = strlen((LPSTR)pData); + printf("%.*s\n", StringLength, pData); + break; + + case TDH_INTYPE_INT8: + printf("%hd\n", *(PCHAR)pData); + break; + + case TDH_INTYPE_UINT8: + if (TDH_OUTTYPE_HEXINT8 == OutType) + printf("0x%x\n", *(PBYTE)pData); + else + printf("%hu\n", *(PBYTE)pData); + break; + + case TDH_INTYPE_INT16: + printf("%hd\n", *(PSHORT)pData); + break; + + case TDH_INTYPE_UINT16: + if (TDH_OUTTYPE_HEXINT16 == OutType) + printf("0x%x\n", *(PUSHORT)pData); + else if (TDH_OUTTYPE_PORT == OutType) + printf("%hu\n", ntohs(*(PUSHORT)pData)); + else + printf("%hu\n", *(PUSHORT)pData); + break; + + case TDH_INTYPE_INT32: + if (TDH_OUTTYPE_HRESULT == OutType) + printf("0x%lx\n", *(PLONG)pData); + else + printf("%ld\n", *(PLONG)pData); + break; + + case TDH_INTYPE_UINT32: + if (TDH_OUTTYPE_HRESULT == OutType || + TDH_OUTTYPE_WIN32ERROR == OutType || + TDH_OUTTYPE_NTSTATUS == OutType || + TDH_OUTTYPE_HEXINT32 == OutType) + printf("0x%lx\n", *(PULONG)pData); + else if (TDH_OUTTYPE_IPV4 == OutType) + printf("%ld.%ld.%ld.%ld\n", (*(PLONG)pData >> 0) & 0xff, + (*(PLONG)pData >> 8) & 0xff, + (*(PLONG)pData >> 16) & 0xff, + (*(PLONG)pData >> 24) & 0xff); + else if (pMapInfo) + PrintMapString(pMapInfo, pData); + else + printf("%lu\n", *(PULONG)pData); + break; + + case TDH_INTYPE_INT64: + printf("%I64d\n", *(PLONGLONG)pData); + break; + + case TDH_INTYPE_UINT64: + if (TDH_OUTTYPE_HEXINT64 == OutType) + printf("0x%I64x\n", *(PULONGLONG)pData); + else + printf("%I64u\n", *(PULONGLONG)pData); + break; + + case TDH_INTYPE_FLOAT: + printf("%f\n", *(PFLOAT)pData); + break; + + case TDH_INTYPE_DOUBLE: + printf("%f\n", *(double*)pData); + break; + + case TDH_INTYPE_BOOLEAN: + printf("%s\n", ((PBOOL)pData == 0) ? "false" : "true"); + break; + + case TDH_INTYPE_BINARY: + if (TDH_OUTTYPE_IPV6 == OutType) + break; + else { + for (i = 0; i < DataSize; i++) + printf("%.2x", pData[i]); + printf("\n"); + } + break; + + case TDH_INTYPE_GUID: + printf("%s\n", strguid((GUID *)pData)); + break; + + case TDH_INTYPE_POINTER: + case TDH_INTYPE_SIZET: + if (EVENT_HEADER_FLAG_32_BIT_HEADER == + (pEvent->EventHeader.Flags & EVENT_HEADER_FLAG_32_BIT_HEADER)) + printf("0x%I32x\n", *(PULONG)pData); + else + printf("0x%I64x\n", *(PULONGLONG)pData); + + case TDH_INTYPE_FILETIME: + break; + + case TDH_INTYPE_SYSTEMTIME: + break; + + case TDH_INTYPE_SID: + break; + + case TDH_INTYPE_HEXINT32: + printf("0x%I32x\n", *(PULONG)pData); + break; + + case TDH_INTYPE_HEXINT64: + printf("0x%I64x\n", *(PULONGLONG)pData); + break; + + case TDH_INTYPE_UNICODECHAR: + printf("%c\n", *(PWCHAR)pData); + break; + + case TDH_INTYPE_ANSICHAR: + printf("%C\n", *(PCHAR)pData); + break; + + case TDH_INTYPE_WBEMSID: + break; + + default: + sts = ERROR_NOT_FOUND; + } + + return sts; +} + +/* + * Get the size of the array. + * For MOF-based events, the size is specified in the declaration or using + * the MAX qualifier. For manifest-based events, the property can specify + * the size of the array using the count attribute. + * The count attribue can specify the size directly or specify the name + * of another property in the event data that contains the size. + */ +static void +GetArraySize(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, + USHORT i, PUSHORT ArraySize) +{ + PROPERTY_DATA_DESCRIPTOR DataDescriptor; + DWORD size = 0; + + if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyParamCount) == + PropertyParamCount) { + DWORD cnt = 0; /* expecting count to be defined as uint16 or uint32 */ + DWORD j = pInfo->EventPropertyInfoArray[i].countPropertyIndex; + + ZeroMemory(&DataDescriptor, sizeof(PROPERTY_DATA_DESCRIPTOR)); + DataDescriptor.PropertyName = + (ULONGLONG)((PBYTE)(pInfo) + + pInfo->EventPropertyInfoArray[j].NameOffset); + DataDescriptor.ArrayIndex = ULONG_MAX; + TdhGetPropertySize(pEvent, 0, NULL, 1, &DataDescriptor, &size); + TdhGetProperty(pEvent, 0, NULL, 1, &DataDescriptor, size, (PBYTE)&cnt); + *ArraySize = (USHORT)cnt; + } else { + *ArraySize = pInfo->EventPropertyInfoArray[i].count; + } +} + +/* + * Mapped string values defined in a manifest will contain a trailing space + * in the EVENT_MAP_ENTRY structure. Replace the trailing space with a null- + * terminating character, so that bit mapped strings are correctly formatted. + */ +static void +RemoveTrailingSpace(PEVENT_MAP_INFO pMapInfo) +{ + SIZE_T ByteLength = 0; + DWORD i; + + for (i = 0; i < pMapInfo->EntryCount; i++) { + ByteLength = (wcslen((LPWSTR)((PBYTE)pMapInfo + + pMapInfo->MapEntryArray[i].OutputOffset)) - 1) * 2; + *((LPWSTR)((PBYTE)pMapInfo + + (pMapInfo->MapEntryArray[i].OutputOffset + + ByteLength))) = L'\0'; + } +} + +/* + * Both MOF-based events and manifest-based events can specify name/value maps. + * The map values can be integer values or bit values. + * If the property specifies a value map, get the map. + */ +static DWORD +GetMapInfo(PEVENT_RECORD pEvent, LPWSTR pMapName, DWORD DecodingSource, + PEVENT_MAP_INFO pMapInfo) +{ + DWORD sts = ERROR_SUCCESS; + DWORD size = 0; + + /* Retrieve required buffer size for map info */ + sts = TdhGetEventMapInformation(pEvent, pMapName, pMapInfo, &size); + if (sts == ERROR_INSUFFICIENT_BUFFER) { + pMapInfo = (PEVENT_MAP_INFO)malloc(size); + if (pMapInfo == NULL) { + fprintf(stderr, "Failed to allocate map info memory (size=%lu).\n", + size); + return ERROR_OUTOFMEMORY; + } + + /* Retrieve the map info */ + sts = TdhGetEventMapInformation(pEvent, pMapName, pMapInfo, &size); + } + + if (sts == ERROR_SUCCESS) { + if (DecodingSourceXMLFile == DecodingSource) + RemoveTrailingSpace(pMapInfo); + } else if (sts == ERROR_NOT_FOUND) { + sts = ERROR_SUCCESS; + } else { + fprintf(stderr, "TdhGetEventMapInformation failed: %s (%ld)\n", + tdherror(sts), sts); + } + return sts; +} + +static DWORD +PrintProperties(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, + USHORT i, LPWSTR pStructureName, USHORT StructIndex) +{ + DWORD sts = ERROR_SUCCESS; + DWORD LastMember = 0; + USHORT ArraySize = 0; + PEVENT_MAP_INFO pMapInfo = NULL; + PROPERTY_DATA_DESCRIPTOR DataDescriptors[2]; + ULONG DescriptorsCount = 0; + USHORT k, j; + + static DWORD size; + static PBYTE pData; + + if (pData == NULL) { + if ((pData = malloc(PROPERTY_BUFFER)) != NULL) + size = PROPERTY_BUFFER; + } + + /* Get the size of the array (if the property is an array) */ + GetArraySize(pEvent, pInfo, i, &ArraySize); + + for (k = 0; k < ArraySize; k++) { + wprintf(L"%*s%s: ", (pStructureName) ? 4 : 0, L"", (LPWSTR) + ((PBYTE)(pInfo) + pInfo->EventPropertyInfoArray[i].NameOffset)); + + /* If the property is a structure, print the members of the structure */ + if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyStruct) == PropertyStruct) { + printf("\n"); + + LastMember = + pInfo->EventPropertyInfoArray[i].structType.StructStartIndex + + pInfo->EventPropertyInfoArray[i].structType.NumOfStructMembers; + + for (j = pInfo->EventPropertyInfoArray[i].structType.StructStartIndex; j < LastMember; j++) { + sts = PrintProperties(pEvent, pInfo, j, +(LPWSTR)((PBYTE)(pInfo) + pInfo->EventPropertyInfoArray[i].NameOffset), k); + if (sts != ERROR_SUCCESS) { + fprintf(stderr, + "Printing the members of the structure failed\n"); + goto cleanup; + } + } + } else { + ZeroMemory(&DataDescriptors, sizeof(DataDescriptors)); + + /* + * To retrieve a member of a structure, you need to specify + * an array of descriptors. The first descriptor in the array + * identifies the name of the structure and the second + * descriptor defines the member of the structure whose data + * you want to retrieve. + */ + if (pStructureName) { + DataDescriptors[0].PropertyName = (ULONGLONG)pStructureName; + DataDescriptors[0].ArrayIndex = StructIndex; + DataDescriptors[1].PropertyName = (ULONGLONG) + ((PBYTE)(pInfo) + pInfo->EventPropertyInfoArray[i].NameOffset); + DataDescriptors[1].ArrayIndex = k; + DescriptorsCount = 2; + } else { + DataDescriptors[0].PropertyName = (ULONGLONG) + ((PBYTE)(pInfo) + pInfo->EventPropertyInfoArray[i].NameOffset); + DataDescriptors[0].ArrayIndex = k; + DescriptorsCount = 1; + } + + /* + * TDH API does not support IPv6 addresses. + * If the output type is TDH_OUTTYPE_IPV6, you will be unable + * to consume the rest of the event. + * If you try to consume the remainder of the event, you will + * get ERROR_EVT_INVALID_EVENT_DATA. + */ + if (TDH_INTYPE_BINARY == + pInfo->EventPropertyInfoArray[i].nonStructType.InType && + TDH_OUTTYPE_IPV6 == + pInfo->EventPropertyInfoArray[i].nonStructType.OutType) { + fprintf(stderr, "Event contains an IPv6 address. Skipping.\n"); + sts = ERROR_EVT_INVALID_EVENT_DATA; + break; + } else { +retry: + sts = TdhGetProperty(pEvent, 0, NULL, + DescriptorsCount, &DataDescriptors[0], size, pData); + if (sts == ERROR_INSUFFICIENT_BUFFER) { + /* TdhGetPropertySize failing on Win2008, so do this: */ + pData = realloc(pData, size *= 2); + goto retry; + } else if (sts != ERROR_SUCCESS) { + fprintf(stderr, "TdhGetProperty failed: %s (%ld)\n", + tdherror(sts), sts); + goto cleanup; + } + + /* + * Get the name/value map if the property specifies a value map. + */ + sts = GetMapInfo(pEvent, (PWCHAR) ((PBYTE)(pInfo) + + pInfo->EventPropertyInfoArray[i].nonStructType.MapNameOffset), + pInfo->DecodingSource, + pMapInfo); + if (sts != ERROR_SUCCESS) { + fprintf(stderr, "GetMapInfo failed\n"); + goto cleanup; + } + + sts = FormatAndPrintData(pEvent, + pInfo->EventPropertyInfoArray[i].nonStructType.InType, + pInfo->EventPropertyInfoArray[i].nonStructType.OutType, + pData, size, pMapInfo); + if (sts != ERROR_SUCCESS) { + fprintf(stderr, "FormatAndPrintData failed\n"); + goto cleanup; + } + + if (pMapInfo) { + free(pMapInfo); + pMapInfo = NULL; + } + } + } + } + +cleanup: + + if (pMapInfo) { + free(pMapInfo); + pMapInfo = NULL; + } + + return sts; +} + +void +PrintHeader(PEVENT_RECORD pEvent) +{ + /* Note: EventHeader.ProcessId is defined as ULONG */ + printf("Event HEADER (size=%u) flags=%s type=%s\npid=%lu tid=%ld eid=%u\n", + pEvent->EventHeader.Size, + eventHeaderFlags(pEvent->EventHeader.Flags), + eventPropertyFlags(pEvent->EventHeader.EventProperty), + pEvent->EventHeader.ThreadId, pEvent->EventHeader.ProcessId, + pEvent->EventHeader.EventDescriptor.Id); + if (pEvent->EventHeader.Flags & + (EVENT_HEADER_FLAG_PRIVATE_SESSION|EVENT_HEADER_FLAG_NO_CPUTIME)) { + printf("Time processor=%"PRIu64"\n", pEvent->EventHeader.ProcessorTime); + } else { + printf("Time: sys=%lu usr=%lu\n", + pEvent->EventHeader.KernelTime, pEvent->EventHeader.UserTime); + } + printf("Event PROVIDER %s\n", strguid(&pEvent->EventHeader.ProviderId)); + printf("Event ACTIVITY %s\n", strguid(&pEvent->EventHeader.ActivityId)); +} + +void +PrintTimestamp(PEVENT_RECORD pEvent) +{ + ULONGLONG TimeStamp = 0; + ULONGLONG Nanoseconds = 0; + SYSTEMTIME st; + SYSTEMTIME stLocal; + FILETIME ft; + + /* Print the time stamp for when the event occurred */ + ft.dwHighDateTime = pEvent->EventHeader.TimeStamp.HighPart; + ft.dwLowDateTime = pEvent->EventHeader.TimeStamp.LowPart; + + FileTimeToSystemTime(&ft, &st); + SystemTimeToTzSpecificLocalTime(NULL, &st, &stLocal); + TimeStamp = pEvent->EventHeader.TimeStamp.QuadPart; + Nanoseconds = (TimeStamp % 10000000) * 100; + + printf("Event TIMESTAMP: %02d/%02d/%02d %02d:%02d:%02d.%I64u\n", + stLocal.wMonth, stLocal.wDay, stLocal.wYear, stLocal.wHour, + stLocal.wMinute, stLocal.wSecond, Nanoseconds); +} + +void +PrintEventInfo(PTRACE_EVENT_INFO pInfo) +{ + if (DecodingSourceWbem == pInfo->DecodingSource) + printf("EventInfo: MOF class event\n"); + else if (DecodingSourceXMLFile == pInfo->DecodingSource) + printf("EventInfo: XML manifest event\n"); + else if (DecodingSourceWPP == pInfo->DecodingSource) + printf("EventInfo: WPP event\n"); + + printf("Event GUID: %s\n", strguid(&pInfo->EventGuid)); + + if (pInfo->ProviderNameOffset > 0) + wprintf(L"Provider name: %s\n", + (LPWSTR)((PBYTE)(pInfo) + pInfo->ProviderNameOffset)); + + if (DecodingSourceXMLFile == pInfo->DecodingSource) + wprintf(L"Event ID: %hu\n", pInfo->EventDescriptor.Id); + + wprintf(L"Version: %d\n", pInfo->EventDescriptor.Version); + + if (pInfo->ChannelNameOffset > 0) + wprintf(L"Channel name: %s\n", + (LPWSTR)((PBYTE)(pInfo) + pInfo->ChannelNameOffset)); + + if (pInfo->LevelNameOffset > 0) + wprintf(L"Level name: %s\n", + (LPWSTR)((PBYTE)(pInfo) + pInfo->LevelNameOffset)); + else + wprintf(L"Level: %hu\n", pInfo->EventDescriptor.Level); + + if (DecodingSourceXMLFile == pInfo->DecodingSource) { + if (pInfo->OpcodeNameOffset > 0) + wprintf(L"Opcode name: %s\n", + (LPWSTR)((PBYTE)(pInfo) + pInfo->OpcodeNameOffset)); + } else + wprintf(L"Type: %hu\n", pInfo->EventDescriptor.Opcode); + + if (DecodingSourceXMLFile == pInfo->DecodingSource) { + if (pInfo->TaskNameOffset > 0) + wprintf(L"Task name: %s\n", + (LPWSTR)((PBYTE)(pInfo) + pInfo->TaskNameOffset)); + } else + wprintf(L"Task: %hu\n", pInfo->EventDescriptor.Task); + + wprintf(L"Keyword mask: 0x%x\n", pInfo->EventDescriptor.Keyword); + if (pInfo->KeywordsNameOffset) { + LPWSTR pKeyword = (LPWSTR)((PBYTE)(pInfo) + pInfo->KeywordsNameOffset); + + for (; *pKeyword != 0; pKeyword += (wcslen(pKeyword) + 1)) + wprintf(L" Keyword name: %s\n", pKeyword); + } + + if (pInfo->EventMessageOffset > 0) + wprintf(L"Event message: %s\n", + (LPWSTR)((PBYTE)(pInfo) + pInfo->EventMessageOffset)); + + if (pInfo->ActivityIDNameOffset > 0) + wprintf(L"Activity ID name: %s\n", + (LPWSTR)((PBYTE)(pInfo) + pInfo->ActivityIDNameOffset)); + + if (pInfo->RelatedActivityIDNameOffset > 0) + wprintf(L"Related activity ID name: %s\n", + (LPWSTR)((PBYTE)(pInfo) + pInfo->RelatedActivityIDNameOffset)); +} + +/* Callback that receives the events */ +VOID WINAPI +ProcessEvent(PEVENT_RECORD pEvent) +{ + DWORD sts = ERROR_SUCCESS; + PTRACE_EVENT_INFO pInfo = NULL; + USHORT i; + + PrintHeader(pEvent); + PrintTimestamp(pEvent); + + /* + * Process the event. + * The pEvent->UserData member is a pointer to the event specific data, + * if any exists. + */ + sts = GetEventInformation(pEvent, &pInfo); + if (sts != ERROR_SUCCESS) + goto cleanup; + + PrintEventInfo(pInfo); + + if (DecodingSourceWPP == pInfo->DecodingSource) + /* Not handling the WPP case, unless just an inline string */ + if (!(pEvent->EventHeader.Flags & EVENT_HEADER_FLAG_STRING_ONLY)) + goto cleanup; + + /* + * Print the event data for all the top-level properties. + * Metadata for all the top-level properties comes before structure + * member properties in the property information array. + * If the EVENT_HEADER_FLAG_STRING_ONLY flag is set, the event data + * is a null-terminated string, so just print it. + */ + if (EVENT_HEADER_FLAG_STRING_ONLY == + (pEvent->EventHeader.Flags & EVENT_HEADER_FLAG_STRING_ONLY)) { + printf("Embedded: %s\n", (char *)pEvent->UserData); + } else { + for (i = 0; i < pInfo->TopLevelPropertyCount; i++) { + sts = PrintProperties(pEvent, pInfo, i, NULL, 0); + if (sts != ERROR_SUCCESS) { + fprintf(stderr, + "Printing top level properties failed, property %u.\n", + i); + } + } + } + +cleanup: + fflush(stdout); + if (pInfo) + free(pInfo); +} + +static struct { + EVENT_TRACE_PROPERTIES *properties; + TRACEHANDLE session; + LPTSTR name; +} sessions[MAX_SESSIONS]; +static int sCount; + +static void +stopSession(TRACEHANDLE session, LPTSTR name, + PEVENT_TRACE_PROPERTIES properties) +{ + if (session != INVALID_PROCESSTRACE_HANDLE) { + ULONG sts = ControlTrace(session, name, properties, + EVENT_TRACE_CONTROL_STOP); + if (sts != ERROR_SUCCESS) + fprintf(stderr, "ControlTrace failed (%s): %s\n", name, + tdherror(sts)); + else + fprintf(stderr, "Stopped %s event tracing session\n", name); + } +} + +static void __attribute__((constructor)) initTracing() +{ + int i; + + for (i = 0; i < sizeof(sessions) / sizeof(sessions[0]); i++) + sessions[i].session = INVALID_PROCESSTRACE_HANDLE; +} + +static void __attribute__((destructor)) stopTracing() +{ + int i; + + for (i = 0; i < sCount; i++) { + sessions[i].properties->EnableFlags = 0; + stopSession(sessions[i].session, sessions[i].name, sessions[i].properties); + } +} + +void +enableEventTrace(LPTSTR name, ULONG namelen, const GUID *guid, + ULONG enableFlags, BOOL useGlobalSequence) +{ + TRACEHANDLE session; + EVENT_TRACE_PROPERTIES *properties; + ULONG size, sts = ERROR_SUCCESS; + int retryStartTrace = 0; + + size = sizeof(EVENT_TRACE_PROPERTIES) + namelen; + properties = malloc(size); + if (properties == NULL) { + fprintf(stderr, "Insufficient memory: %lu bytes\n", size); + exit(1); + } + + sessions[sCount].properties = properties; + sessions[sCount].session = session; + sessions[sCount].name = name; + sCount++; + +retrySession: + ZeroMemory(properties, size); + properties->Wnode.BufferSize = size; + properties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; + properties->Wnode.ClientContext = 1; + properties->Wnode.Guid = *guid; + properties->EnableFlags = enableFlags; + properties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE; + if (useGlobalSequence == TRUE) + properties->LogFileMode |= EVENT_TRACE_USE_GLOBAL_SEQUENCE; + properties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + + sts = StartTrace(&session, name, properties); + if (sts != ERROR_SUCCESS) { + if (retryStartTrace == 1) { + fprintf(stderr, "Cannot start %s session (in use)\n", name); + } else if (sts == ERROR_ALREADY_EXISTS) { + fprintf(stderr, "%s session is in use - retry ... (flags=%lx)\n", + name, enableFlags); + stopTracing(); + retryStartTrace = 1; + goto retrySession; + } + else { + fprintf(stderr, "StartTrace: %s\n", tdherror(sts)); + } + exit(1); + } +} + +ULONG bufferCount, buffersRead, bufferTotal, bufferBytes, eventsLost, sysCount; + +ULONG WINAPI +BufferCallback(PEVENT_TRACE_LOGFILE mode) +{ + buffersRead += mode->BuffersRead; + bufferTotal += mode->BufferSize; + bufferBytes += mode->Filled; + eventsLost += mode->EventsLost; + sysCount += mode->IsKernelTrace; + bufferCount++; + printf("Event BUFFER (size=%lu) all reads=%lu bytes=%lu lost=%lu sys=%lu\n", + mode->Filled, buffersRead, bufferBytes, eventsLost, sysCount); + return TRUE; +} + +LPGUID +LookupGuidInBuffer(LPTSTR name, PPROVIDER_ENUMERATION_INFO buffer) +{ + PTRACE_PROVIDER_INFO traceProviderInfo; + PWCHAR stringPointer; + char s[1024]; + LPGUID guidPointer; + PBYTE bufferPointer = (PBYTE)buffer; + ULONG i; + + for (i = 0; i < buffer->NumberOfProviders; i++) { + traceProviderInfo = &buffer->TraceProviderInfoArray[i]; + if (traceProviderInfo->ProviderNameOffset == 0) + continue; + guidPointer = &traceProviderInfo->ProviderGuid; + stringPointer = (PWCHAR) + (bufferPointer + traceProviderInfo->ProviderNameOffset); + WideCharToMultiByte(CP_ACP, 0, stringPointer, -1, s, 1024, NULL, NULL); + if (strcmp(s, name) == 0) + return guidPointer; + } + return NULL; +} + +ULONG +ProviderGuid(LPTSTR name, LPGUID guidPointer) +{ + PROVIDER_ENUMERATION_INFO providerEnumerationInfo; + PPROVIDER_ENUMERATION_INFO buffer; + ULONG size, sts; + + buffer = &providerEnumerationInfo; + size = sizeof(providerEnumerationInfo); + sts = TdhEnumerateProviders(buffer, &size); + do { + if (sts == ERROR_INSUFFICIENT_BUFFER) { + if (buffer != &providerEnumerationInfo) + BufferFree(buffer); + buffer = (PPROVIDER_ENUMERATION_INFO)BufferAllocate(size); + if (!buffer) + return ERROR_NOT_ENOUGH_MEMORY; + sts = TdhEnumerateProviders(buffer, &size); + } + else if (sts == ERROR_SUCCESS) { + guidPointer = LookupGuidInBuffer(name, buffer); + break; + } + else { + fprintf(stderr, "TdhEnumerateProviders failed: %s (=%lu)\n", + tdherror(sts), sts); + break; + } + } while (1); + + if (buffer != &providerEnumerationInfo) + BufferFree(buffer); + + return sts; +} + +TRACEHANDLE +openTraceHandle(LPTSTR name) +{ + TRACEHANDLE handle; + EVENT_TRACE_LOGFILE traceMode; + + ZeroMemory(&traceMode, sizeof(EVENT_TRACE_LOGFILE)); + traceMode.LoggerName = name; + traceMode.BufferCallback = (PEVENT_TRACE_BUFFER_CALLBACK)BufferCallback; + traceMode.EventRecordCallback = (PEVENT_RECORD_CALLBACK)ProcessEvent; + traceMode.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME; + + handle = OpenTrace(&traceMode); + if (handle == INVALID_PROCESSTRACE_HANDLE) { + ULONG sts = GetLastError(); + fprintf(stderr, "OpenTrace: %s (%lu)\n", tdherror(sts), sts); + CloseTrace(handle); + exit(1); + } + return handle; +} + +static char *options = "k:v?"; +static char usage[] = + "Usage: %s [options] tracename\n\n" + "Options:\n" + " -k subsys kernel subsystem to trace\n" + " -g use global sequence numbers\n" + " -v verbose diagnostics (errors)\n"; + +int +main(int argc, LPTSTR *argv) +{ + BOOL useGlobalSequence = FALSE; + TRACEHANDLE session; + ULONG sts, sysFlags = 0; + int c; + + while ((c = getopt(argc, argv, options)) != EOF) { + switch (c) { + case 'g': + useGlobalSequence = TRUE; + break; + case 'k': + sysFlags |= kernelTraceFlag(optarg); + break; + case 'v': + verbose++; + break; + case '?': + default: + fprintf(stderr, usage, argv[0]); + exit(1); + } + } + + if (sysFlags) { + enableEventTrace(KERNEL_LOGGER_NAME, sizeof(KERNEL_LOGGER_NAME), + &SystemTraceControlGuid, sysFlags, useGlobalSequence); + session = openTraceHandle(KERNEL_LOGGER_NAME); + } else { + session = openTraceHandle(PCP_SESSION); + } + + sts = ProcessTrace(&session, 1, NULL, NULL); + if (sts == ERROR_CANCELLED) + sts = ERROR_SUCCESS; + if (sts != ERROR_SUCCESS) + fprintf(stderr, "ProcessTrace: %s (%lu)\n", tdherror(sts), sts); + return sts; +} diff --git a/src/pmdas/etw/tdhlist.c b/src/pmdas/etw/tdhlist.c new file mode 100644 index 0000000..e21c024 --- /dev/null +++ b/src/pmdas/etw/tdhlist.c @@ -0,0 +1,267 @@ +/* + * List Event Traces Providers or sessions for Windows events. + * + * Copyright (c) 2011, Nathan Scott. All Rights Reserved. + * + * 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 <inttypes.h> +#include <windows.h> +#include <wmistr.h> +#include <stdio.h> +#include <errno.h> +#include <tdh.h> +#include "util.h" + +#define MAX_SESSIONS (ULONG)64 +#define MAX_SESSION_NAME_LEN 1024 +#define MAX_LOGFILE_PATH_LEN 1024 + +void +PrintFieldInfo(PBYTE buffer, + PPROVIDER_FIELD_INFO fieldInfo, int id, EVENT_FIELD_TYPE eventFieldType) +{ + PWCHAR stringBuffer; + + printf("\tField %u\n", id); + switch (eventFieldType) { + case EventKeywordInformation: + printf("\t\tType: KeywordInformation\n"); + break; + case EventLevelInformation: + printf("\t\tType: LevelInformation\n"); + break; + case EventChannelInformation: + printf("\t\tType: ChannelInformation\n"); + break; + case EventTaskInformation: + printf("\t\tType: TaskInformation\n"); + break; + case EventOpcodeInformation: + printf("\t\tType: OpcodeInformation\n"); + break; + default: + break; + } + printf("\t\tValue: %" PRIu64 "\n", fieldInfo->Value); + if (fieldInfo->NameOffset) { + stringBuffer = (PWCHAR)(buffer + fieldInfo->NameOffset); + printf("\t\tField: %ls\n", stringBuffer); + } + if (fieldInfo->DescriptionOffset) { + stringBuffer = (PWCHAR)(buffer + fieldInfo->DescriptionOffset); + printf("\t\tDescription: %ls\n", stringBuffer); + } +} + +void +PrintFieldElements(PPROVIDER_FIELD_INFOARRAY buffer, EVENT_FIELD_TYPE eventFieldType) +{ + PPROVIDER_FIELD_INFO traceFieldInfo; + ULONG i; + + for (i = 0; i < buffer->NumberOfElements; i++) { + traceFieldInfo = &buffer->FieldInfoArray[i]; + PrintFieldInfo((PBYTE)buffer, traceFieldInfo, i, eventFieldType); + } +} + +void +EnumerateProviderFieldInformation(LPGUID guidPointer, EVENT_FIELD_TYPE eventFieldType) +{ + PPROVIDER_FIELD_INFOARRAY buffer = NULL; + ULONG sts, size = 0; + + sts = TdhEnumerateProviderFieldInformation(guidPointer, + eventFieldType, buffer, &size); + do { + if (sts == ERROR_INSUFFICIENT_BUFFER) { + BufferFree(buffer); + buffer = (PPROVIDER_FIELD_INFOARRAY)BufferAllocate(size); + if (!buffer) + return; + sts = TdhEnumerateProviderFieldInformation(guidPointer, + eventFieldType, buffer, &size); + } + else if (sts == ERROR_SUCCESS) { + PrintFieldElements(buffer, eventFieldType); + break; + } + else if (sts == ERROR_NOT_FOUND) { + break; + } + else { + fprintf(stderr, "TdhEnumerateProviderFieldInformation: %s (%lu)\n", + tdherror(sts), sts); + break; + } + } while (1); + + BufferFree(buffer); +} + +void +PrintTraceProviderInfo(PBYTE buffer, PTRACE_PROVIDER_INFO traceProviderInfo) +{ + LPGUID guidPointer = &traceProviderInfo->ProviderGuid; + PWCHAR stringBuffer; + ULONG i; + + if (traceProviderInfo->ProviderNameOffset) { + stringBuffer = (PWCHAR)(buffer + traceProviderInfo->ProviderNameOffset); + printf("Name: %ls\n", stringBuffer); + } + + printf("Guid: %s\n", strguid(guidPointer)); + printf("SchemaSource: %ld (%s)\n", traceProviderInfo->SchemaSource, + traceProviderInfo->SchemaSource==0 ? "XML manifest" : "WMI MOF class"); + + for (i = EventKeywordInformation; i <= EventChannelInformation; i++) { + EnumerateProviderFieldInformation(guidPointer, (EVENT_FIELD_TYPE)i); + } +} + +void +PrintProviderEnumeration(PPROVIDER_ENUMERATION_INFO buffer) +{ + PTRACE_PROVIDER_INFO traceProviderInfo; + ULONG i; + + for (i = 0; i < buffer->NumberOfProviders; i++) { + traceProviderInfo = &buffer->TraceProviderInfoArray[i]; + PrintTraceProviderInfo((PBYTE)buffer, traceProviderInfo); + } +} + +ULONG +EnumerateProviders(void) +{ + PROVIDER_ENUMERATION_INFO providerEnumerationInfo; + PPROVIDER_ENUMERATION_INFO buffer; + ULONG size, sts; + + buffer = &providerEnumerationInfo; + size = sizeof(providerEnumerationInfo); + sts = TdhEnumerateProviders(buffer, &size); + do { + if (sts == ERROR_INSUFFICIENT_BUFFER) { + if (buffer != &providerEnumerationInfo) + BufferFree(buffer); + buffer = (PPROVIDER_ENUMERATION_INFO)BufferAllocate(size); + if (!buffer) + return ERROR_NOT_ENOUGH_MEMORY; + sts = TdhEnumerateProviders(buffer, &size); + } + else if (sts == ERROR_SUCCESS) { + PrintProviderEnumeration(buffer); + break; + } + else { + fprintf(stderr, "TdhEnumerateProviders failed: %s (=%lu)\n", + tdherror(sts), sts); + break; + } + } while (1); + + if (buffer != &providerEnumerationInfo) + BufferFree(buffer); + + return sts; +} + +ULONG +EnumerateSessions(void) +{ + PEVENT_TRACE_PROPERTIES pSessions[MAX_SESSIONS]; + PEVENT_TRACE_PROPERTIES pBuffer = NULL; + ULONG PropertiesSize = 0; + ULONG SessionCount = 0; + ULONG BufferSize = 0; + ULONG i, sts = ERROR_SUCCESS; + + PropertiesSize = sizeof(EVENT_TRACE_PROPERTIES) + + (MAX_SESSION_NAME_LEN * sizeof(WCHAR)) + + (MAX_LOGFILE_PATH_LEN * sizeof(WCHAR)); + BufferSize = PropertiesSize * MAX_SESSIONS; + + pBuffer = (PEVENT_TRACE_PROPERTIES) malloc(BufferSize); + if (pBuffer) { + ZeroMemory(pBuffer, BufferSize); + for (i = 0; i < MAX_SESSIONS; i++) { + pSessions[i] = (EVENT_TRACE_PROPERTIES *)((BYTE *)pBuffer + + (i * PropertiesSize)); + pSessions[i]->Wnode.BufferSize = PropertiesSize; + pSessions[i]->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + pSessions[i]->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + + (MAX_SESSION_NAME_LEN * sizeof(WCHAR)); + } + } else { + fprintf(stderr, "Error allocating memory for properties.\n"); + return ENOMEM; + } + + sts = QueryAllTraces(pSessions, MAX_SESSIONS, &SessionCount); + if (sts == ERROR_SUCCESS || sts == ERROR_MORE_DATA) { + printf("Requested session count, %ld. Actual session count, %ld.\n\n", + MAX_SESSIONS, SessionCount); + for (i = 0; i < SessionCount; i++) { + LPCSTR l, f; + l = (LPCSTR)((char *)pSessions[i] + pSessions[i]->LoggerNameOffset); + f = (LPCSTR)((char*)pSessions[i] + pSessions[i]->LogFileNameOffset); + printf("Session GUID: %s\nSession ID: %"PRIu64"\n", + strguid(&pSessions[i]->Wnode.Guid), + pSessions[i]->Wnode.HistoricalContext); + if (pSessions[i]->LogFileNameOffset == 0) + printf("Realtime session name: %s\n", l); + else + printf("Log session name: %s\nLog file: %s\n", l, f); + if (memcmp(&SystemTraceControlGuid, + &pSessions[i]->Wnode.Guid, sizeof(GUID)) == 0) { + printf("Enable Flags: "); + dumpKernelTraceFlags(stdout, "", ","); + printf("\n"); + } + printf("flush timer: %ld\n" + "min buffers: %ld\n" + "max buffers: %ld\n" + "buffers: %ld\n" + "buffers written: %ld\n" + "buffers lost: %ld\n" + "events lost: %ld\n\n", + pSessions[i]->FlushTimer, + pSessions[i]->MinimumBuffers, + pSessions[i]->MaximumBuffers, + pSessions[i]->NumberOfBuffers, + pSessions[i]->BuffersWritten, + pSessions[i]->LogBuffersLost, + pSessions[i]->EventsLost); + } + } else { + fprintf(stderr, "Error calling QueryAllTraces: %s (%ld)\n", + tdherror(sts), sts); + } + + free(pBuffer); + return sts; +} + +int +main(int argc, char **argv) +{ + ULONG sts; + + if (argc > 1 && strcmp(argv[1], "-s") == 0) + sts = EnumerateSessions(); + else + sts = EnumerateProviders(); + return sts; +} diff --git a/src/pmdas/etw/util.c b/src/pmdas/etw/util.c new file mode 100644 index 0000000..19869e6 --- /dev/null +++ b/src/pmdas/etw/util.c @@ -0,0 +1,190 @@ +/* + * Event Trace for Windows utility routines. + * + * Copyright (c) 2011, Nathan Scott. All Rights Reserved. + * + * 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. + */ + +#define INITGUID +#include <pmapi.h> +#include <impl.h> +#include <tdh.h> +#include <evntrace.h> +#include "util.h" + +struct { + ULONG flag; + char *name; +} kernelFlags[] = { + { EVENT_TRACE_FLAG_PROCESS, "process" }, + { EVENT_TRACE_FLAG_THREAD, "thread" }, + { EVENT_TRACE_FLAG_IMAGE_LOAD, "image_load" }, + { EVENT_TRACE_FLAG_DISK_IO, "disk_io" }, + { EVENT_TRACE_FLAG_DISK_FILE_IO, "disk_file_io" }, + { EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS, "memory_page_faults" }, + { EVENT_TRACE_FLAG_MEMORY_HARD_FAULTS, "memory_hard_faults" }, + { EVENT_TRACE_FLAG_NETWORK_TCPIP, "network_tcpip" }, + { EVENT_TRACE_FLAG_REGISTRY, "registry" }, + { EVENT_TRACE_FLAG_DBGPRINT, "dbgprint" }, + { EVENT_TRACE_FLAG_PROCESS_COUNTERS, "process_counters" }, + { EVENT_TRACE_FLAG_CSWITCH, "cswitch" }, + { EVENT_TRACE_FLAG_DPC, "dpc" }, + { EVENT_TRACE_FLAG_INTERRUPT, "interrupt" }, + { EVENT_TRACE_FLAG_SYSTEMCALL, "syscall" }, + { EVENT_TRACE_FLAG_DISK_IO_INIT, "disk_io_init" }, + { EVENT_TRACE_FLAG_ALPC, "alpc" }, + { EVENT_TRACE_FLAG_SPLIT_IO, "split_io" }, + { EVENT_TRACE_FLAG_DRIVER, "driver" }, + { EVENT_TRACE_FLAG_PROFILE, "profile" }, + { EVENT_TRACE_FLAG_FILE_IO, "file_io" }, + { EVENT_TRACE_FLAG_FILE_IO_INIT, "file_io_init" }, + { EVENT_TRACE_FLAG_DISPATCHER, "dispatcher" }, + { EVENT_TRACE_FLAG_VIRTUAL_ALLOC, "virtual_alloc" }, + { EVENT_TRACE_FLAG_EXTENSION, "extension" }, + { EVENT_TRACE_FLAG_FORWARD_WMI, "forward_wmi" }, + { EVENT_TRACE_FLAG_ENABLE_RESERVE, "enable_reserve" }, +}; + +void +dumpKernelTraceFlags(FILE *output, const char *prefix, const char *suffix) +{ + int i; + + for (i = 0; i < sizeof(kernelFlags)/sizeof(kernelFlags[0]); i++) + fprintf(output, "%s%s%s", prefix, kernelFlags[i].name, + (i+1 == sizeof(kernelFlags)/sizeof(kernelFlags[0])) ? + "\0" : suffix); +} + +ULONG +kernelTraceFlag(const char *name) +{ + int i; + + for (i = 0; i < sizeof(kernelFlags)/sizeof(kernelFlags[0]); i++) + if (strcmp(kernelFlags[i].name, name) == 0) + return kernelFlags[i].flag; + fprintf(stderr, "Unrecognised kernel trace flag: %s\n", name); + fprintf(stderr, "List of all known options:\n"); + dumpKernelTraceFlags(stderr, "\t", "\n"); + fprintf(stderr, "\n\n"); + exit(1); +} + +const char * +eventPropertyFlags(USHORT flags) +{ + if (flags & EVENT_HEADER_PROPERTY_XML) + return "XML"; + if (flags & EVENT_HEADER_PROPERTY_FORWARDED_XML) + return "forwarded XML"; + if (flags & EVENT_HEADER_PROPERTY_LEGACY_EVENTLOG) + return "legacy WMI MOF"; + return "none"; +} + +const char * +eventHeaderFlags(USHORT flags) +{ + static char buffer[128]; + char *p = &buffer[0]; + + *p = '\0'; + if (flags & EVENT_HEADER_FLAG_EXTENDED_INFO) + strcat(p, "extended info,"); + if (flags & EVENT_HEADER_FLAG_PRIVATE_SESSION) + strcat(p, "private session,"); + if (flags & EVENT_HEADER_FLAG_STRING_ONLY) + strcat(p, "string,"); + if (flags & EVENT_HEADER_FLAG_TRACE_MESSAGE) + strcat(p, "TraceMessage,"); + if (flags & EVENT_HEADER_FLAG_NO_CPUTIME) + strcat(p, "no cputime,"); + if (flags & EVENT_HEADER_FLAG_32_BIT_HEADER) + strcat(p, "32bit,"); + if (flags & EVENT_HEADER_FLAG_64_BIT_HEADER) + strcat(p, "64bit,"); + if (flags & EVENT_HEADER_FLAG_CLASSIC_HEADER) + strcat(p, "classic,"); + buffer[strlen(buffer)-1] = '\0'; + return buffer; +} + +const char * +tdherror(ULONG code) +{ + switch (code){ + case ERROR_ACCESS_DENIED: + return "Insufficient privileges for requested operation"; + case ERROR_ALREADY_EXISTS: + return "A sessions with the same name or GUID already exists"; + case ERROR_BAD_LENGTH: + return "Insufficient space or size for a parameter"; + case ERROR_BAD_PATHNAME: + return "Given path parameter is not valid"; + case ERROR_CANCELLED: + return "Consumer cancelled processing via buffer callback"; + case ERROR_FILE_NOT_FOUND: + return "Unable to find the requested file"; + case ERROR_INSUFFICIENT_BUFFER: + return "Size of buffer is too small"; + case ERROR_INVALID_HANDLE: + return "Element of array is not a valid event tracing session handle"; + case ERROR_INVALID_PARAMETER: + return "One or more of the parameters is not valid"; + case ERROR_INVALID_TIME: + return "EndTime is less than StartTime"; + case ERROR_NOACCESS: + return "An exception occurred in one of the event callback routines"; + case ERROR_NOT_FOUND: + return "Requested class or field type not found"; + case ERROR_NOT_SUPPORTED: + return "The requested field type is not supported"; + case ERROR_OUTOFMEMORY: + return "Insufficient memory"; + case ERROR_WMI_INSTANCE_NOT_FOUND: + return "Session from which realtime events to be consumed not running"; + case ERROR_WMI_ALREADY_ENABLED: + return "Handle array contains more than one realtime session handle"; + case ERROR_SUCCESS: + return "Success"; + } + return strerror(code); +} + +const char * +strguid(LPGUID guidPointer) +{ + static char stringBuffer[64]; + + snprintf(stringBuffer, sizeof(stringBuffer), + "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", + guidPointer->Data1, guidPointer->Data2, guidPointer->Data3, + guidPointer->Data4[0], guidPointer->Data4[1], + guidPointer->Data4[2], guidPointer->Data4[3], + guidPointer->Data4[4], guidPointer->Data4[5], + guidPointer->Data4[6], guidPointer->Data4[7]); + return stringBuffer; +} + +void * +BufferAllocate(ULONG size) +{ + return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); +} + +void +BufferFree(void *buffer) +{ + if (buffer) + HeapFree(GetProcessHeap(), 0, buffer); +} diff --git a/src/pmdas/etw/util.h b/src/pmdas/etw/util.h new file mode 100644 index 0000000..4c3b32a --- /dev/null +++ b/src/pmdas/etw/util.h @@ -0,0 +1,31 @@ +/* + * Trace Data Helper utility routines. + * + * Copyright (c) 2011, Nathan Scott. All Rights Reserved. + * + * 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. + */ +#ifndef UTIL_H +#define UTIL_H + +extern void dumpKernelTraceFlags(FILE *, const char *, const char *); +extern ULONG kernelTraceFlag(const char *); + +extern const char *eventHeaderFlags(USHORT); +extern const char *eventPropertyFlags(USHORT); + +extern const char *strguid(LPGUID); +extern const char *tdherror(ULONG); + +extern void *BufferAllocate(ULONG); +extern void BufferFree(void *); + +#endif |