diff options
Diffstat (limited to 'src/pmdas/etw/tdhconsume.c')
-rw-r--r-- | src/pmdas/etw/tdhconsume.c | 923 |
1 files changed, 923 insertions, 0 deletions
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; +} |