summaryrefslogtreecommitdiff
path: root/src/pmdas/etw
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
committerIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
commit47e6e7c84f008a53061e661f31ae96629bc694ef (patch)
tree648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmdas/etw
downloadpcp-47e6e7c84f008a53061e661f31ae96629bc694ef.tar.gz
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmdas/etw')
-rw-r--r--src/pmdas/etw/GNUmakefile78
-rw-r--r--src/pmdas/etw/event.c251
-rw-r--r--src/pmdas/etw/event.h94
-rw-r--r--src/pmdas/etw/help68
-rw-r--r--src/pmdas/etw/pcp.xmlbin0 -> 107596 bytes
-rw-r--r--src/pmdas/etw/pmda.c513
-rw-r--r--src/pmdas/etw/pmns132
-rw-r--r--src/pmdas/etw/root10
-rw-r--r--src/pmdas/etw/tdhconsume.c923
-rw-r--r--src/pmdas/etw/tdhlist.c267
-rw-r--r--src/pmdas/etw/util.c190
-rw-r--r--src/pmdas/etw/util.h31
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, &timestamp);
+ 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, &timestamp);
+ 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
new file mode 100644
index 0000000..d87f6a0
--- /dev/null
+++ b/src/pmdas/etw/pcp.xml
Binary files differ
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