4995 lines
147 KiB
C
4995 lines
147 KiB
C
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
tracedc.c
|
|
|
|
Abstract:
|
|
|
|
Basic data consumer APIs to process trace data directly from buffers.
|
|
|
|
|
|
Author:
|
|
|
|
15-Sep-1997 JeePang
|
|
|
|
Revision History:
|
|
|
|
18-Apr-2001 insungp
|
|
|
|
Added asynchronopus IO for reading log files.
|
|
Also replaced WmiGetFirstTraceOffset() calls with sizeof(WMI_BUFFER_HEADER).
|
|
|
|
--*/
|
|
|
|
#ifndef MEMPHIS
|
|
|
|
#include "wmiump.h"
|
|
#include "traceump.h"
|
|
#include "evntrace.h"
|
|
#include "tracelib.h"
|
|
|
|
#define MAXSTR 1024
|
|
|
|
extern ULONG WmiTraceAlignment;
|
|
|
|
#ifdef DBG
|
|
void
|
|
WmipDumpEvent(
|
|
PEVENT_TRACE pEvent
|
|
);
|
|
void
|
|
WmipDumpGuid(
|
|
LPGUID
|
|
);
|
|
|
|
void
|
|
WmipDumpCallbacks();
|
|
#endif
|
|
#define KERNEL_LOGGER_CAPTION TEXT("NT Kernel Logger")
|
|
#define DEFAULT_LOG_BUFFER_SIZE 1024
|
|
|
|
#define MAXBUFFERS 1024
|
|
#define MINBUFFERS 8
|
|
#define MAX_KERNEL_TRACE_EVENTS 22 // Look at \nt\base\published\ntwmi.w for the event groups
|
|
|
|
#define TRACE_HEADER_SYSTEM32 TRACE_HEADER_FLAG | TRACE_HEADER_EVENT_TRACE \
|
|
| (TRACE_HEADER_TYPE_SYSTEM32 << 16)
|
|
#define TRACE_HEADER_SYSTEM64 TRACE_HEADER_FLAG | TRACE_HEADER_EVENT_TRACE \
|
|
| (TRACE_HEADER_TYPE_SYSTEM64 << 16)
|
|
#define TRACE_HEADER_FULL TRACE_HEADER_FLAG | TRACE_HEADER_EVENT_TRACE \
|
|
| (TRACE_HEADER_TYPE_FULL_HEADER << 16)
|
|
#define TRACE_HEADER_INSTANCE TRACE_HEADER_FLAG | TRACE_HEADER_EVENT_TRACE \
|
|
| (TRACE_HEADER_TYPE_INSTANCE << 16)
|
|
|
|
|
|
|
|
#define DEFAULT_REALTIME_BUFFER_SIZE 32768
|
|
#define MAXBUFFERS 1024
|
|
#define MINBUFFERS 8
|
|
|
|
//
|
|
// If the tracelog instance is a realtime data feed instead of from a
|
|
// tracefile, TRACELOG_REALTIME_CONTEXT is used to maintain the real time
|
|
// buffers in a buffer pool.
|
|
//
|
|
|
|
typedef struct _TRACE_BUFFER_SPACE {
|
|
ULONG Reserved; // amount of memory reserved
|
|
ULONG Committed;
|
|
PVOID Space;
|
|
LIST_ENTRY FreeListHead;
|
|
} TRACE_BUFFER_SPACE, *PTRACE_BUFFER_SPACE;
|
|
|
|
typedef struct _TRACELOG_REALTIME_CONTEXT {
|
|
ULONG BuffersProduced; // Number of Buffers to read
|
|
ULONG BufferOverflow; // Number of Buffers missed by the consumer
|
|
GUID InstanceGuid; // Logger Instance Guid
|
|
HANDLE MoreDataEvent; // Event to signal there is more data in this stream
|
|
PTRACE_BUFFER_SPACE WmipTraceBufferSpace;
|
|
PWNODE_HEADER RealTimeBufferPool[MAXBUFFERS];
|
|
} TRACELOG_REALTIME_CONTEXT, *PTRACELOG_REALTIME_CONTEXT;
|
|
|
|
|
|
//
|
|
// RealTime Free Buffer Pool is chained up as TRACE_BUFFER_HEADER
|
|
//
|
|
|
|
typedef struct _TRACE_BUFFER_HEADER {
|
|
WNODE_HEADER Wnode;
|
|
LIST_ENTRY Entry;
|
|
} TRACE_BUFFER_HEADER, *PTRACE_BUFFER_HEADER;
|
|
|
|
typedef struct _TRACERT_BUFFER_LIST_ENTRY {
|
|
ULONG Size;
|
|
LIST_ENTRY Entry;
|
|
LIST_ENTRY BufferListHead;
|
|
} TRACERT_BUFFER_LIST_ENTRY, *PTRACERT_BUFFER_LIST_ENTRY;
|
|
|
|
|
|
|
|
//
|
|
// This TraceHandleListHeadPtr should be the only real global
|
|
// for ProcessTrace to be multithreaded
|
|
//
|
|
|
|
PLIST_ENTRY TraceHandleListHeadPtr = NULL;
|
|
|
|
//
|
|
// For kernel events we map the Grouptype to Guid transparently to the caller.
|
|
// The mapping between GroupType and Guid is maintained by this structure.
|
|
//
|
|
|
|
typedef struct _TRACE_GUID_MAP { // used to map GroupType to Guid
|
|
ULONG GroupType; // Group & Type
|
|
GUID Guid; // Guid
|
|
} TRACE_GUID_MAP, *PTRACE_GUID_MAP;
|
|
|
|
PTRACE_GUID_MAP EventMapList = NULL; // Array mapping the Grouptype to Guids
|
|
|
|
typedef struct _EVENT_GUID_MAP {
|
|
LIST_ENTRY Entry;
|
|
ULONGLONG GuidHandle;
|
|
GUID Guid;
|
|
} EVENT_GUID_MAP, *PEVENT_GUID_MAP;
|
|
|
|
// List of Registered callbacks. One callback per Guid Only.
|
|
|
|
PLIST_ENTRY EventCallbackListHead = NULL;
|
|
|
|
typedef struct _EVENT_TRACE_CALLBACK {
|
|
LIST_ENTRY Entry;
|
|
GUID Guid;
|
|
PEVENT_CALLBACK CallbackRoutine;
|
|
} EVENT_TRACE_CALLBACK, *PEVENT_TRACE_CALLBACK;
|
|
|
|
|
|
#define MAX_TRACE_BUFFER_CACHE_SIZE 29
|
|
|
|
typedef struct _TRACE_BUFFER_CACHE_ENTRY {
|
|
LONG Index;
|
|
PVOID Buffer;
|
|
} TRACE_BUFFER_CACHE_ENTRY, *PTRACE_BUFFER_CACHE_ENTRY;
|
|
|
|
|
|
struct _TRACE_BUFFER_LIST_ENTRY;
|
|
|
|
typedef struct _TRACE_BUFFER_LIST_ENTRY {
|
|
struct _TRACE_BUFFER_LIST_ENTRY *Next;
|
|
LONG FileOffset; // Offset in File of this Buffer
|
|
ULONG BufferOffset; // Offset in Buffer for the current event
|
|
ULONG Flags; // Flags on status of this buffer
|
|
ULONG EventSize;
|
|
ULONG ClientContext; // Alignment, ProcessorNumber
|
|
ULONG TraceType; // Current Event Type
|
|
EVENT_TRACE Event; // CurrentEvent of this Buffer
|
|
} TRACE_BUFFER_LIST_ENTRY, *PTRACE_BUFFER_LIST_ENTRY;
|
|
|
|
|
|
typedef struct _TRACELOG_CONTEXT {
|
|
LIST_ENTRY Entry; // Keeps track of storage allocations.
|
|
|
|
// Fields from HandleListEntry
|
|
EVENT_TRACE_LOGFILEW Logfile;
|
|
|
|
TRACEHANDLE TraceHandle;
|
|
ULONG ConversionFlags; // Flags indicating event processing options
|
|
LONG BufferBeingRead;
|
|
OVERLAPPED AsynchRead;
|
|
|
|
//
|
|
// Fields Below this will be reset upon ProcessTrace exit.
|
|
//
|
|
|
|
BOOLEAN fProcessed;
|
|
USHORT LoggerId; // Logger Id of this DataFeed.
|
|
UCHAR IsRealTime; // Flag to tell if this feed is RT.
|
|
UCHAR fGuidMapRead;
|
|
|
|
LIST_ENTRY GuidMapListHead; // This is LogFile specific property
|
|
|
|
//
|
|
// For using PerfClock, we need to save startTime, Freq
|
|
//
|
|
|
|
ULONG UsePerfClock;
|
|
ULONG CpuSpeedInMHz;
|
|
LARGE_INTEGER PerfFreq; // Frequency from the LogFile
|
|
LARGE_INTEGER StartTime; // Start Wall clock time
|
|
LARGE_INTEGER StartPerfClock; // Start PerfClock value
|
|
|
|
union
|
|
{
|
|
HANDLE Handle; // NT handle to logfile
|
|
PTRACELOG_REALTIME_CONTEXT RealTimeCxt; // Ptr to Real Time Context
|
|
};
|
|
|
|
ULONG EndOfFile; // Flag to show whether this stream is still active.
|
|
|
|
|
|
ULONG BufferSize;
|
|
ULONG BufferCount;
|
|
ULONG InitialSize;
|
|
ULONG StartBuffer;
|
|
|
|
PTRACE_BUFFER_LIST_ENTRY Root;
|
|
PTRACE_BUFFER_LIST_ENTRY BufferList;
|
|
PVOID BufferCacheSpace;
|
|
TRACE_BUFFER_CACHE_ENTRY BufferCache[MAX_TRACE_BUFFER_CACHE_SIZE];
|
|
|
|
} TRACELOG_CONTEXT, *PTRACELOG_CONTEXT;
|
|
|
|
//
|
|
// this structure is used only by WmipGetBuffersWrittenFromQuery() and
|
|
// WmipCheckForRealTimeLoggers()
|
|
//
|
|
typedef struct _ETW_QUERY_PROPERTIES {
|
|
EVENT_TRACE_PROPERTIES TraceProp;
|
|
WCHAR LoggerName[MAXSTR];
|
|
WCHAR LogFileName[MAXSTR];
|
|
} ETW_QUERY_PROPERTIES, *PETW_QUERY_PROPERTIES;
|
|
|
|
ETW_QUERY_PROPERTIES Properties;
|
|
|
|
ULONG
|
|
WmipConvertEnumToTraceType(
|
|
WMI_HEADER_TYPE eTraceType
|
|
);
|
|
|
|
WMI_HEADER_TYPE
|
|
WmipConvertTraceTypeToEnum(
|
|
ULONG TraceType
|
|
);
|
|
|
|
ULONG
|
|
WmipCheckForRealTimeLoggers(
|
|
PEVENT_TRACE_LOGFILEW *Logfiles,
|
|
ULONG LogfileCount,
|
|
ULONG Unicode
|
|
);
|
|
|
|
ULONG
|
|
WmipLookforRealTimeBuffers(
|
|
PEVENT_TRACE_LOGFILEW logfile
|
|
);
|
|
ULONG
|
|
WmipRealTimeCallback(
|
|
IN PWNODE_HEADER Wnode,
|
|
IN ULONG_PTR Context
|
|
);
|
|
void
|
|
WmipFreeRealTimeContext(
|
|
PTRACELOG_REALTIME_CONTEXT RTCxt
|
|
);
|
|
|
|
ULONG
|
|
WmipSetupRealTimeContext(
|
|
PTRACEHANDLE HandleArray,
|
|
PEVENT_TRACE_LOGFILEW *Logfiles,
|
|
ULONG LogfileCount
|
|
);
|
|
|
|
PVOID
|
|
WmipAllocTraceBuffer(
|
|
PTRACELOG_REALTIME_CONTEXT RTCxt,
|
|
ULONG BufferSize
|
|
);
|
|
|
|
VOID
|
|
WmipFreeTraceBuffer(
|
|
PTRACELOG_REALTIME_CONTEXT RTCxt,
|
|
PVOID Buffer
|
|
);
|
|
|
|
ULONG
|
|
WmipProcessRealTimeTraces(
|
|
PTRACEHANDLE HandleArray,
|
|
PEVENT_TRACE_LOGFILEW *Logfiles,
|
|
ULONG LogfileCount,
|
|
LONGLONG StartTime,
|
|
LONGLONG EndTime,
|
|
ULONG Unicode
|
|
);
|
|
|
|
//
|
|
// Routines used in this file only
|
|
//
|
|
|
|
ULONG
|
|
WmipDoEventCallbacks(
|
|
PEVENT_TRACE_LOGFILEW logfile,
|
|
PEVENT_TRACE pEvent
|
|
);
|
|
|
|
ULONG
|
|
WmipCreateGuidMapping(void);
|
|
|
|
LPGUID
|
|
WmipGuidMapHandleToGuid(
|
|
PLIST_ENTRY GuidMapListHeadPtr,
|
|
ULONGLONG GuidHandle
|
|
);
|
|
|
|
void
|
|
WmipCleanupGuidMapList(
|
|
PLIST_ENTRY GuidMapListHeadPtr
|
|
);
|
|
|
|
|
|
void
|
|
WmipCleanupTraceLog(
|
|
PTRACELOG_CONTEXT pEntry
|
|
);
|
|
|
|
VOID
|
|
WmipGuidMapCallback(
|
|
PLIST_ENTRY GuidMapListHeadPtr,
|
|
PEVENT_TRACE pEvent
|
|
);
|
|
|
|
ULONG
|
|
WmipProcessGuidMaps(
|
|
PEVENT_TRACE_LOGFILEW *Logfiles,
|
|
ULONG LogfileCount,
|
|
ULONG Unicode
|
|
);
|
|
|
|
PEVENT_TRACE_CALLBACK
|
|
WmipGetCallbackRoutine(
|
|
LPGUID pGuid
|
|
);
|
|
|
|
VOID
|
|
WmipFreeCallbackList();
|
|
|
|
|
|
ULONG
|
|
WmipProcessLogHeader(
|
|
PTRACEHANDLE HandleArray,
|
|
PEVENT_TRACE_LOGFILEW *Logfiles,
|
|
ULONG LogfileCount,
|
|
ULONG Unicode,
|
|
ULONG bFree
|
|
);
|
|
|
|
ULONG
|
|
WmipProcessTraceLog(
|
|
PTRACEHANDLE HandleArray,
|
|
PEVENT_TRACE_LOGFILEW *Logfiles,
|
|
ULONG LogfileCount,
|
|
LONGLONG StartTime,
|
|
LONGLONG EndTime,
|
|
ULONG Unicode
|
|
);
|
|
|
|
ULONG
|
|
WmipParseTraceEvent(
|
|
IN PTRACELOG_CONTEXT pContext,
|
|
IN PVOID LogBuffer,
|
|
IN ULONG Offset,
|
|
IN WMI_HEADER_TYPE HeaderType,
|
|
IN OUT PVOID EventInfo,
|
|
IN ULONG EventInfoSize
|
|
);
|
|
|
|
ULONG
|
|
WmipGetBuffersWrittenFromQuery(
|
|
LPWSTR LoggerName
|
|
);
|
|
|
|
VOID
|
|
WmipCopyLogHeader (
|
|
IN PTRACE_LOGFILE_HEADER pLogFileHeader,
|
|
IN PVOID MofData,
|
|
IN ULONG MofLength,
|
|
IN PWCHAR *LoggerName,
|
|
IN PWCHAR *LogFileName,
|
|
IN ULONG Unicode
|
|
);
|
|
|
|
VOID
|
|
WmipInsertBuffer (
|
|
PTRACE_BUFFER_LIST_ENTRY *Root,
|
|
PTRACE_BUFFER_LIST_ENTRY NewEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine inserts a buffer in a sorted list. The insertion
|
|
is done based on the timestamp of the BufferHeader. If two buffers
|
|
have the same timestamp, then the BufferIndex is used to resolve the tie.
|
|
|
|
Arguments:
|
|
Root - Pointer to the root of the LIST
|
|
NewEntry - Entry being inserted
|
|
|
|
Returned Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PTRACE_BUFFER_LIST_ENTRY Current, Prev;
|
|
//
|
|
// If List is empty, make the new entry the Root and return.
|
|
//
|
|
|
|
if (NewEntry == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (*Root == NULL) {
|
|
*Root = NewEntry;
|
|
NewEntry->Next = NULL;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Traverse the list and insert the NewEntry in order
|
|
//
|
|
Prev = NULL;
|
|
Current = *Root;
|
|
|
|
while (Current != NULL) {
|
|
if ((ULONGLONG)NewEntry->Event.Header.TimeStamp.QuadPart < (ULONGLONG)Current->Event.Header.TimeStamp.QuadPart) {
|
|
if (Prev != NULL) {
|
|
Prev->Next = NewEntry;
|
|
}
|
|
else {
|
|
*Root = NewEntry;
|
|
}
|
|
NewEntry->Next = Current;
|
|
return;
|
|
}
|
|
else if ((ULONGLONG)NewEntry->Event.Header.TimeStamp.QuadPart == (ULONGLONG)Current->Event.Header.TimeStamp.QuadPart) {
|
|
if (NewEntry->FileOffset < Current->FileOffset) {
|
|
if (Prev != NULL) {
|
|
Prev->Next = NewEntry;
|
|
}
|
|
else {
|
|
*Root = NewEntry;
|
|
}
|
|
NewEntry->Next = Current;
|
|
return;
|
|
}
|
|
}
|
|
Prev = Current;
|
|
Current = Current->Next;
|
|
}
|
|
|
|
|
|
if (Prev != NULL) {
|
|
Prev->Next = NewEntry;
|
|
NewEntry->Next = NULL;
|
|
}
|
|
#if DBG
|
|
else {
|
|
WmipAssert(Prev != NULL);
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
|
|
PTRACE_BUFFER_LIST_ENTRY
|
|
WmipRemoveBuffer(
|
|
PTRACE_BUFFER_LIST_ENTRY *Root
|
|
)
|
|
{
|
|
PTRACE_BUFFER_LIST_ENTRY OldEntry = *Root;
|
|
|
|
if (OldEntry == NULL)
|
|
return NULL;
|
|
*Root = OldEntry->Next;
|
|
OldEntry->Next = NULL;
|
|
return OldEntry;
|
|
}
|
|
|
|
PVOID
|
|
WmipGetCurrentBuffer(
|
|
PTRACELOG_CONTEXT pContext,
|
|
PTRACE_BUFFER_LIST_ENTRY Current
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
LONG FileOffset = (ULONG)Current->FileOffset;
|
|
ULONG nBytesRead;
|
|
LONG TableIndex;
|
|
|
|
HANDLE hFile = pContext->Handle;
|
|
ULONG BufferSize = pContext->BufferSize;
|
|
PVOID pBuffer;
|
|
ULONGLONG Offset;
|
|
|
|
DWORD BytesTransffered;
|
|
|
|
//
|
|
// Look for the buffer in cache.
|
|
//
|
|
TableIndex = FileOffset % MAX_TRACE_BUFFER_CACHE_SIZE;
|
|
|
|
if (pContext->BufferCache[TableIndex].Index == FileOffset) {
|
|
//
|
|
// Check whether the buffer we want is still being read.
|
|
// If so, we need to wait for it to finish.
|
|
//
|
|
if (pContext->BufferBeingRead == FileOffset) {
|
|
if (GetOverlappedResult(hFile, &pContext->AsynchRead, &BytesTransffered, TRUE)) {
|
|
pContext->BufferBeingRead = -1;
|
|
}
|
|
else { // getting the result failed
|
|
return NULL;
|
|
}
|
|
}
|
|
return pContext->BufferCache[TableIndex].Buffer;
|
|
}
|
|
|
|
//
|
|
// Do a synch read for the buffer we need. We still need to make sure the previous read
|
|
// has completed.
|
|
//
|
|
pBuffer = pContext->BufferCache[TableIndex].Buffer;
|
|
Offset = FileOffset * BufferSize;
|
|
if (pContext->BufferBeingRead != -1) {
|
|
if (!GetOverlappedResult(hFile, &pContext->AsynchRead, &BytesTransffered, TRUE) &&
|
|
GetLastError() != ERROR_HANDLE_EOF) {
|
|
WmipDebugPrint(("GetOverlappedResult failed with Status %d in GetCurrentBuffer\n", GetLastError()));
|
|
// cannot determine the status of the previous read
|
|
return NULL;
|
|
}
|
|
}
|
|
pContext->AsynchRead.Offset = (DWORD)(Offset & 0xFFFFFFFF);
|
|
pContext->AsynchRead.OffsetHigh = (DWORD)(Offset >> 32);
|
|
Status = WmipSynchReadFile(hFile,
|
|
(LPVOID)pBuffer,
|
|
BufferSize,
|
|
&nBytesRead,
|
|
&pContext->AsynchRead);
|
|
pContext->BufferBeingRead = -1;
|
|
if (nBytesRead == 0) {
|
|
return NULL;
|
|
}
|
|
//
|
|
// Update the cache entry with the one just read in
|
|
//
|
|
|
|
pContext->BufferCache[TableIndex].Index = FileOffset;
|
|
|
|
//
|
|
// We need to account for event alignments when backtracking to get the MofPtr.
|
|
// (BufferOffset - EventSize) backtracks to the start of the current event.
|
|
// Add EventHeaderSize and Subtract the MofLength gives the MofPtr.
|
|
//
|
|
if (pContext->ConversionFlags & EVENT_TRACE_GET_RAWEVENT) {
|
|
Current->Event.MofData = ((PUCHAR)pBuffer
|
|
+ Current->BufferOffset
|
|
- Current->EventSize);
|
|
if (pContext->UsePerfClock == EVENT_TRACE_CLOCK_PERFCOUNTER) {
|
|
|
|
//
|
|
// Need to override the timestamp with SystemTime
|
|
//
|
|
switch(Current->TraceType) {
|
|
case TRACE_HEADER_TYPE_PERFINFO32:
|
|
case TRACE_HEADER_TYPE_PERFINFO64:
|
|
{
|
|
PPERFINFO_TRACE_HEADER pPerf = (PPERFINFO_TRACE_HEADER)Current->Event.MofData;
|
|
pPerf->SystemTime = Current->Event.Header.TimeStamp;
|
|
break;
|
|
}
|
|
case TRACE_HEADER_TYPE_SYSTEM32:
|
|
{
|
|
PSYSTEM_TRACE_HEADER pSystemHeader32 = (PSYSTEM_TRACE_HEADER) Current->Event.MofData;
|
|
pSystemHeader32->SystemTime = Current->Event.Header.TimeStamp;
|
|
break;
|
|
}
|
|
case TRACE_HEADER_TYPE_SYSTEM64:
|
|
{
|
|
PSYSTEM_TRACE_HEADER pSystemHeader64 = (PSYSTEM_TRACE_HEADER) Current->Event.MofData;
|
|
pSystemHeader64->SystemTime = Current->Event.Header.TimeStamp;
|
|
break;
|
|
}
|
|
case TRACE_HEADER_TYPE_FULL_HEADER:
|
|
{
|
|
PEVENT_TRACE_HEADER pWnodeHeader = (PEVENT_TRACE_HEADER) Current->Event.MofData;
|
|
pWnodeHeader->TimeStamp = Current->Event.Header.TimeStamp;
|
|
break;
|
|
}
|
|
case TRACE_HEADER_TYPE_INSTANCE:
|
|
{
|
|
PEVENT_INSTANCE_HEADER pInstanceHeader = (PEVENT_INSTANCE_HEADER) Current->Event.MofData;
|
|
pInstanceHeader->TimeStamp = Current->Event.Header.TimeStamp;
|
|
break;
|
|
}
|
|
case TRACE_HEADER_TYPE_TIMED:
|
|
{
|
|
PTIMED_TRACE_HEADER pTimedHeader = (PTIMED_TRACE_HEADER) Current->Event.MofData;
|
|
pTimedHeader->TimeStamp = Current->Event.Header.TimeStamp;
|
|
break;
|
|
}
|
|
case TRACE_HEADER_TYPE_WNODE_HEADER:
|
|
case TRACE_HEADER_TYPE_MESSAGE:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// When The FileOffset is 0 (First Buffer) and the EventType is
|
|
// LOGFILE_HEADER
|
|
//
|
|
|
|
if ( (FileOffset == 0) &&
|
|
((Current->BufferOffset - Current->EventSize) == sizeof(WMI_BUFFER_HEADER)) )
|
|
{
|
|
Current->Event.MofData = (PVOID)&pContext->Logfile.LogfileHeader;
|
|
}
|
|
else
|
|
{
|
|
Current->Event.MofData = ((PUCHAR)pBuffer
|
|
+ Current->BufferOffset
|
|
- Current->EventSize
|
|
+ Current->Event.Header.Size
|
|
- Current->Event.MofLength );
|
|
}
|
|
}
|
|
|
|
return pBuffer;
|
|
}
|
|
|
|
PTRACELOG_CONTEXT
|
|
WmipAllocateTraceHandle()
|
|
{
|
|
PLIST_ENTRY Next, Head;
|
|
PTRACELOG_CONTEXT NewHandleEntry, pEntry;
|
|
|
|
WmipEnterPMCritSection();
|
|
|
|
if (TraceHandleListHeadPtr == NULL) {
|
|
TraceHandleListHeadPtr = WmipAlloc(sizeof(LIST_ENTRY));
|
|
if (TraceHandleListHeadPtr == NULL) {
|
|
WmipSetDosError(ERROR_OUTOFMEMORY);
|
|
WmipLeavePMCritSection();
|
|
return NULL;
|
|
}
|
|
InitializeListHead(TraceHandleListHeadPtr);
|
|
}
|
|
|
|
NewHandleEntry = WmipAlloc(sizeof(TRACELOG_CONTEXT));
|
|
if (NewHandleEntry == NULL) {
|
|
WmipSetDosError(ERROR_OUTOFMEMORY);
|
|
WmipLeavePMCritSection();
|
|
return NULL;
|
|
}
|
|
|
|
RtlZeroMemory(NewHandleEntry, sizeof(TRACELOG_CONTEXT));
|
|
|
|
// AsynchRead initialization
|
|
NewHandleEntry->BufferBeingRead = -1;
|
|
NewHandleEntry->AsynchRead.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
|
|
if (NewHandleEntry->AsynchRead.hEvent == NULL) {
|
|
WmipFree(NewHandleEntry);
|
|
WmipLeavePMCritSection();
|
|
return NULL;
|
|
}
|
|
|
|
|
|
InitializeListHead(&NewHandleEntry->GuidMapListHead);
|
|
Head = TraceHandleListHeadPtr;
|
|
Next = Head->Flink;
|
|
if (Next == Head) {
|
|
NewHandleEntry->TraceHandle = 1;
|
|
InsertTailList(Head, &NewHandleEntry->Entry);
|
|
WmipLeavePMCritSection();
|
|
return NewHandleEntry;
|
|
}
|
|
|
|
while (Next != Head) {
|
|
pEntry = CONTAINING_RECORD( Next, TRACELOG_CONTEXT, Entry );
|
|
Next = Next->Flink;
|
|
NewHandleEntry->TraceHandle++;
|
|
if (NewHandleEntry->TraceHandle < pEntry->TraceHandle) {
|
|
InsertTailList(&pEntry->Entry, &NewHandleEntry->Entry);
|
|
WmipLeavePMCritSection();
|
|
return NewHandleEntry;
|
|
}
|
|
}
|
|
//
|
|
// TODO: Need to optimize this case out first before searching...
|
|
//
|
|
NewHandleEntry->TraceHandle++;
|
|
InsertTailList(Head, &NewHandleEntry->Entry);
|
|
WmipLeavePMCritSection();
|
|
return NewHandleEntry;
|
|
|
|
}
|
|
|
|
PTRACELOG_CONTEXT
|
|
WmipLookupTraceHandle(
|
|
TRACEHANDLE TraceHandle
|
|
)
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
PTRACELOG_CONTEXT pEntry;
|
|
|
|
WmipEnterPMCritSection();
|
|
Head = TraceHandleListHeadPtr;
|
|
|
|
if (Head == NULL) {
|
|
WmipLeavePMCritSection();
|
|
return NULL;
|
|
}
|
|
Next = Head->Flink;
|
|
while (Next != Head) {
|
|
pEntry = CONTAINING_RECORD( Next, TRACELOG_CONTEXT, Entry );
|
|
Next = Next->Flink;
|
|
|
|
if (TraceHandle == pEntry->TraceHandle) {
|
|
WmipLeavePMCritSection();
|
|
return pEntry;
|
|
}
|
|
}
|
|
WmipLeavePMCritSection();
|
|
return NULL;
|
|
}
|
|
|
|
ULONG
|
|
WmipFreeTraceHandle(
|
|
TRACEHANDLE TraceHandle
|
|
)
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
PTRACELOG_CONTEXT pEntry;
|
|
|
|
WmipEnterPMCritSection();
|
|
|
|
Head = TraceHandleListHeadPtr;
|
|
if (Head == NULL) {
|
|
WmipLeavePMCritSection();
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
Next = Head->Flink;
|
|
|
|
while (Next != Head) {
|
|
pEntry = CONTAINING_RECORD( Next, TRACELOG_CONTEXT, Entry );
|
|
Next = Next->Flink;
|
|
if (TraceHandle == pEntry->TraceHandle) {
|
|
|
|
if (pEntry->fProcessed == TRUE)
|
|
{
|
|
WmipLeavePMCritSection();
|
|
return ERROR_BUSY;
|
|
}
|
|
|
|
RemoveEntryList(&pEntry->Entry);
|
|
|
|
// Free pEntry memory
|
|
//
|
|
if (pEntry->Logfile.LogFileName != NULL)
|
|
{
|
|
WmipFree(pEntry->Logfile.LogFileName);
|
|
}
|
|
if (pEntry->Logfile.LoggerName != NULL)
|
|
{
|
|
WmipFree(pEntry->Logfile.LoggerName);
|
|
}
|
|
CloseHandle(pEntry->AsynchRead.hEvent);
|
|
WmipFree(pEntry);
|
|
|
|
//
|
|
// If the handle list is empty, then delete it.
|
|
//
|
|
if (Head == Head->Flink) {
|
|
WmipFree(TraceHandleListHeadPtr);
|
|
TraceHandleListHeadPtr = NULL;
|
|
}
|
|
WmipLeavePMCritSection();
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
WmipLeavePMCritSection();
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
|
|
ULONG
|
|
WMIAPI
|
|
WmipCreateGuidMapping(void)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine is used to create the mapping array between GroupTypes and Guid
|
|
for kernel events.
|
|
|
|
Arguments:
|
|
None.
|
|
|
|
Returned Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
ULONG i = 0;
|
|
ULONG listsize;
|
|
|
|
if (EventMapList == NULL) {
|
|
listsize = sizeof(TRACE_GUID_MAP) * MAX_KERNEL_TRACE_EVENTS;
|
|
EventMapList = (PTRACE_GUID_MAP) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, listsize);
|
|
if (EventMapList == NULL) {
|
|
return WmipSetDosError(ERROR_OUTOFMEMORY);
|
|
}
|
|
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_IO;
|
|
RtlCopyMemory(&EventMapList[i++].Guid,&DiskIoGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_FILE;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &FileIoGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_TCPIP;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &TcpIpGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_UDPIP;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &UdpIpGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_THREAD;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &ThreadGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_PROCESS;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &ProcessGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_MEMORY;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &PageFaultGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_HEADER;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &EventTraceGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_PROCESS +
|
|
EVENT_TRACE_TYPE_LOAD;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &ImageLoadGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_REGISTRY;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &RegistryGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_DBGPRINT;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &DbgPrintGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_CONFIG;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &EventTraceConfigGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_POOL;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &PoolGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_PERFINFO;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &PerfinfoGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_HEAP;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &HeapGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_OBJECT;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &ObjectGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_MODBOUND;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &ModBoundGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_DPC;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &DpcGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_POWER;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &PowerGuid, sizeof(GUID));
|
|
EventMapList[i].GroupType = EVENT_TRACE_GROUP_CRITSEC;
|
|
RtlCopyMemory(&EventMapList[i++].Guid, &CritSecGuid, sizeof(GUID));
|
|
}
|
|
return WmipSetDosError(ERROR_SUCCESS);
|
|
}
|
|
|
|
LPGUID
|
|
WmipGuidMapHandleToGuid(
|
|
PLIST_ENTRY GuidMapListHeadPtr,
|
|
ULONGLONG GuidHandle
|
|
)
|
|
{
|
|
PLIST_ENTRY Next, Head;
|
|
PEVENT_GUID_MAP GuidMap;
|
|
ULONG retry_count=0;
|
|
|
|
retry:
|
|
|
|
WmipEnterPMCritSection();
|
|
|
|
Head = GuidMapListHeadPtr;
|
|
Next = Head->Flink;
|
|
while (Next != Head) {
|
|
GuidMap = CONTAINING_RECORD( Next, EVENT_GUID_MAP, Entry );
|
|
if (GuidMap->GuidHandle == GuidHandle) {
|
|
WmipLeavePMCritSection();
|
|
return (&GuidMap->Guid);
|
|
}
|
|
Next = Next->Flink;
|
|
}
|
|
WmipLeavePMCritSection();
|
|
|
|
//
|
|
// At this point, assume that this is realtime feed and
|
|
// and try to dumpout guids and try again.
|
|
|
|
if ((WmipDumpGuidMaps(NULL, GuidMapListHeadPtr, TRUE) > 0) &&
|
|
(retry_count++ < 1)) {
|
|
goto retry;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
ULONG
|
|
WmipAddGuidHandleToGuidMapList(
|
|
IN PLIST_ENTRY GuidMapListHeadPtr,
|
|
IN ULONGLONG GuidHandle,
|
|
IN LPGUID Guid
|
|
)
|
|
{
|
|
PEVENT_GUID_MAP GuidMap;
|
|
|
|
if (GuidMapListHeadPtr != NULL) {
|
|
GuidMap = WmipAlloc(sizeof(EVENT_GUID_MAP));
|
|
if (GuidMap == NULL)
|
|
return WmipSetDosError(ERROR_OUTOFMEMORY);
|
|
|
|
RtlZeroMemory(GuidMap, sizeof(EVENT_GUID_MAP));
|
|
|
|
GuidMap->GuidHandle = GuidHandle;
|
|
GuidMap->Guid = *Guid;
|
|
WmipEnterPMCritSection();
|
|
InsertTailList(GuidMapListHeadPtr, &GuidMap->Entry);
|
|
WmipLeavePMCritSection();
|
|
}
|
|
return WmipSetDosError(ERROR_SUCCESS);
|
|
}
|
|
|
|
void
|
|
WmipCleanupGuidMapList(
|
|
PLIST_ENTRY GuidMapListHeadPtr
|
|
)
|
|
{
|
|
PLIST_ENTRY Next, Head;
|
|
PEVENT_GUID_MAP GuidMap;
|
|
WmipEnterPMCritSection();
|
|
if (GuidMapListHeadPtr != NULL) {
|
|
|
|
Head = GuidMapListHeadPtr;
|
|
Next = Head->Flink;
|
|
while (Next != Head) {
|
|
GuidMap = CONTAINING_RECORD( Next, EVENT_GUID_MAP, Entry );
|
|
Next = Next->Flink;
|
|
RemoveEntryList(&GuidMap->Entry);
|
|
WmipFree(GuidMap);
|
|
}
|
|
GuidMapListHeadPtr = NULL;
|
|
}
|
|
WmipLeavePMCritSection();
|
|
}
|
|
|
|
LPGUID
|
|
WmipGroupTypeToGuid(
|
|
ULONG GroupType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine returns the GUID corresponding to a given GroupType.
|
|
The mapping is static and is defined by the kernel provider.
|
|
|
|
Arguments:
|
|
GroupType The GroupType of the kernel event.
|
|
|
|
Returned Value:
|
|
|
|
Pointer to the GUID representing the given GroupType.
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
for (i = 0; i < MAX_KERNEL_TRACE_EVENTS; i++) {
|
|
if (EventMapList[i].GroupType == GroupType)
|
|
return (&EventMapList[i].Guid);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
VOID
|
|
WmipFreeCallbackList()
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine removes all event callbacks and frees the storage.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returned Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY Next, Head;
|
|
PEVENT_TRACE_CALLBACK EventCb;
|
|
|
|
|
|
if (EventCallbackListHead == NULL)
|
|
return;
|
|
|
|
WmipEnterPMCritSection();
|
|
|
|
Head = EventCallbackListHead;
|
|
Next = Head->Flink;
|
|
while (Next != Head) {
|
|
EventCb = CONTAINING_RECORD( Next, EVENT_TRACE_CALLBACK, Entry );
|
|
Next = Next->Flink;
|
|
RemoveEntryList(&EventCb->Entry);
|
|
WmipFree(EventCb);
|
|
}
|
|
|
|
WmipFree(EventCallbackListHead);
|
|
EventCallbackListHead = NULL;
|
|
|
|
WmipLeavePMCritSection();
|
|
}
|
|
|
|
|
|
PEVENT_TRACE_CALLBACK
|
|
WmipGetCallbackRoutine(
|
|
LPGUID pGuid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine returns the callback function for a given Guid.
|
|
If no callback was registered for the Guid, returns NULL.
|
|
|
|
Arguments:
|
|
pGuid pointer to the Guid.
|
|
|
|
Returned Value:
|
|
|
|
Event Trace Callback Function.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY head, next;
|
|
PEVENT_TRACE_CALLBACK pEventCb = NULL;
|
|
|
|
if (pGuid == NULL)
|
|
return NULL;
|
|
|
|
WmipEnterPMCritSection();
|
|
|
|
if (EventCallbackListHead == NULL) {
|
|
WmipLeavePMCritSection();
|
|
return NULL;
|
|
}
|
|
|
|
head = EventCallbackListHead;
|
|
next = head->Flink;
|
|
while (next != head) {
|
|
pEventCb = CONTAINING_RECORD( next, EVENT_TRACE_CALLBACK, Entry);
|
|
if (IsEqualGUID(pGuid, &pEventCb->Guid)) {
|
|
WmipLeavePMCritSection();
|
|
return (pEventCb);
|
|
}
|
|
next = next->Flink;
|
|
}
|
|
|
|
WmipLeavePMCritSection();
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
ULONG
|
|
WMIAPI
|
|
SetTraceCallback(
|
|
IN LPCGUID pGuid,
|
|
IN PEVENT_CALLBACK EventCallback
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to wire a callback function for Guid. The
|
|
callback function is called when an Event with this Guid is found in
|
|
the subsequent ProcessTraceLog Call.
|
|
|
|
Arguments:
|
|
|
|
pGuid Pointer to the Guid.
|
|
|
|
func Callback Function Address.
|
|
|
|
|
|
Return Value:
|
|
ERROR_SUCCESS Callback function is wired
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
PEVENT_TRACE_CALLBACK pEventCb;
|
|
PLIST_ENTRY head, next;
|
|
GUID FilterGuid;
|
|
ULONG Checksum;
|
|
ULONG Status;
|
|
|
|
WmipInitProcessHeap();
|
|
|
|
if ((pGuid == NULL) || (EventCallback == NULL) ||
|
|
(EventCallback == (PEVENT_CALLBACK) -1 ) ) {
|
|
return WmipSetDosError(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Capture the Guid first
|
|
//
|
|
try {
|
|
FilterGuid = *pGuid;
|
|
Checksum = *((PULONG)EventCallback);
|
|
if (Checksum) {
|
|
Status = Checksum;
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return WmipSetDosError(ERROR_NOACCESS);
|
|
}
|
|
|
|
WmipEnterPMCritSection();
|
|
|
|
if (EventCallbackListHead == NULL) {
|
|
EventCallbackListHead = (PLIST_ENTRY) WmipAlloc(sizeof(LIST_ENTRY));
|
|
if (EventCallbackListHead == NULL) {
|
|
WmipLeavePMCritSection();
|
|
return WmipSetDosError(ERROR_OUTOFMEMORY);
|
|
}
|
|
InitializeListHead(EventCallbackListHead);
|
|
}
|
|
|
|
//
|
|
// If there is a callback wired for this Guid, simply update the function.
|
|
//
|
|
|
|
head = EventCallbackListHead;
|
|
next = head->Flink;
|
|
while (next != head) {
|
|
pEventCb = CONTAINING_RECORD( next, EVENT_TRACE_CALLBACK, Entry);
|
|
if (IsEqualGUID(&FilterGuid, &pEventCb->Guid)) {
|
|
pEventCb->CallbackRoutine = EventCallback;
|
|
WmipLeavePMCritSection();
|
|
return WmipSetDosError(ERROR_SUCCESS);
|
|
}
|
|
next = next->Flink;
|
|
}
|
|
|
|
//
|
|
// Create a new entry in the EventCallbackList for this Guid.
|
|
//
|
|
pEventCb = (PEVENT_TRACE_CALLBACK) WmipAlloc (sizeof(EVENT_TRACE_CALLBACK));
|
|
if (pEventCb == NULL) {
|
|
WmipLeavePMCritSection();
|
|
return WmipSetDosError(ERROR_OUTOFMEMORY);
|
|
}
|
|
RtlZeroMemory(pEventCb, sizeof(EVENT_TRACE_CALLBACK));
|
|
pEventCb->Guid = FilterGuid;
|
|
pEventCb->CallbackRoutine = EventCallback;
|
|
|
|
InsertTailList(EventCallbackListHead, &pEventCb->Entry);
|
|
|
|
WmipLeavePMCritSection();
|
|
Status = ERROR_SUCCESS;
|
|
return WmipSetDosError(Status);
|
|
|
|
}
|
|
|
|
ULONG
|
|
WMIAPI
|
|
RemoveTraceCallback(
|
|
IN LPCGUID pGuid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine removes a callback function for a given Guid.
|
|
|
|
Arguments:
|
|
|
|
pGuid Pointer to the Guid for which the callback routine needs
|
|
to be deleted.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS Successfully deleted the callback routine.
|
|
ERROR_INVALID_PARAMETER Could not find any callbacks for the Guid.
|
|
--*/
|
|
{
|
|
PLIST_ENTRY next, head;
|
|
PEVENT_TRACE_CALLBACK EventCb;
|
|
GUID RemoveGuid;
|
|
ULONG errorCode;
|
|
#ifdef DBG
|
|
CHAR GuidStr[64];
|
|
#endif
|
|
|
|
WmipInitProcessHeap();
|
|
|
|
if ((pGuid == NULL) || (EventCallbackListHead == NULL))
|
|
return WmipSetDosError(ERROR_INVALID_PARAMETER);
|
|
|
|
//
|
|
// Capture the Guid into a local variable first
|
|
//
|
|
try {
|
|
RemoveGuid = *pGuid;
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return WmipSetDosError(ERROR_NOACCESS);
|
|
}
|
|
|
|
errorCode = ERROR_WMI_GUID_NOT_FOUND;
|
|
|
|
WmipEnterPMCritSection();
|
|
|
|
head = EventCallbackListHead;
|
|
next = head->Flink;
|
|
while (next != head) {
|
|
EventCb = CONTAINING_RECORD( next, EVENT_TRACE_CALLBACK, Entry);
|
|
next = next->Flink;
|
|
if (IsEqualGUID(&EventCb->Guid, &RemoveGuid)) {
|
|
RemoveEntryList(&EventCb->Entry);
|
|
WmipFree(EventCb);
|
|
errorCode = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
WmipLeavePMCritSection();
|
|
return WmipSetDosError(errorCode);
|
|
}
|
|
|
|
#ifdef DBG
|
|
void
|
|
WmipDumpEvent(
|
|
PEVENT_TRACE pEvent
|
|
)
|
|
{
|
|
DbgPrint("\tSize %d\n", pEvent->Header.Size);
|
|
DbgPrint("\tThreadId %X\n", pEvent->Header.ThreadId);
|
|
DbgPrint("\tTime Stamp %I64u\n", pEvent->Header.TimeStamp.QuadPart);
|
|
}
|
|
|
|
void
|
|
WmipDumpGuid(
|
|
LPGUID pGuid
|
|
)
|
|
{
|
|
DbgPrint("Guid=%x,%x,%x,\n\t{%x,%x,%x,%x,%x,%x,%x}\n",
|
|
pGuid->Data1, pGuid->Data2, pGuid->Data3,
|
|
pGuid->Data4[0], pGuid->Data4[1], pGuid->Data4[2], pGuid->Data4[3],
|
|
pGuid->Data4[5], pGuid->Data4[6], pGuid->Data4[7], pGuid->Data4[8]);
|
|
}
|
|
|
|
void WmipDumpCallbacks()
|
|
{
|
|
PLIST_ENTRY next, head;
|
|
PEVENT_TRACE_CALLBACK EventCb;
|
|
|
|
if (EventCallbackListHead == NULL)
|
|
return;
|
|
WmipEnterPMCritSection();
|
|
head = EventCallbackListHead;
|
|
next = head->Flink;
|
|
while (next != head) {
|
|
EventCb = CONTAINING_RECORD(next, EVENT_TRACE_CALLBACK, Entry);
|
|
WmipDumpGuid(&EventCb->Guid);
|
|
next = next->Flink;
|
|
}
|
|
WmipLeavePMCritSection();
|
|
}
|
|
|
|
#endif
|
|
|
|
VOID
|
|
WmipCalculateCurrentTime (
|
|
OUT PLARGE_INTEGER DestTime,
|
|
IN PLARGE_INTEGER TimeValue,
|
|
IN PTRACELOG_CONTEXT pContext
|
|
)
|
|
{
|
|
ULONG64 StartPerfClock;
|
|
ULONG64 CurrentTime, TimeStamp;
|
|
ULONG64 Delta;
|
|
double dDelta;
|
|
|
|
if (pContext == NULL) {
|
|
Move64(TimeValue, DestTime);
|
|
return;
|
|
}
|
|
|
|
if (pContext->ConversionFlags & EVENT_TRACE_GET_RAWEVENT) {
|
|
Move64(TimeValue, DestTime);
|
|
return;
|
|
}
|
|
|
|
Move64(TimeValue, (PLARGE_INTEGER) &TimeStamp);
|
|
|
|
if ((pContext->UsePerfClock == EVENT_TRACE_CLOCK_SYSTEMTIME) ||
|
|
(pContext->UsePerfClock == EVENT_TRACE_CLOCK_RAW)) {
|
|
//
|
|
// System time, just return the time stamp.
|
|
//
|
|
Move64(TimeValue, DestTime);
|
|
return;
|
|
}
|
|
else if (pContext->UsePerfClock == EVENT_TRACE_CLOCK_PERFCOUNTER) {
|
|
if (pContext->PerfFreq.QuadPart == 0) {
|
|
Move64(TimeValue, DestTime);
|
|
return;
|
|
}
|
|
StartPerfClock = pContext->StartPerfClock.QuadPart;
|
|
if (TimeStamp > StartPerfClock) {
|
|
Delta = (TimeStamp - StartPerfClock);
|
|
dDelta = ((double) Delta) * (10000000.0 / (double)pContext->PerfFreq.QuadPart);
|
|
Delta = (ULONG64)dDelta;
|
|
CurrentTime = pContext->StartTime.QuadPart + Delta;
|
|
}
|
|
else {
|
|
Delta = StartPerfClock - TimeStamp;
|
|
dDelta = ((double) Delta) * (10000000.0 / (double)pContext->PerfFreq.QuadPart);
|
|
Delta = (ULONG64)dDelta;
|
|
CurrentTime = pContext->StartTime.QuadPart - Delta;
|
|
}
|
|
Move64((PLARGE_INTEGER) &CurrentTime, DestTime);
|
|
return;
|
|
}
|
|
else {
|
|
if (pContext->CpuSpeedInMHz == 0) {
|
|
Move64(TimeValue, DestTime);
|
|
return;
|
|
}
|
|
StartPerfClock = pContext->StartPerfClock.QuadPart;
|
|
if (TimeStamp > StartPerfClock) {
|
|
Delta = (TimeStamp - StartPerfClock);
|
|
dDelta = ((double) Delta) * (10.0 / (double)pContext->CpuSpeedInMHz);
|
|
Delta = (ULONG64)dDelta;
|
|
CurrentTime = pContext->StartTime.QuadPart + Delta;
|
|
}
|
|
else {
|
|
Delta = StartPerfClock - TimeStamp;
|
|
dDelta = ((double) Delta) * (10.0 / (double)pContext->CpuSpeedInMHz);
|
|
Delta = (ULONG64)dDelta;
|
|
CurrentTime = pContext->StartTime.QuadPart - Delta;
|
|
}
|
|
Move64((PLARGE_INTEGER) &CurrentTime, DestTime);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
ULONG
|
|
WmipCopyCurrentEvent(
|
|
PTRACELOG_CONTEXT pContext,
|
|
PVOID pHeader,
|
|
PEVENT_TRACE pEvent,
|
|
ULONG TraceType,
|
|
PWMI_BUFFER_HEADER LogBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine copies the Current Event from the logfile buffer stream to
|
|
the CurrentEvent structure provided by the caller. The routine takes
|
|
care of the differences between kernel event and user events by mapping
|
|
all events uniformly to the EVENT_TRACE_HEADER structure.
|
|
|
|
Arguments:
|
|
pHeader Pointer to the datablock in the input stream (logfile).
|
|
pEvent Current Event to which the data is copied.
|
|
TraceType Enum indicating the header type.
|
|
LogBuffer The buffer
|
|
|
|
Returned Value:
|
|
|
|
Status indicating success or failure.
|
|
|
|
--*/
|
|
{
|
|
PEVENT_TRACE_HEADER pWnode;
|
|
PEVENT_TRACE_HEADER pWnodeHeader;
|
|
ULONG nGroupType;
|
|
LPGUID pGuid;
|
|
ULONG UsePerfClock = 0;
|
|
ULONG UseBasePtr = 0;
|
|
ULONG PrivateLogger=0;
|
|
|
|
if (pHeader == NULL || pEvent == NULL)
|
|
return WmipSetDosError(ERROR_INVALID_PARAMETER);
|
|
|
|
if (pContext != NULL) {
|
|
UsePerfClock = pContext->UsePerfClock;
|
|
UseBasePtr = pContext->ConversionFlags & EVENT_TRACE_GET_RAWEVENT;
|
|
PrivateLogger = (pContext->Logfile.LogFileMode & EVENT_TRACE_PRIVATE_LOGGER_MODE);
|
|
}
|
|
|
|
switch(TraceType) {
|
|
//
|
|
// ISSUE: Need to split the two so we can process cross platform.
|
|
// shsiao 03/22/2000
|
|
//
|
|
case TRACE_HEADER_TYPE_PERFINFO32:
|
|
case TRACE_HEADER_TYPE_PERFINFO64:
|
|
{
|
|
PPERFINFO_TRACE_HEADER pPerfHeader;
|
|
pPerfHeader = (PPERFINFO_TRACE_HEADER) pHeader;
|
|
nGroupType = pPerfHeader->Packet.Group << 8;
|
|
if ((nGroupType == EVENT_TRACE_GROUP_PROCESS) &&
|
|
(pPerfHeader->Packet.Type == EVENT_TRACE_TYPE_LOAD)) {
|
|
nGroupType += pPerfHeader->Packet.Type;
|
|
}
|
|
RtlZeroMemory(pEvent, sizeof(EVENT_TRACE));
|
|
pWnode = (PEVENT_TRACE_HEADER) &pEvent->Header;
|
|
|
|
pGuid = WmipGroupTypeToGuid(nGroupType);
|
|
if (pGuid != NULL)
|
|
RtlCopyMemory(&pWnode->Guid, pGuid, sizeof(GUID));
|
|
|
|
pWnode->Size = pPerfHeader->Packet.Size;
|
|
pWnode->Class.Type = pPerfHeader->Packet.Type;
|
|
pWnode->Class.Version = pPerfHeader->Version;
|
|
|
|
WmipCalculateCurrentTime( &pWnode->TimeStamp,
|
|
&pPerfHeader->SystemTime,
|
|
pContext );
|
|
|
|
//
|
|
// PERFINFO headers does not have ThreadId or CPU Times
|
|
//
|
|
|
|
if( LogBuffer->Flags & WNODE_FLAG_THREAD_BUFFER ){
|
|
|
|
pWnode->ThreadId = LogBuffer->CurrentOffset;
|
|
|
|
} else {
|
|
|
|
pWnode->ProcessId = -1;
|
|
pWnode->ThreadId = -1;
|
|
|
|
}
|
|
|
|
|
|
if (UseBasePtr) {
|
|
pEvent->MofData = (PVOID) pHeader;
|
|
pEvent->MofLength = pWnode->Size;
|
|
//
|
|
// Override the Timestamp with SystemTime from PERFCounter.
|
|
// If rdtsc is used no conversion is done.
|
|
//
|
|
if (UsePerfClock == EVENT_TRACE_CLOCK_PERFCOUNTER) {
|
|
pPerfHeader->SystemTime = pWnode->TimeStamp;
|
|
}
|
|
}
|
|
else if (pWnode->Size > FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data)) {
|
|
pEvent->MofData = (PVOID) ((char*) pHeader +
|
|
FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data));
|
|
pEvent->MofLength = pWnode->Size - FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
|
|
}
|
|
pEvent->Header.FieldTypeFlags = EVENT_TRACE_USE_NOCPUTIME;
|
|
|
|
break;
|
|
}
|
|
case TRACE_HEADER_TYPE_SYSTEM32:
|
|
{
|
|
PSYSTEM_TRACE_HEADER pSystemHeader32;
|
|
pSystemHeader32 = (PSYSTEM_TRACE_HEADER) pHeader;
|
|
nGroupType = pSystemHeader32->Packet.Group << 8;
|
|
if ((nGroupType == EVENT_TRACE_GROUP_PROCESS) &&
|
|
(pSystemHeader32->Packet.Type == EVENT_TRACE_TYPE_LOAD)) {
|
|
nGroupType += pSystemHeader32->Packet.Type;
|
|
}
|
|
RtlZeroMemory(pEvent, sizeof(EVENT_TRACE));
|
|
pWnode = (PEVENT_TRACE_HEADER) &pEvent->Header;
|
|
pGuid = WmipGroupTypeToGuid(nGroupType);
|
|
if (pGuid != NULL)
|
|
RtlCopyMemory(&pWnode->Guid, pGuid, sizeof(GUID));
|
|
pWnode->Size = pSystemHeader32->Packet.Size;
|
|
pWnode->ThreadId = pSystemHeader32->ThreadId;
|
|
pWnode->ProcessId = pSystemHeader32->ProcessId;
|
|
pWnode->KernelTime = pSystemHeader32->KernelTime;
|
|
pWnode->UserTime = pSystemHeader32->UserTime;
|
|
pWnode->Class.Type = pSystemHeader32->Packet.Type;
|
|
pWnode->Class.Version = pSystemHeader32->Version;
|
|
|
|
WmipCalculateCurrentTime( &pWnode->TimeStamp,
|
|
&pSystemHeader32->SystemTime,
|
|
pContext );
|
|
|
|
if (UseBasePtr) {
|
|
pEvent->MofData = (PVOID) pHeader;
|
|
pEvent->MofLength = pWnode->Size;
|
|
if (UsePerfClock == EVENT_TRACE_CLOCK_PERFCOUNTER) {
|
|
pSystemHeader32->SystemTime = pWnode->TimeStamp;
|
|
}
|
|
}
|
|
else {
|
|
pWnode->FieldTypeFlags = 0;
|
|
if (pWnode->Size > sizeof(SYSTEM_TRACE_HEADER)) {
|
|
pEvent->MofData = (PVOID) ((char*) pHeader +
|
|
sizeof(SYSTEM_TRACE_HEADER));
|
|
pEvent->MofLength = pWnode->Size - sizeof(SYSTEM_TRACE_HEADER);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case TRACE_HEADER_TYPE_SYSTEM64:
|
|
{
|
|
PSYSTEM_TRACE_HEADER pSystemHeader64;
|
|
pSystemHeader64 = (PSYSTEM_TRACE_HEADER) pHeader;
|
|
|
|
nGroupType = pSystemHeader64->Packet.Group << 8;
|
|
if ((nGroupType == EVENT_TRACE_GROUP_PROCESS) &&
|
|
(pSystemHeader64->Packet.Type == EVENT_TRACE_TYPE_LOAD)) {
|
|
nGroupType += pSystemHeader64->Packet.Type;
|
|
}
|
|
RtlZeroMemory(pEvent, sizeof(EVENT_TRACE));
|
|
pWnode = (PEVENT_TRACE_HEADER) &pEvent->Header;
|
|
pGuid = WmipGroupTypeToGuid(nGroupType);
|
|
if (pGuid != NULL)
|
|
RtlCopyMemory(&pWnode->Guid, pGuid, sizeof(GUID));
|
|
pWnode->Size = pSystemHeader64->Packet.Size;
|
|
pWnode->ThreadId = pSystemHeader64->ThreadId;
|
|
pWnode->ProcessId = pSystemHeader64->ProcessId;
|
|
pWnode->KernelTime = pSystemHeader64->KernelTime;
|
|
pWnode->UserTime = pSystemHeader64->UserTime;
|
|
pWnode->Class.Type = pSystemHeader64->Packet.Type;
|
|
pWnode->Class.Version = pSystemHeader64->Version;
|
|
|
|
WmipCalculateCurrentTime( &pWnode->TimeStamp,
|
|
&pSystemHeader64->SystemTime,
|
|
pContext );
|
|
|
|
if (UseBasePtr) {
|
|
pEvent->MofData = (PVOID) pHeader;
|
|
pEvent->MofLength = pWnode->Size;
|
|
if (UsePerfClock == EVENT_TRACE_CLOCK_PERFCOUNTER) {
|
|
pSystemHeader64->SystemTime = pWnode->TimeStamp;
|
|
}
|
|
}
|
|
else {
|
|
pWnode->FieldTypeFlags = 0;
|
|
if (pWnode->Size > sizeof(SYSTEM_TRACE_HEADER)) {
|
|
|
|
pEvent->MofData = (PVOID) ((char*) pHeader +
|
|
sizeof(SYSTEM_TRACE_HEADER));
|
|
pEvent->MofLength = pWnode->Size - sizeof(SYSTEM_TRACE_HEADER);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case TRACE_HEADER_TYPE_FULL_HEADER:
|
|
{
|
|
pWnodeHeader = (PEVENT_TRACE_HEADER) pHeader;
|
|
RtlZeroMemory(pEvent, sizeof(EVENT_TRACE));
|
|
pWnode = (PEVENT_TRACE_HEADER) &pEvent->Header;
|
|
RtlCopyMemory(pWnode,
|
|
pWnodeHeader,
|
|
sizeof(EVENT_TRACE_HEADER)
|
|
);
|
|
WmipCalculateCurrentTime( &pWnode->TimeStamp,
|
|
&pWnodeHeader->TimeStamp,
|
|
pContext );
|
|
|
|
if (UseBasePtr) {
|
|
pEvent->Header.Size = pWnodeHeader->Size;
|
|
pEvent->MofData = (PVOID)pHeader;
|
|
pEvent->MofLength = pWnodeHeader->Size;
|
|
if (UsePerfClock == EVENT_TRACE_CLOCK_PERFCOUNTER) {
|
|
pWnodeHeader->TimeStamp = pWnode->TimeStamp;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// If the data came from Process Private Logger, then
|
|
// mark the ProcessorTime field as valid
|
|
//
|
|
pEvent->Header.FieldTypeFlags = (PrivateLogger) ? EVENT_TRACE_USE_PROCTIME : 0;
|
|
|
|
if (pWnodeHeader->Size > sizeof(EVENT_TRACE_HEADER)) {
|
|
|
|
pEvent->MofData = (PVOID) ((char*)pWnodeHeader +
|
|
sizeof(EVENT_TRACE_HEADER));
|
|
pEvent->MofLength = pWnodeHeader->Size -
|
|
sizeof(EVENT_TRACE_HEADER);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case TRACE_HEADER_TYPE_INSTANCE:
|
|
{
|
|
PEVENT_INSTANCE_HEADER pInstanceHeader;
|
|
pInstanceHeader = (PEVENT_INSTANCE_HEADER) pHeader;
|
|
RtlZeroMemory(pEvent, sizeof(EVENT_TRACE));
|
|
pWnode = (PEVENT_TRACE_HEADER) &pEvent->Header;
|
|
RtlCopyMemory(pWnode,
|
|
pInstanceHeader,
|
|
sizeof(EVENT_INSTANCE_HEADER)
|
|
);
|
|
WmipCalculateCurrentTime( &pWnode->TimeStamp,
|
|
&pInstanceHeader->TimeStamp,
|
|
pContext );
|
|
|
|
pEvent->InstanceId = pInstanceHeader->InstanceId;
|
|
pEvent->ParentInstanceId = pInstanceHeader->ParentInstanceId;
|
|
|
|
pGuid = WmipGuidMapHandleToGuid(&pContext->GuidMapListHead, pInstanceHeader->RegHandle);
|
|
if (pGuid != NULL) {
|
|
pEvent->Header.Guid = *pGuid;
|
|
}
|
|
else {
|
|
RtlZeroMemory(&pEvent->Header.Guid, sizeof(GUID));
|
|
}
|
|
|
|
if (pInstanceHeader->ParentRegHandle != (ULONGLONG)0) {
|
|
pGuid = WmipGuidMapHandleToGuid(
|
|
&pContext->GuidMapListHead,
|
|
pInstanceHeader->ParentRegHandle);
|
|
if (pGuid != NULL) {
|
|
pEvent->ParentGuid = *pGuid;
|
|
}
|
|
#ifdef DBG
|
|
else {
|
|
WmipAssert(pGuid != NULL);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
if (UseBasePtr) {
|
|
pEvent->Header.Size = pInstanceHeader->Size;
|
|
pEvent->MofData = (PVOID)pHeader;
|
|
pEvent->MofLength = pInstanceHeader->Size;
|
|
if (UsePerfClock == EVENT_TRACE_CLOCK_PERFCOUNTER) {
|
|
pInstanceHeader->TimeStamp = pWnode->TimeStamp;
|
|
}
|
|
}
|
|
else {
|
|
pEvent->Header.FieldTypeFlags = (PrivateLogger) ? EVENT_TRACE_USE_PROCTIME : 0;
|
|
if (pInstanceHeader->Size > sizeof(EVENT_INSTANCE_HEADER)) {
|
|
|
|
pEvent->MofData = (PVOID) ((char*)pInstanceHeader +
|
|
sizeof(EVENT_INSTANCE_HEADER));
|
|
pEvent->MofLength = pInstanceHeader->Size -
|
|
sizeof(EVENT_INSTANCE_HEADER);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case TRACE_HEADER_TYPE_TIMED:
|
|
{
|
|
PTIMED_TRACE_HEADER pTimedHeader;
|
|
pTimedHeader = (PTIMED_TRACE_HEADER) pHeader;
|
|
|
|
RtlZeroMemory(pEvent, sizeof(EVENT_TRACE));
|
|
pWnode = (PEVENT_TRACE_HEADER) &pEvent->Header;
|
|
pWnode->Size = pTimedHeader->Size;
|
|
pWnode->Version = pTimedHeader->EventId;
|
|
WmipCalculateCurrentTime( &pWnode->TimeStamp,
|
|
&pTimedHeader->TimeStamp,
|
|
pContext );
|
|
|
|
pWnode->ThreadId = -1;
|
|
pWnode->ProcessId = -1;
|
|
|
|
if (UseBasePtr) {
|
|
pEvent->MofData = (PVOID)pHeader;
|
|
pEvent->MofLength = pTimedHeader->Size;
|
|
if (UsePerfClock == EVENT_TRACE_CLOCK_PERFCOUNTER) {
|
|
pTimedHeader->TimeStamp = pWnode->TimeStamp;
|
|
}
|
|
}
|
|
else if (pWnode->Size > sizeof(TIMED_TRACE_HEADER)) {
|
|
|
|
pEvent->MofData = (PVOID) ((char*) pHeader +
|
|
sizeof(TIMED_TRACE_HEADER));
|
|
pEvent->MofLength = pWnode->Size - sizeof(TIMED_TRACE_HEADER);
|
|
}
|
|
pEvent->Header.FieldTypeFlags = EVENT_TRACE_USE_NOCPUTIME;
|
|
break;
|
|
}
|
|
case TRACE_HEADER_TYPE_WNODE_HEADER:
|
|
{
|
|
PWNODE_HEADER pWnode = (PWNODE_HEADER) pHeader;
|
|
RtlZeroMemory(pEvent, sizeof(EVENT_TRACE));
|
|
RtlCopyMemory(&pEvent->Header, pWnode, sizeof(WNODE_HEADER));
|
|
pEvent->MofData = (PVOID) pWnode;
|
|
pEvent->MofLength = pWnode->BufferSize;
|
|
break;
|
|
}
|
|
case TRACE_HEADER_TYPE_MESSAGE:
|
|
{
|
|
PMESSAGE_TRACE pMsg = (PMESSAGE_TRACE) pHeader ;
|
|
RtlZeroMemory(pEvent, sizeof(EVENT_TRACE));
|
|
RtlCopyMemory(&pEvent->Header, pMsg, sizeof(MESSAGE_TRACE_HEADER)) ;
|
|
if (UseBasePtr) {
|
|
pEvent->MofData = (PVOID)pHeader;
|
|
pEvent->MofLength = pMsg->MessageHeader.Size;
|
|
}
|
|
else {
|
|
pEvent->MofData = (PVOID)&(pMsg->Data) ;
|
|
pEvent->MofLength = pMsg->MessageHeader.Size - sizeof(MESSAGE_TRACE_HEADER) ;
|
|
}
|
|
break;
|
|
}
|
|
default: // Assumed to be REAL WNODE
|
|
break;
|
|
}
|
|
|
|
return WmipSetDosError(ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
WmipGetNextEventOffsetType(
|
|
PUCHAR pBuffer,
|
|
ULONG Offset,
|
|
PULONG RetSize
|
|
)
|
|
{
|
|
ULONG nSize;
|
|
ULONG TraceMarker;
|
|
ULONG TraceType = 0;
|
|
|
|
|
|
PWMI_BUFFER_HEADER Header = (PWMI_BUFFER_HEADER)pBuffer;
|
|
|
|
ULONG Alignment = Header->ClientContext.Alignment;
|
|
ULONG BufferSize = Header->Wnode.BufferSize;
|
|
|
|
|
|
*RetSize = 0;
|
|
|
|
//
|
|
// Check for end of buffer (w/o End of Buffer Marker case...)
|
|
//
|
|
if ( Offset >= (BufferSize - sizeof(long)) ){
|
|
return 0;
|
|
}
|
|
|
|
TraceMarker = *((PULONG)(pBuffer + Offset));
|
|
|
|
if (TraceMarker == 0xFFFFFFFF) {
|
|
return 0;
|
|
}
|
|
|
|
if (TraceMarker & TRACE_HEADER_FLAG) {
|
|
//
|
|
// If the first bit is set, then it is either TRACE or PERF record.
|
|
//
|
|
if (TraceMarker & TRACE_HEADER_EVENT_TRACE) { // One of Ours.
|
|
TraceType = (TraceMarker & TRACE_HEADER_ENUM_MASK) >> 16;
|
|
switch(TraceType) {
|
|
//
|
|
// ISSUE: Need to split the two so we can process cross platform.
|
|
// shsiao 03/22/2000
|
|
//
|
|
case TRACE_HEADER_TYPE_PERFINFO32:
|
|
case TRACE_HEADER_TYPE_PERFINFO64:
|
|
{
|
|
PUSHORT Size;
|
|
Size = (PUSHORT) (pBuffer + Offset + sizeof(ULONG));
|
|
nSize = *Size;
|
|
break;
|
|
}
|
|
case TRACE_HEADER_TYPE_SYSTEM32:
|
|
case TRACE_HEADER_TYPE_SYSTEM64:
|
|
{
|
|
PUSHORT Size;
|
|
Size = (PUSHORT) (pBuffer + Offset + sizeof(ULONG));
|
|
nSize = *Size;
|
|
break;
|
|
}
|
|
case TRACE_HEADER_TYPE_FULL_HEADER:
|
|
case TRACE_HEADER_TYPE_INSTANCE:
|
|
{
|
|
PUSHORT Size;
|
|
Size = (PUSHORT)(pBuffer + Offset);
|
|
nSize = *Size;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
else if ((TraceMarker & TRACE_HEADER_ULONG32_TIME) ==
|
|
TRACE_HEADER_ULONG32_TIME) {
|
|
PUSHORT Size;
|
|
Size = (PUSHORT) (pBuffer + Offset);
|
|
nSize = *Size;
|
|
TraceType = TRACE_HEADER_TYPE_TIMED;
|
|
}
|
|
else if ((TraceMarker & TRACE_HEADER_ULONG32) ==
|
|
TRACE_HEADER_ULONG32) {
|
|
PUSHORT Size;
|
|
Size = (PUSHORT) (pBuffer + Offset);
|
|
nSize = *Size;
|
|
TraceType = TRACE_HEADER_TYPE_ULONG32;
|
|
}
|
|
else if ((TraceMarker & TRACE_MESSAGE) ==
|
|
TRACE_MESSAGE) {
|
|
PUSHORT Size;
|
|
Size = (PUSHORT) (pBuffer + Offset) ;
|
|
nSize = *Size;
|
|
TraceType = TRACE_HEADER_TYPE_MESSAGE;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
else { // Must be WNODE_HEADER
|
|
PUSHORT Size;
|
|
Size = (PUSHORT) (pBuffer + Offset);
|
|
nSize = *Size;
|
|
TraceType = TRACE_HEADER_TYPE_WNODE_HEADER;
|
|
}
|
|
//
|
|
// Check for End Of Buffer Marker
|
|
//
|
|
if (nSize == 0xFFFFFFFF) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Check for larger than BufferSize
|
|
//
|
|
|
|
if (nSize >= BufferSize) {
|
|
return 0;
|
|
}
|
|
if (Alignment != 0) {
|
|
nSize = (ULONG) ALIGN_TO_POWER2(nSize, Alignment);
|
|
}
|
|
|
|
*RetSize = nSize;
|
|
|
|
return TraceType;
|
|
}
|
|
|
|
|
|
ULONG
|
|
WmipReadGuidMapRecords(
|
|
PEVENT_TRACE_LOGFILEW logfile,
|
|
PVOID pBuffer,
|
|
BOOLEAN bLogFileHeader
|
|
)
|
|
{
|
|
PEVENT_TRACE pEvent;
|
|
EVENT_TRACE EventTrace;
|
|
ULONG BufferSize;
|
|
ULONG Status;
|
|
WMI_HEADER_TYPE HeaderType = WMIHT_NONE;
|
|
ULONG Size;
|
|
ULONG Offset;
|
|
PTRACELOG_CONTEXT pContext = logfile->Context;
|
|
|
|
Offset = sizeof(WMI_BUFFER_HEADER);
|
|
|
|
while (TRUE) {
|
|
pEvent = &EventTrace;
|
|
RtlZeroMemory(pEvent, sizeof(EVENT_TRACE) );
|
|
HeaderType = WmiGetTraceHeader(pBuffer, Offset, &Size);
|
|
if ( (HeaderType == WMIHT_NONE) ||
|
|
(HeaderType == WMIHT_WNODE) ||
|
|
(Size == 0)
|
|
) {
|
|
break;
|
|
}
|
|
Status = WmipParseTraceEvent(pContext, pBuffer, Offset, HeaderType, pEvent, sizeof(EVENT_TRACE));
|
|
Offset += Size;
|
|
|
|
if (IsEqualGUID(&pEvent->Header.Guid, &EventTraceGuid)
|
|
&& (pEvent->Header.Class.Type == EVENT_TRACE_TYPE_GUIDMAP))
|
|
{
|
|
WmipGuidMapCallback(&pContext->GuidMapListHead, pEvent);
|
|
//
|
|
// If we are processing the events in raw base pointer mode,
|
|
// we fire callbacks for guid maps also. Note that only the
|
|
// GuidMaps at the start of the file are triggered. The ones at the
|
|
// end are ignored. This is because the time order needs to be
|
|
// preserved when firing callbacks to the user.
|
|
//
|
|
if (bLogFileHeader && (pContext->ConversionFlags & EVENT_TRACE_GET_RAWEVENT)) {
|
|
Status = WmipDoEventCallbacks( logfile, pEvent);
|
|
if (Status != ERROR_SUCCESS) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
else {
|
|
if (bLogFileHeader) {
|
|
Status = WmipDoEventCallbacks( logfile, pEvent);
|
|
if (Status != ERROR_SUCCESS) {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
ULONG
|
|
WmipProcessGuidMaps(
|
|
PEVENT_TRACE_LOGFILEW *Logfiles,
|
|
ULONG LogfileCount,
|
|
ULONG Unicode
|
|
)
|
|
{
|
|
long i;
|
|
NTSTATUS Status;
|
|
PTRACELOG_CONTEXT pContext;
|
|
PEVENT_TRACE_LOGFILEW logfile;
|
|
ULONG BuffersWritten;
|
|
ULONG BufferSize, nBytesRead;
|
|
ULONGLONG SizeWritten, ReadPosition;
|
|
PVOID pBuffer;
|
|
|
|
for (i=0; i<(long)LogfileCount; i++) {
|
|
|
|
logfile = Logfiles[i];
|
|
if (Logfiles[i]->LogFileMode & EVENT_TRACE_REAL_TIME_MODE) {
|
|
continue;
|
|
}
|
|
|
|
if (logfile->IsKernelTrace) {
|
|
continue;
|
|
}
|
|
|
|
pContext = (PTRACELOG_CONTEXT) logfile->Context;
|
|
if (pContext == NULL) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// We now start reading the GuidMaps at the end of file.
|
|
//
|
|
if (!(Logfiles[i]->LogFileMode & EVENT_TRACE_FILE_MODE_CIRCULAR))
|
|
{
|
|
pContext->fGuidMapRead = FALSE;
|
|
}
|
|
|
|
BuffersWritten = logfile->LogfileHeader.BuffersWritten;
|
|
BufferSize = pContext->BufferSize;
|
|
SizeWritten = BuffersWritten * BufferSize;
|
|
|
|
if (Logfiles[i]->LogFileMode & EVENT_TRACE_FILE_MODE_CIRCULAR) {
|
|
ULONGLONG maxFileSize = (logfile->LogfileHeader.MaximumFileSize
|
|
* 1024 * 1024);
|
|
if ( (maxFileSize > 0) && (SizeWritten > maxFileSize) ) {
|
|
SizeWritten = maxFileSize;
|
|
}
|
|
}
|
|
|
|
pBuffer = WmipAlloc(BufferSize);
|
|
if (pBuffer == NULL) {
|
|
return WmipSetDosError(ERROR_OUTOFMEMORY);
|
|
}
|
|
|
|
|
|
RtlZeroMemory(pBuffer, BufferSize);
|
|
|
|
ReadPosition = SizeWritten;
|
|
while (TRUE) {
|
|
if (!GetOverlappedResult(pContext->Handle, &pContext->AsynchRead, &nBytesRead, TRUE) &&
|
|
GetLastError() != ERROR_HANDLE_EOF) {
|
|
WmipDebugPrint(("GetOverlappedResult failed with Status %d in ProcessGuidMaps\n", GetLastError()));
|
|
break;
|
|
}
|
|
pContext->AsynchRead.Offset = (DWORD)(ReadPosition & 0xFFFFFFFF);
|
|
pContext->AsynchRead.OffsetHigh = (DWORD)(ReadPosition >> 32);
|
|
Status = WmipSynchReadFile(pContext->Handle,
|
|
(LPVOID)pBuffer,
|
|
BufferSize,
|
|
&nBytesRead,
|
|
&pContext->AsynchRead);
|
|
if (nBytesRead == 0) {
|
|
break;
|
|
}
|
|
Status = WmipReadGuidMapRecords(Logfiles[i], pBuffer, FALSE);
|
|
if (Status != ERROR_SUCCESS) {
|
|
break;
|
|
// WmipFree(pBuffer);
|
|
// return Status;
|
|
}
|
|
ReadPosition += BufferSize;
|
|
}
|
|
|
|
//
|
|
// End of File was reached. Now set the File Pointer back to
|
|
// the top of the file and process it.
|
|
|
|
pContext->StartBuffer = 0;
|
|
ReadPosition = 0;
|
|
while (TRUE) {
|
|
BOOLEAN bLogFileHeader;
|
|
if (!GetOverlappedResult(pContext->Handle, &pContext->AsynchRead, &nBytesRead, TRUE) &&
|
|
GetLastError() != ERROR_HANDLE_EOF) {
|
|
WmipDebugPrint(("GetOverlappedResult failed with Status %d in ProcessGuidMaps\n", GetLastError()));
|
|
break;
|
|
}
|
|
pContext->AsynchRead.Offset = (DWORD)(ReadPosition & 0xFFFFFFFF);
|
|
pContext->AsynchRead.OffsetHigh = (DWORD)(ReadPosition >> 32);
|
|
Status = WmipSynchReadFile(pContext->Handle,
|
|
(LPVOID)pBuffer,
|
|
BufferSize,
|
|
&nBytesRead,
|
|
&pContext->AsynchRead);
|
|
if (nBytesRead == 0) {
|
|
break;
|
|
}
|
|
bLogFileHeader = (pContext->StartBuffer == 0);
|
|
Status = WmipReadGuidMapRecords(Logfiles[i], pBuffer, bLogFileHeader );
|
|
if (Status != ERROR_SUCCESS){
|
|
break;
|
|
}
|
|
pContext->StartBuffer++;
|
|
ReadPosition += BufferSize;
|
|
}
|
|
|
|
WmipFree(pBuffer);
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
ULONG
|
|
WmipGetBuffersWrittenFromQuery(
|
|
LPWSTR LoggerName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine returns the number of buffers written by querying a logger.
|
|
In case of an array of LogFiles, this routine should be called individually for
|
|
each one.
|
|
|
|
Arguments:
|
|
LogFile - pointer to EVENT_TRACE_LOGFILEW under consideration
|
|
Unicode - whether the logger name is in unicode or not
|
|
|
|
Returned Value:
|
|
|
|
The number of buffers written.
|
|
|
|
--*/
|
|
{
|
|
TRACEHANDLE LoggerHandle = 0;
|
|
ULONG Status;
|
|
RtlZeroMemory(&Properties, sizeof(Properties));
|
|
Properties.TraceProp.Wnode.BufferSize = sizeof(Properties);
|
|
|
|
Status = ControlTraceW(LoggerHandle,
|
|
LoggerName,
|
|
&Properties.TraceProp,
|
|
EVENT_TRACE_CONTROL_QUERY);
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
return Properties.TraceProp.BuffersWritten;
|
|
}
|
|
else {
|
|
SetLastError(Status);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
WmipCopyLogHeader (
|
|
IN PTRACE_LOGFILE_HEADER pOutHeader,
|
|
IN PVOID MofData,
|
|
IN ULONG MofLength,
|
|
IN PWCHAR *LoggerName,
|
|
IN PWCHAR *LogFileName,
|
|
IN ULONG Unicode
|
|
)
|
|
{
|
|
PUCHAR Src, Dest;
|
|
|
|
PTRACE_LOGFILE_HEADER pInHeader;
|
|
ULONG PointerSize;
|
|
ULONG SizeToCopy;
|
|
ULONG Offset;
|
|
|
|
pInHeader = (PTRACE_LOGFILE_HEADER) MofData;
|
|
PointerSize = pInHeader->PointerSize; // This is the PointerSize in File
|
|
|
|
if ( (PointerSize != 4) && (PointerSize != 8) ) {
|
|
#ifdef DBG
|
|
WmipDebugPrint(("WMI: Invalid PointerSize in File %d\n",PointerSize));
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We have Two pointers (LPWSTR) in the middle of the LOGFILE_HEADER
|
|
// structure. So We copy upto the Pointer Fields first, skip over
|
|
// the pointers and copy the remaining stuff. We come back and fixup
|
|
// the pointers appropriately.
|
|
//
|
|
|
|
SizeToCopy = FIELD_OFFSET(TRACE_LOGFILE_HEADER, LoggerName);
|
|
|
|
RtlCopyMemory(pOutHeader, pInHeader, SizeToCopy);
|
|
|
|
//
|
|
// Skip over the Troublesome pointers in both Src and Dest
|
|
//
|
|
|
|
Dest = (PUCHAR)pOutHeader + SizeToCopy + 2 * sizeof(LPWSTR);
|
|
|
|
Src = (PUCHAR)pInHeader + SizeToCopy + 2 * PointerSize;
|
|
|
|
//
|
|
// Copy the Remaining fields at the tail end of the LOGFILE_HEADER
|
|
//
|
|
|
|
SizeToCopy = sizeof(TRACE_LOGFILE_HEADER) -
|
|
FIELD_OFFSET(TRACE_LOGFILE_HEADER, TimeZone);
|
|
|
|
RtlCopyMemory(Dest, Src, SizeToCopy);
|
|
|
|
//
|
|
// Adjust the pointer fields now
|
|
//
|
|
Offset = sizeof(TRACE_LOGFILE_HEADER) -
|
|
2 * sizeof(LPWSTR) +
|
|
2 * PointerSize;
|
|
|
|
*LoggerName = (PWCHAR) ((PUCHAR)pInHeader + Offset);
|
|
pOutHeader->LoggerName = *LoggerName;
|
|
|
|
}
|
|
|
|
ULONG
|
|
WmipProcessLogHeader(
|
|
PTRACEHANDLE HandleArray,
|
|
PEVENT_TRACE_LOGFILEW *Logfiles,
|
|
ULONG LogfileCount,
|
|
ULONG Unicode,
|
|
ULONG bFree
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine processes the header of an array of logfiles.
|
|
|
|
Arguments:
|
|
|
|
LogFile Array of Logfiles being processed.
|
|
LogFileCount Number of Logfiles in the Array.
|
|
Unicode Unicode Flag.
|
|
|
|
Returned Value:
|
|
|
|
Status Code.
|
|
|
|
--*/
|
|
{
|
|
HANDLE hFile;
|
|
PTRACELOG_CONTEXT pContext = NULL;
|
|
PVOID pBuffer;
|
|
PEVENT_TRACE pEvent;
|
|
long i;
|
|
WMI_HEADER_TYPE HeaderType = WMIHT_NONE;
|
|
ULONG Size;
|
|
ULONG Offset;
|
|
LPWSTR loggerName, logFileName;
|
|
ULONG BufferSize, nBytesRead;
|
|
PTRACE_LOGFILE_HEADER logfileHeader;
|
|
ULONG Status = ERROR_SUCCESS;
|
|
|
|
|
|
//
|
|
// Open the Log File for shared Read
|
|
//
|
|
BufferSize = DEFAULT_LOG_BUFFER_SIZE; // Log file header must be smaller than 1K
|
|
|
|
pBuffer = WmipAlloc(BufferSize);
|
|
if (pBuffer == NULL) {
|
|
return WmipSetDosError(ERROR_OUTOFMEMORY);
|
|
}
|
|
|
|
|
|
for (i=0; i<(long)LogfileCount; i++) {
|
|
EVENT_TRACE EventTrace;
|
|
ULONG SavedConversionFlags;
|
|
OVERLAPPED LogHeaderOverlapped;
|
|
//
|
|
// Caller can pass in Flags to fetch the timestamps in raw mode.
|
|
// Since LogFileHeader gets overwritten from with data from the logfile
|
|
// we need to save the passed in value here.
|
|
//
|
|
|
|
SavedConversionFlags = Logfiles[i]->LogfileHeader.ReservedFlags;
|
|
if (Unicode) {
|
|
hFile = CreateFileW(
|
|
(LPWSTR) Logfiles[i]->LogFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
|
NULL
|
|
);
|
|
}
|
|
else {
|
|
hFile = CreateFileA(
|
|
(LPSTR) Logfiles[i]->LogFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
Status = WmipSetDosError(ERROR_BAD_PATHNAME);
|
|
break;
|
|
}
|
|
|
|
BufferSize = DEFAULT_LOG_BUFFER_SIZE;
|
|
RtlZeroMemory(pBuffer, BufferSize);
|
|
|
|
LogHeaderOverlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
|
|
if (LogHeaderOverlapped.hEvent == NULL) {
|
|
// cannot create event for file read
|
|
break;
|
|
}
|
|
LogHeaderOverlapped.Offset = 0;
|
|
LogHeaderOverlapped.OffsetHigh = 0;
|
|
Status = WmipSynchReadFile(hFile,
|
|
(LPVOID)pBuffer,
|
|
BufferSize,
|
|
&nBytesRead,
|
|
&LogHeaderOverlapped);
|
|
if (nBytesRead == 0) {
|
|
NtClose(hFile);
|
|
Status = WmipSetDosError(ERROR_FILE_CORRUPT);
|
|
break;
|
|
}
|
|
CloseHandle(LogHeaderOverlapped.hEvent);
|
|
|
|
Offset = sizeof(WMI_BUFFER_HEADER);
|
|
|
|
pEvent = &EventTrace;
|
|
RtlZeroMemory(pEvent, sizeof(EVENT_TRACE) );
|
|
HeaderType = WmiGetTraceHeader(pBuffer, Offset, &Size);
|
|
if ( (HeaderType == WMIHT_NONE) ||
|
|
(HeaderType == WMIHT_WNODE) ||
|
|
(Size == 0)
|
|
) {
|
|
NtClose(hFile);
|
|
Status = WmipSetDosError(ERROR_FILE_CORRUPT);
|
|
break;
|
|
}
|
|
Status = WmipParseTraceEvent(NULL, pBuffer, Offset, HeaderType, pEvent, sizeof(EVENT_TRACE));
|
|
|
|
//
|
|
// Set up the header structure properly
|
|
//
|
|
if ((Status == ERROR_SUCCESS) && (pEvent->MofLength > 0)) {
|
|
ULONG PointerSize;
|
|
|
|
logfileHeader = &Logfiles[i]->LogfileHeader;
|
|
|
|
//
|
|
// We are relying on the fact that the PointerSize field
|
|
// will not shift between platforms.
|
|
//
|
|
|
|
PointerSize = ((PTRACE_LOGFILE_HEADER)(pEvent->MofData))->PointerSize;
|
|
|
|
if (PointerSize == sizeof(PUCHAR) ) {
|
|
|
|
RtlCopyMemory(&Logfiles[i]->LogfileHeader, pEvent->MofData,
|
|
sizeof(TRACE_LOGFILE_HEADER));
|
|
|
|
loggerName = (LPWSTR) ( (char*)pEvent->MofData +
|
|
sizeof(TRACE_LOGFILE_HEADER) );
|
|
|
|
// logFileName = (LPWSTR) ( (char*)pEvent->MofData +
|
|
// sizeof(TRACE_LOGFILE_HEADER) +
|
|
// sizeof(WCHAR)* wcslen(loggerName));
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Ugly thunking going on here. Close your eyes...
|
|
//
|
|
|
|
WmipCopyLogHeader(&Logfiles[i]->LogfileHeader,
|
|
pEvent->MofData,
|
|
pEvent->MofLength,
|
|
&loggerName,
|
|
&logFileName,
|
|
Unicode);
|
|
pEvent->MofData = (PVOID)&Logfiles[i]->LogfileHeader;
|
|
}
|
|
}
|
|
else {
|
|
NtClose(hFile);
|
|
Status = WmipSetDosError(ERROR_FILE_CORRUPT);
|
|
break;
|
|
}
|
|
|
|
Logfiles[i]->IsKernelTrace = !wcscmp(loggerName, KERNEL_LOGGER_CAPTION);
|
|
|
|
Logfiles[i]->LogFileMode = (logfileHeader->LogFileMode &
|
|
~(EVENT_TRACE_REAL_TIME_MODE));
|
|
|
|
#ifdef DBG
|
|
DbgPrint("Dumping Logfile Header\n");
|
|
DbgPrint("\tStart Time %I64u\n",
|
|
pEvent->Header.TimeStamp);
|
|
DbgPrint("\tLogger Thread Id %X\n",
|
|
pEvent->Header.ThreadId);
|
|
DbgPrint("\tHeader Size %d\n",
|
|
pEvent->Header.Size);
|
|
DbgPrint("\tBufferSize %d\n",
|
|
logfileHeader->BufferSize);
|
|
DbgPrint("\tVersion %d\n",
|
|
logfileHeader->Version);
|
|
DbgPrint("\tProviderVersion %d\n",
|
|
logfileHeader->ProviderVersion);
|
|
DbgPrint("\tEndTime %I64u\n",
|
|
logfileHeader->EndTime);
|
|
DbgPrint("\tTimer Resolution %d\n",
|
|
logfileHeader->TimerResolution);
|
|
DbgPrint("\tMaximum File Size %d\n",
|
|
logfileHeader->MaximumFileSize);
|
|
DbgPrint("\tBuffers Written %d\n",
|
|
logfileHeader->BuffersWritten);
|
|
DbgPrint("\tEvents Lost %d\n",
|
|
logfileHeader->EventsLost);
|
|
DbgPrint("\tBuffers Lost %d\n",
|
|
logfileHeader->BuffersLost);
|
|
DbgPrint("\tStart Buffers%d\n",
|
|
logfileHeader->StartBuffers);
|
|
DbgPrint("\tReserved Flags %x\n",
|
|
logfileHeader->ReservedFlags);
|
|
DbgPrint("\tFrequency %I64u\n",
|
|
logfileHeader->PerfFreq.QuadPart);
|
|
DbgPrint("\tLogger Name %ls\n",
|
|
loggerName);
|
|
DbgPrint("\tStartTime %I64u\n",
|
|
logfileHeader->StartTime.QuadPart);
|
|
// DbgPrint("\tLogfile Name %ls\n",
|
|
// logFileName);
|
|
|
|
DbgPrint("\tLogfile Mode %X\n",
|
|
logfileHeader->LogFileMode);
|
|
DbgPrint("\tProcessorCount %d\n",
|
|
logfileHeader->NumberOfProcessors);
|
|
#endif
|
|
if (Logfiles[i]->IsKernelTrace)
|
|
WmipDebugPrint(("\tLogfile contains kernel trace\n"));
|
|
|
|
BufferSize = logfileHeader->BufferSize;
|
|
|
|
WmipAssert(BufferSize > 0);
|
|
|
|
if ( (BufferSize/1024 == 0) ||
|
|
(((BufferSize/1024)*1024) != BufferSize) ) {
|
|
NtClose(hFile);
|
|
Status = WmipSetDosError(ERROR_FILE_CORRUPT);
|
|
break;
|
|
}
|
|
|
|
|
|
if (Logfiles[i]->IsKernelTrace)
|
|
WmipDebugPrint(("\tLogfile contains kernel trace\n"));
|
|
|
|
if (bFree) {
|
|
NtClose(hFile);
|
|
}
|
|
else {
|
|
//
|
|
// At this point, the logfile is opened successfully
|
|
// Initialize the internal context now
|
|
//
|
|
pContext = WmipLookupTraceHandle(HandleArray[i]);
|
|
if (pContext == NULL) {
|
|
NtClose(hFile);
|
|
// TODO: Find an appropriate eerror code here?
|
|
Status = WmipSetDosError(ERROR_OUTOFMEMORY);
|
|
break;
|
|
}
|
|
|
|
Logfiles[i]->Context = pContext;
|
|
pContext->Handle = hFile;
|
|
|
|
|
|
//
|
|
// If the EndTime is 0, then compute the BuffersWritten from
|
|
// FileSize and BufferSize.
|
|
//
|
|
// However, an on-going session with a preallocated log file
|
|
// will use QueryTrace() to get BuffersWritten.
|
|
//
|
|
if (logfileHeader->EndTime.QuadPart == 0) {
|
|
if (logfileHeader->LogFileMode & EVENT_TRACE_FILE_MODE_PREALLOCATE) {
|
|
ULONG QueriedBuffersWritten = WmipGetBuffersWrittenFromQuery(loggerName);
|
|
|
|
if (QueriedBuffersWritten) {
|
|
logfileHeader->BuffersWritten = QueriedBuffersWritten;
|
|
}
|
|
}
|
|
else {
|
|
FILE_STANDARD_INFORMATION FileInfo;
|
|
NTSTATUS NtStatus;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
|
|
NtStatus = NtQueryInformationFile(
|
|
hFile,
|
|
&IoStatus,
|
|
&FileInfo,
|
|
sizeof(FILE_STANDARD_INFORMATION),
|
|
FileStandardInformation
|
|
);
|
|
if (NT_SUCCESS(NtStatus)) {
|
|
ULONG64 FileSize = FileInfo.AllocationSize.QuadPart;
|
|
ULONG64 BuffersWritten = FileSize / (ULONG64) BufferSize;
|
|
logfileHeader->BuffersWritten = (ULONG) BuffersWritten;
|
|
}
|
|
}
|
|
}
|
|
|
|
pContext->BufferCount = logfileHeader->BuffersWritten;
|
|
pContext->BufferSize = logfileHeader->BufferSize;
|
|
pContext->InitialSize = Size;
|
|
|
|
|
|
|
|
//
|
|
// Save the flags from OpenTrace at this time before the first
|
|
// buffer callback which will erase it.
|
|
//
|
|
|
|
pContext->ConversionFlags = SavedConversionFlags;
|
|
|
|
|
|
//
|
|
// Make the header the current Event ...
|
|
// and the callbacks for the header are handled by ProcessTraceLog.
|
|
|
|
pContext->UsePerfClock = logfileHeader->ReservedFlags;
|
|
pContext->StartTime = logfileHeader->StartTime;
|
|
pContext->PerfFreq = logfileHeader->PerfFreq;
|
|
pContext->CpuSpeedInMHz = logfileHeader->CpuSpeedInMHz;
|
|
|
|
//
|
|
// If the conversion flags are set, adjust UsePerfClock accordingly.
|
|
//
|
|
if (pContext->ConversionFlags & EVENT_TRACE_USE_RAWTIMESTAMP) {
|
|
pContext->UsePerfClock = EVENT_TRACE_CLOCK_RAW;
|
|
}
|
|
|
|
if ((pContext->UsePerfClock == EVENT_TRACE_CLOCK_PERFCOUNTER) ||
|
|
(pContext->UsePerfClock == EVENT_TRACE_CLOCK_CPUCYCLE) ) {
|
|
pContext->StartPerfClock = pEvent->Header.TimeStamp;
|
|
Logfiles[i]->CurrentTime = pContext->StartTime.QuadPart;
|
|
pEvent->Header.TimeStamp.QuadPart = pContext->StartTime.QuadPart;
|
|
}
|
|
else {
|
|
Logfiles[i]->CurrentTime = pEvent->Header.TimeStamp.QuadPart;
|
|
}
|
|
}
|
|
}
|
|
|
|
WmipFree(pBuffer);
|
|
return Status;
|
|
}
|
|
|
|
ULONG
|
|
WmipDoEventCallbacks(
|
|
PEVENT_TRACE_LOGFILEW logfile,
|
|
PEVENT_TRACE pEvent
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PEVENT_TRACE_CALLBACK pCallback;
|
|
|
|
//
|
|
// First the Generic Event Callback is called.
|
|
//
|
|
if ( logfile->EventCallback ) {
|
|
try {
|
|
(*logfile->EventCallback)(pEvent);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
#ifdef DBG
|
|
WmipDebugPrint(("TRACE: EventCallback threw exception %X\n",
|
|
Status));
|
|
#endif
|
|
return WmipSetDosError(WmipNtStatusToDosError(Status));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now Call the event specific callback.
|
|
//
|
|
pCallback = WmipGetCallbackRoutine( &pEvent->Header.Guid );
|
|
if ( pCallback != NULL ) {
|
|
try {
|
|
(*pCallback->CallbackRoutine)(pEvent);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
#ifdef DBG
|
|
WmipDebugPrint(("EventCallback %X threw exception %X\n",
|
|
pCallback->CallbackRoutine, Status));
|
|
#endif
|
|
|
|
return WmipSetDosError(WmipNtStatusToDosError(Status));
|
|
}
|
|
}
|
|
logfile->CurrentTime = pEvent->Header.TimeStamp.QuadPart;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
ULONG
|
|
WmipAdvanceToNewEvent(
|
|
PEVENT_TRACE_LOGFILEW logfile,
|
|
BOOL EventInRange
|
|
)
|
|
{
|
|
ULONG Status = ERROR_SUCCESS;
|
|
PEVENT_TRACE pEvent;
|
|
PTRACELOG_CONTEXT pContext;
|
|
PVOID pBuffer;
|
|
PTRACE_BUFFER_LIST_ENTRY Current;
|
|
ULONG Size;
|
|
WMI_HEADER_TYPE HeaderType = WMIHT_NONE;
|
|
|
|
pContext = logfile->Context;
|
|
if (pContext == NULL) {
|
|
return WmipSetDosError(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
Current = WmipRemoveBuffer(&pContext->Root);
|
|
if (Current == NULL) {
|
|
pContext->EndOfFile = TRUE;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Advance Event for current buffer
|
|
//
|
|
pEvent = &Current->Event;
|
|
|
|
//
|
|
// Before we make the callbacks, we need to restore the
|
|
// raw buffer, so that MofData will be pointing to the right data.
|
|
//
|
|
pBuffer = WmipGetCurrentBuffer(pContext, Current);
|
|
if (pBuffer == NULL) {
|
|
//
|
|
// This condition could happen when the file we are reading
|
|
// gets overwritten.
|
|
//
|
|
return ERROR_SHARING_VIOLATION;
|
|
}
|
|
|
|
if (EventInRange) {
|
|
Status = WmipDoEventCallbacks( logfile, pEvent);
|
|
if (Status != ERROR_SUCCESS) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
Size = 0;
|
|
if ((HeaderType = WmiGetTraceHeader(pBuffer, Current->BufferOffset, &Size)) != WMIHT_NONE) {
|
|
if (Size > 0) {
|
|
Status = WmipParseTraceEvent(pContext, pBuffer, Current->BufferOffset, HeaderType, pEvent, sizeof(EVENT_TRACE));
|
|
Current->BufferOffset += Size;
|
|
Current->TraceType = WmipConvertEnumToTraceType(HeaderType);
|
|
}
|
|
}
|
|
Current->EventSize = Size;
|
|
|
|
if ( ( Size > 0) && (Status == ERROR_SUCCESS) ) {
|
|
WmipInsertBuffer(&pContext->Root, Current);
|
|
}
|
|
else {
|
|
DWORD BytesTransffered;
|
|
//
|
|
// When the current buffer is exhausted, make the
|
|
// BufferCallback
|
|
//
|
|
if (logfile->BufferCallback) {
|
|
ULONG bRetVal;
|
|
PWMI_BUFFER_HEADER pHeader = (PWMI_BUFFER_HEADER)pBuffer;
|
|
logfile->Filled = (ULONG)pHeader->Offset;
|
|
logfile->EventsLost = pHeader->EventsLost;
|
|
try {
|
|
bRetVal = (*logfile->BufferCallback) (logfile);
|
|
if (!bRetVal) {
|
|
return ERROR_CANCELLED;
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
pContext->EndOfFile = TRUE;
|
|
Status = GetExceptionCode();
|
|
#ifdef DBG
|
|
WmipDebugPrint(("TRACE: BufferCallback threw exception %X\n",
|
|
Status));
|
|
#endif
|
|
WmipSetDosError(WmipNtStatusToDosError(Status));
|
|
return ERROR_CANCELLED; // so that realtime also cleans up.
|
|
}
|
|
}
|
|
//
|
|
// Issue another asynch read on this buffer cache slot if there are no outstanding reads
|
|
// at this point.
|
|
// GetOverlappedResult() returns FALSE if IO is still pending.
|
|
//
|
|
if (pContext->BufferBeingRead == -1 ||
|
|
GetOverlappedResult(pContext->Handle, &pContext->AsynchRead, &BytesTransffered, FALSE)) {
|
|
|
|
LONG FileOffset = Current->FileOffset + MAX_TRACE_BUFFER_CACHE_SIZE;
|
|
if ((ULONG)FileOffset < pContext->BufferCount) {
|
|
ULONGLONG Offset = FileOffset * pContext->BufferSize;
|
|
ResetEvent(pContext->AsynchRead.hEvent);
|
|
pContext->AsynchRead.Offset = (DWORD)(Offset & 0xFFFFFFFF);
|
|
pContext->AsynchRead.OffsetHigh = (DWORD)(Offset >> 32);
|
|
|
|
Status = ReadFile(pContext->Handle,
|
|
(LPVOID)pBuffer,
|
|
pContext->BufferSize,
|
|
NULL,
|
|
&pContext->AsynchRead);
|
|
if (Status || GetLastError() == ERROR_IO_PENDING) {
|
|
ULONG TableIndex = FileOffset % MAX_TRACE_BUFFER_CACHE_SIZE;
|
|
pContext->BufferBeingRead = FileOffset;
|
|
pContext->BufferCache[TableIndex].Index = FileOffset;
|
|
}
|
|
else { // Issuing asynch IO failed. Not a fatal error. Just continue for now.
|
|
SetEvent(pContext->AsynchRead.hEvent);
|
|
pContext->BufferBeingRead = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// The File reaches end of file when the Root is NULL
|
|
//
|
|
if (pContext->Root == NULL) {
|
|
pContext->EndOfFile = TRUE;
|
|
}
|
|
else {
|
|
logfile->CurrentTime = pContext->Root->Event.Header.TimeStamp.QuadPart;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
ULONG
|
|
WmipBuildEventTable(
|
|
PTRACELOG_CONTEXT pContext
|
|
)
|
|
{
|
|
ULONG i, nBytesRead;
|
|
PVOID pBuffer;
|
|
ULONG BufferSize = pContext->BufferSize;
|
|
PEVENT_TRACE pEvent;
|
|
ULONG TotalBuffersRead;
|
|
NTSTATUS Status;
|
|
ULONGLONG ReadPosition;
|
|
|
|
|
|
//
|
|
// File is already open.
|
|
// Reset the file pointer and continue.
|
|
// TODO: If we start at bottom of file and insert
|
|
// it might be more efficient.
|
|
//
|
|
ReadPosition = pContext->StartBuffer * BufferSize;
|
|
TotalBuffersRead = pContext->StartBuffer;
|
|
|
|
//
|
|
// If there are no other buffers except header and guidmaps, EOF is true
|
|
//
|
|
|
|
if (TotalBuffersRead == pContext->BufferCount) {
|
|
pContext->EndOfFile = TRUE;
|
|
pContext->Root = NULL;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
do {
|
|
WMI_HEADER_TYPE HeaderType = WMIHT_NONE;
|
|
ULONG Size;
|
|
ULONG Offset;
|
|
ULONG TableIndex;
|
|
|
|
TableIndex = TotalBuffersRead % MAX_TRACE_BUFFER_CACHE_SIZE ;
|
|
pBuffer = pContext->BufferCache[TableIndex].Buffer;
|
|
|
|
if (!GetOverlappedResult(pContext->Handle, &pContext->AsynchRead, &nBytesRead, TRUE) &&
|
|
GetLastError() != ERROR_HANDLE_EOF) {
|
|
WmipDebugPrint(("GetOverlappedResult failed with Status %d in BuildEventTable\n", GetLastError()));
|
|
break;
|
|
}
|
|
pContext->AsynchRead.Offset = (DWORD)(ReadPosition & 0xFFFFFFFF);
|
|
pContext->AsynchRead.OffsetHigh = (DWORD)(ReadPosition >> 32);
|
|
|
|
Status = WmipSynchReadFile(pContext->Handle,
|
|
(LPVOID)pBuffer,
|
|
BufferSize,
|
|
&nBytesRead,
|
|
&pContext->AsynchRead);
|
|
|
|
if (nBytesRead == 0)
|
|
break;
|
|
|
|
ReadPosition += BufferSize;
|
|
Offset = sizeof(WMI_BUFFER_HEADER);
|
|
|
|
pEvent = &pContext->BufferList[TotalBuffersRead].Event;
|
|
|
|
HeaderType = WmiGetTraceHeader(pBuffer, Offset, &Size);
|
|
if ( (HeaderType == WMIHT_NONE) || (HeaderType == WMIHT_WNODE) || (Size == 0) ) {
|
|
TotalBuffersRead++;
|
|
continue;
|
|
}
|
|
Status = WmipParseTraceEvent(pContext, pBuffer, Offset, HeaderType, pEvent, sizeof(EVENT_TRACE));
|
|
|
|
//
|
|
// Set up the header structure properly
|
|
//
|
|
if (Status != ERROR_SUCCESS) {
|
|
TotalBuffersRead++;
|
|
continue;
|
|
}
|
|
|
|
Offset += Size;
|
|
pContext->BufferList[TotalBuffersRead].BufferOffset = Offset;
|
|
pContext->BufferList[TotalBuffersRead].FileOffset = TotalBuffersRead;
|
|
pContext->BufferList[TotalBuffersRead].EventSize = Size;
|
|
pContext->BufferList[TotalBuffersRead].TraceType = WmipConvertEnumToTraceType(HeaderType);
|
|
WmipInsertBuffer(&pContext->Root, &pContext->BufferList[TotalBuffersRead]);
|
|
|
|
TotalBuffersRead++;
|
|
if (TotalBuffersRead >= pContext->BufferCount) {
|
|
break;
|
|
}
|
|
} while (1);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
ULONG
|
|
WmipProcessTraceLog(
|
|
PTRACEHANDLE HandleArray,
|
|
PEVENT_TRACE_LOGFILEW *Logfiles,
|
|
ULONG LogfileCount,
|
|
LONGLONG StartTime,
|
|
LONGLONG EndTime,
|
|
ULONG Unicode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine processes an array of traces (from file or realtime input
|
|
stream). If the trace is from a file, goes through each event till the
|
|
end of file, firing event callbacks (if any) along the way. If the trace
|
|
is from realtime, it waits for event notification about buffer delivery
|
|
from the realtime callback and processes the buffer delivered in the
|
|
same way. It handles circular logfiles and windowing of data (with the
|
|
given start and end times) correctly. When more than one trace it
|
|
provides the callback in chronological order.
|
|
|
|
Arguments:
|
|
|
|
Logfiles Array of traces
|
|
LogfileCount Number of traces
|
|
StartTime Starting Time of the window of analysis
|
|
EndTime Ending Time of the window of analysis
|
|
Unicode Unicode Flag.
|
|
|
|
Returned Value:
|
|
|
|
Status Code.
|
|
|
|
--*/
|
|
{
|
|
PEVENT_TRACE_LOGFILE logfile;
|
|
ULONG Status;
|
|
PEVENT_TRACE pEvent;
|
|
PTRACELOG_CONTEXT pContext;
|
|
EVENT_TRACE_PROPERTIES Properties;
|
|
ULONG RealTimeDataFeed, LogFileDataFeed;
|
|
USHORT LoggerId;
|
|
TRACEHANDLE LoggerHandle = 0;
|
|
ULONG i, j;
|
|
BOOL Done = FALSE;
|
|
ACCESS_MASK DesiredAccess = TRACELOG_ACCESS_REALTIME;
|
|
|
|
// ContextListHeadPtr = &ContextListHead;
|
|
// InitializeListHead(ContextListHeadPtr);
|
|
|
|
Status = WmipCreateGuidMapping();
|
|
if (Status != ERROR_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// After reading the First Buffer, determine the BufferSize,
|
|
// Number of Buffers written, filesize, kernel or non-kernel logger
|
|
// Set a flag to strip out the GuidMap at the end.
|
|
//
|
|
|
|
Status = WmipProcessLogHeader( HandleArray, Logfiles, LogfileCount, Unicode, FALSE );
|
|
if (Status != ERROR_SUCCESS) {
|
|
goto Cleanup;
|
|
}
|
|
Status = WmipProcessGuidMaps( Logfiles, LogfileCount, Unicode );
|
|
if (Status != ERROR_SUCCESS) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set up storage
|
|
//
|
|
for (i=0; i < LogfileCount; i++) {
|
|
ULONG BufferSize, BufferCount;
|
|
ULONG SizeNeeded;
|
|
PUCHAR Space;
|
|
PTRACE_BUFFER_LIST_ENTRY Current;
|
|
|
|
|
|
pContext = (PTRACELOG_CONTEXT)Logfiles[i]->Context;
|
|
|
|
BufferSize = pContext->BufferSize;
|
|
BufferCount = pContext->BufferCount;
|
|
|
|
SizeNeeded = BufferCount * sizeof(TRACE_BUFFER_LIST_ENTRY);
|
|
pContext->BufferList = WmipMemCommit( NULL, SizeNeeded );
|
|
|
|
if (pContext->BufferList == NULL) {
|
|
Status = ERROR_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlZeroMemory(pContext->BufferList, SizeNeeded);
|
|
|
|
//
|
|
// Allocate Buffer Cache
|
|
//
|
|
SizeNeeded = MAX_TRACE_BUFFER_CACHE_SIZE * BufferSize;
|
|
Space = WmipMemCommit( NULL, SizeNeeded );
|
|
if (Space == NULL) {
|
|
Status = ERROR_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
for (j=0; j<MAX_TRACE_BUFFER_CACHE_SIZE; j++) {
|
|
pContext->BufferCache[j].Index = -1;
|
|
pContext->BufferCache[j].Buffer = (PVOID)(Space + j * BufferSize);
|
|
}
|
|
pContext->BufferCacheSpace = Space;
|
|
Status = WmipBuildEventTable(pContext);
|
|
if (Status != ERROR_SUCCESS) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
Current = pContext->Root;
|
|
if (Current != NULL) {
|
|
Logfiles[i]->CurrentTime = Current->Event.Header.TimeStamp.QuadPart;
|
|
}
|
|
else {
|
|
pContext->EndOfFile = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make the Second Pass and get the events.
|
|
//
|
|
|
|
#ifdef DBG
|
|
WmipDumpCallbacks();
|
|
#endif
|
|
while (!Done) {
|
|
LONGLONG nextTimeStamp;
|
|
BOOL EventInRange;
|
|
//
|
|
// Check to see if end of file has been reached on all the
|
|
// files.
|
|
//
|
|
|
|
logfile = NULL;
|
|
nextTimeStamp = 0;
|
|
|
|
for (j=0; j < LogfileCount; j++) {
|
|
pContext = (PTRACELOG_CONTEXT)Logfiles[j]->Context;
|
|
|
|
if (pContext->EndOfFile)
|
|
continue;
|
|
if (nextTimeStamp == 0) {
|
|
nextTimeStamp = Logfiles[j]->CurrentTime;
|
|
logfile = Logfiles[j];
|
|
}
|
|
else if (nextTimeStamp > Logfiles[j]->CurrentTime) {
|
|
nextTimeStamp = Logfiles[j]->CurrentTime;
|
|
logfile = Logfiles[j];
|
|
}
|
|
}
|
|
|
|
if (logfile == NULL) {
|
|
break;
|
|
}
|
|
//
|
|
// if the Next event timestamp is not within the window of
|
|
// analysis, we do not fire the event callbacks.
|
|
//
|
|
|
|
EventInRange = TRUE;
|
|
|
|
if ((StartTime != 0) && (StartTime > nextTimeStamp))
|
|
EventInRange = FALSE;
|
|
if ((EndTime != 0) && (EndTime < nextTimeStamp))
|
|
EventInRange = FALSE;
|
|
|
|
//
|
|
// Now advance to next event.
|
|
//
|
|
|
|
Status = WmipAdvanceToNewEvent(logfile, EventInRange);
|
|
Done = (Status == ERROR_CANCELLED);
|
|
}
|
|
Cleanup:
|
|
for (i=0; i < LogfileCount; i++) {
|
|
pContext = (PTRACELOG_CONTEXT)Logfiles[i]->Context;
|
|
if (pContext != NULL) {
|
|
WmipCleanupTraceLog(pContext);
|
|
}
|
|
}
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
ULONG
|
|
WmipCopyLogfileInfo(
|
|
PTRACELOG_CONTEXT HandleEntry,
|
|
PEVENT_TRACE_LOGFILEW Logfile,
|
|
ULONG Unicode
|
|
)
|
|
{
|
|
ULONG bufSize;
|
|
PWCHAR ws;
|
|
//
|
|
// Allocate LogfileName and LoggerName as well
|
|
//
|
|
RtlCopyMemory(&HandleEntry->Logfile,
|
|
Logfile,
|
|
sizeof(EVENT_TRACE_LOGFILEW));
|
|
|
|
HandleEntry->Logfile.LogFileName = NULL;
|
|
HandleEntry->Logfile.LoggerName = NULL;
|
|
|
|
if (Logfile->LogFileName != NULL) {
|
|
if (Unicode)
|
|
bufSize = (wcslen(Logfile->LogFileName) + 1) * sizeof(WCHAR);
|
|
else
|
|
bufSize = (strlen((PUCHAR)(Logfile->LogFileName)) + 1)
|
|
* sizeof(WCHAR);
|
|
|
|
ws = WmipAlloc( bufSize );
|
|
if (ws == NULL)
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
if (Unicode) {
|
|
wcscpy(ws, Logfile->LogFileName);
|
|
}
|
|
else {
|
|
MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
(LPCSTR)Logfile->LogFileName,
|
|
-1,
|
|
(LPWSTR)ws,
|
|
bufSize);
|
|
}
|
|
HandleEntry->Logfile.LogFileName = ws;
|
|
}
|
|
if (Logfile->LoggerName != NULL) {
|
|
if (Unicode)
|
|
bufSize = (wcslen(Logfile->LoggerName) + 1) * sizeof(WCHAR);
|
|
else
|
|
bufSize = (strlen((PUCHAR)(Logfile->LoggerName)) + 1)
|
|
* sizeof(WCHAR);
|
|
|
|
ws = WmipAlloc( bufSize );
|
|
if (ws == NULL)
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
if (Unicode)
|
|
wcscpy(ws, Logfile->LoggerName);
|
|
else {
|
|
MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
(LPCSTR)Logfile->LoggerName,
|
|
-1,
|
|
(LPWSTR)ws,
|
|
bufSize);
|
|
}
|
|
HandleEntry->Logfile.LoggerName = ws;
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
TRACEHANDLE
|
|
WMIAPI
|
|
OpenTraceA(
|
|
IN PEVENT_TRACE_LOGFILEA Logfile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This is the Ansi version of the ProcessTracelogHeader routine.
|
|
|
|
|
|
Arguments:
|
|
|
|
LogFile Trace Input
|
|
|
|
|
|
|
|
Returned Value:
|
|
|
|
TraceHandle
|
|
|
|
--*/
|
|
{
|
|
ULONG status = ERROR_INVALID_PARAMETER;
|
|
PTRACELOG_CONTEXT HandleEntry = NULL;
|
|
TRACEHANDLE TraceHandle = (TRACEHANDLE)INVALID_HANDLE_VALUE;
|
|
|
|
WmipInitProcessHeap();
|
|
|
|
if (Logfile != NULL) {
|
|
HandleEntry = WmipAllocateTraceHandle();
|
|
if (HandleEntry == NULL) {
|
|
status = ERROR_OUTOFMEMORY;
|
|
}
|
|
else {
|
|
//
|
|
// Copy the LogFileStructure over. Converts strings to Unicode
|
|
//
|
|
TraceHandle = HandleEntry->TraceHandle;
|
|
try {
|
|
status = WmipCopyLogfileInfo(
|
|
HandleEntry,
|
|
(PEVENT_TRACE_LOGFILEW)Logfile,
|
|
FALSE
|
|
);
|
|
if (status == ERROR_SUCCESS) {
|
|
//
|
|
// For RealTime, handle is a place holder until ProcessTrace.
|
|
//
|
|
if ( (Logfile->LogFileMode & EVENT_TRACE_REAL_TIME_MODE)
|
|
!= EVENT_TRACE_REAL_TIME_MODE ) {
|
|
status = WmipCreateGuidMapping();
|
|
if (status == ERROR_SUCCESS) {
|
|
status = WmipProcessLogHeader(
|
|
&HandleEntry->TraceHandle,
|
|
(PEVENT_TRACE_LOGFILEW*)&Logfile,
|
|
1,
|
|
FALSE,
|
|
TRUE
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
status = WmipNtStatusToDosError( GetExceptionCode() );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( (status != ERROR_SUCCESS) && (HandleEntry != NULL) ) {
|
|
WmipFreeTraceHandle(TraceHandle);
|
|
TraceHandle = (TRACEHANDLE)INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
WmipSetDosError(status);
|
|
return TraceHandle;
|
|
}
|
|
|
|
TRACEHANDLE
|
|
WMIAPI
|
|
OpenTraceW(
|
|
IN PEVENT_TRACE_LOGFILEW Logfile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine processes a trace input and returns the tracelog header.
|
|
Only for logfiles. For realtime traces, the header may not be available.
|
|
|
|
Arguments:
|
|
|
|
Logfile Trace input.
|
|
|
|
|
|
|
|
Returned Value:
|
|
|
|
Pointer to Tracelog header.
|
|
|
|
--*/
|
|
{
|
|
ULONG status = ERROR_INVALID_PARAMETER;
|
|
PTRACELOG_CONTEXT HandleEntry = NULL;
|
|
TRACEHANDLE TraceHandle = (TRACEHANDLE)INVALID_HANDLE_VALUE;
|
|
|
|
WmipInitProcessHeap();
|
|
|
|
if (Logfile != NULL) {
|
|
HandleEntry = WmipAllocateTraceHandle();
|
|
if (HandleEntry == NULL) {
|
|
status = ERROR_OUTOFMEMORY;
|
|
}
|
|
else {
|
|
TraceHandle = HandleEntry->TraceHandle;
|
|
try {
|
|
status = WmipCopyLogfileInfo(
|
|
HandleEntry,
|
|
(PEVENT_TRACE_LOGFILEW)Logfile,
|
|
TRUE
|
|
);
|
|
if (status == ERROR_SUCCESS) {
|
|
if ( (Logfile->LogFileMode & EVENT_TRACE_REAL_TIME_MODE)
|
|
!= EVENT_TRACE_REAL_TIME_MODE ) {
|
|
status = WmipCreateGuidMapping();
|
|
if (status == ERROR_SUCCESS) {
|
|
status = WmipProcessLogHeader(
|
|
&HandleEntry->TraceHandle,
|
|
(PEVENT_TRACE_LOGFILEW*)&Logfile,
|
|
1,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
status = WmipNtStatusToDosError( GetExceptionCode() );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( (status != ERROR_SUCCESS) && (HandleEntry != NULL) ) {
|
|
WmipFreeTraceHandle(TraceHandle);
|
|
TraceHandle = (TRACEHANDLE)INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
WmipSetDosError(status);
|
|
return TraceHandle;
|
|
}
|
|
|
|
ULONG
|
|
WMIAPI
|
|
ProcessTrace(
|
|
IN PTRACEHANDLE HandleArray,
|
|
IN ULONG HandleCount,
|
|
IN LPFILETIME StartTime,
|
|
IN LPFILETIME EndTime
|
|
)
|
|
{
|
|
|
|
PEVENT_TRACE_LOGFILEW Logfiles[MAXLOGGERS];
|
|
PLIST_ENTRY Head, Next;
|
|
PTRACELOG_CONTEXT pHandleEntry, pEntry;
|
|
ULONG i, Status;
|
|
LONGLONG sTime, eTime;
|
|
TRACEHANDLE SavedArray[MAXLOGGERS];
|
|
|
|
PEVENT_TRACE_LOGFILE logfile;
|
|
PEVENT_TRACE pEvent;
|
|
PTRACELOG_CONTEXT pContext;
|
|
PEVENT_TRACE_PROPERTIES Properties;
|
|
ULONG szProperties;
|
|
ULONG RealTimeDataFeed, LogFileDataFeed;
|
|
USHORT LoggerId;
|
|
TRACEHANDLE LoggerHandle = 0;
|
|
ULONG j;
|
|
BOOL Done = FALSE;
|
|
ACCESS_MASK DesiredAccess = TRACELOG_ACCESS_REALTIME;
|
|
|
|
WmipInitProcessHeap();
|
|
|
|
if ((HandleCount == 0) || (HandleCount >= MAXLOGGERS)) {
|
|
return ERROR_BAD_LENGTH;
|
|
}
|
|
if (HandleArray == NULL) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
RtlZeroMemory(Logfiles, MAXLOGGERS*sizeof(PEVENT_TRACE_LOGFILEW) );
|
|
szProperties = sizeof(EVENT_TRACE_PROPERTIES) + 2 * MAXSTR * sizeof(WCHAR);
|
|
Properties = WmipAlloc(szProperties);
|
|
if (Properties == NULL) {
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
|
|
WmipEnterPMCritSection();
|
|
|
|
eTime = 0;
|
|
sTime = 0;
|
|
|
|
try {
|
|
if (StartTime != NULL)
|
|
sTime = *((PLONGLONG) StartTime);
|
|
if (EndTime != NULL)
|
|
eTime = *((PLONGLONG) EndTime);
|
|
|
|
if ((eTime != 0) && (eTime < sTime) ) {
|
|
WmipLeavePMCritSection();
|
|
Status = ERROR_INVALID_TIME;
|
|
goto Cleanup;
|
|
}
|
|
|
|
for (i=0; i<HandleCount; i++) {
|
|
SavedArray[i] = HandleArray[i];
|
|
if (SavedArray[i] == (TRACEHANDLE) INVALID_HANDLE_VALUE) {
|
|
WmipLeavePMCritSection();
|
|
Status = ERROR_INVALID_HANDLE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
for (i=0; i< HandleCount; i++) {
|
|
pHandleEntry = NULL;
|
|
Head = TraceHandleListHeadPtr;
|
|
if (Head != NULL) {
|
|
Next = Head->Flink;
|
|
while (Next != Head) {
|
|
pEntry = CONTAINING_RECORD(Next,
|
|
TRACELOG_CONTEXT,
|
|
Entry);
|
|
Next = Next->Flink;
|
|
if (SavedArray[i] == pEntry->TraceHandle) {
|
|
if (pEntry->fProcessed == FALSE) {
|
|
pHandleEntry = pEntry;
|
|
pHandleEntry->fProcessed = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (pHandleEntry == NULL) {
|
|
Status = ERROR_INVALID_HANDLE;
|
|
WmipLeavePMCritSection();
|
|
goto Cleanup;
|
|
}
|
|
Logfiles[i] = &pHandleEntry->Logfile;
|
|
}
|
|
|
|
WmipLeavePMCritSection();
|
|
|
|
//
|
|
// Scan the Logfiles list and decide it's realtime or
|
|
// Logfile Proceessing.
|
|
//
|
|
for (i=0; i < HandleCount; i++) {
|
|
RealTimeDataFeed = FALSE;
|
|
LogFileDataFeed = FALSE;
|
|
//
|
|
// Check to see if this is a RealTime Datafeed
|
|
//
|
|
if (Logfiles[i]->LogFileMode & EVENT_TRACE_REAL_TIME_MODE) {
|
|
if (Logfiles[i]->LoggerName == NULL) {
|
|
Status = WmipSetDosError(ERROR_INVALID_NAME);
|
|
goto Cleanup;
|
|
}
|
|
//
|
|
// Using the LoggerName, Query the Logger to determine
|
|
// whether this is a Kernel or Usermode realtime logger.
|
|
//
|
|
RtlZeroMemory(Properties, szProperties);
|
|
Properties->Wnode.BufferSize = szProperties;
|
|
|
|
|
|
Status = ControlTraceW(LoggerHandle,
|
|
(LPWSTR)Logfiles[i]->LoggerName,
|
|
Properties,
|
|
EVENT_TRACE_CONTROL_QUERY);
|
|
|
|
if (Status != ERROR_SUCCESS) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!(Properties->LogFileMode & EVENT_TRACE_REAL_TIME_MODE)) {
|
|
Status = ERROR_WMI_INSTANCE_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Logfiles[i]->IsKernelTrace =
|
|
IsEqualGUID(&Properties->Wnode.Guid, &SystemTraceControlGuid);
|
|
|
|
LoggerId = WmiGetLoggerId(Properties->Wnode.HistoricalContext);
|
|
|
|
if (LoggerId == KERNEL_LOGGER_ID)
|
|
LoggerId = 0;
|
|
Logfiles[i]->Filled = LoggerId; // Temporarily stash it away
|
|
Logfiles[i]->LogfileHeader.LogInstanceGuid = Properties->Wnode.Guid;
|
|
|
|
//
|
|
// If the Logger is using UsePerfClock for TimeStamps, make a reference
|
|
// timestamp now.
|
|
//
|
|
|
|
Logfiles[i]->LogfileHeader.ReservedFlags = Properties->Wnode.ClientContext;
|
|
|
|
//
|
|
// Save the BuffferSize for Realtime Buffer Pool Allocation
|
|
//
|
|
Logfiles[i]->BufferSize = Properties->BufferSize * 1024;
|
|
|
|
//
|
|
// This is the place to do security check on this Guid.
|
|
//
|
|
|
|
|
|
Status = WmipCheckGuidAccess( &Properties->Wnode.Guid,
|
|
DesiredAccess );
|
|
|
|
if (Status != ERROR_SUCCESS) {
|
|
goto Cleanup;
|
|
}
|
|
RealTimeDataFeed = TRUE;
|
|
}
|
|
//
|
|
// Check to see if this is a Logfile datafeed.
|
|
//
|
|
|
|
|
|
if (!(Logfiles[i]->LogFileMode & EVENT_TRACE_REAL_TIME_MODE)) {
|
|
if (Logfiles[i]->LogFileName == NULL) {
|
|
Status = WmipSetDosError(ERROR_BAD_PATHNAME);
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( wcslen((LPWSTR)Logfiles[i]->LogFileName) <= 0 ) {
|
|
Status = WmipSetDosError(ERROR_BAD_PATHNAME);
|
|
goto Cleanup;
|
|
}
|
|
|
|
LogFileDataFeed = TRUE;
|
|
}
|
|
|
|
//
|
|
// We don't support both RealTimeFeed and LogFileDataFeed.
|
|
//
|
|
|
|
if (RealTimeDataFeed && LogFileDataFeed) {
|
|
Status = WmipSetDosError(ERROR_INVALID_PARAMETER);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
if (LogFileDataFeed) {
|
|
Status = WmipProcessTraceLog(&SavedArray[0], Logfiles,
|
|
HandleCount,
|
|
sTime,
|
|
eTime,
|
|
TRUE);
|
|
}
|
|
else {
|
|
Status = WmipProcessRealTimeTraces(&SavedArray[0], Logfiles,
|
|
HandleCount,
|
|
sTime,
|
|
eTime,
|
|
TRUE);
|
|
}
|
|
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
#ifdef DBG
|
|
WmipDebugPrint(("TRACE: WmipProcessTraceLog threw exception %X\n",
|
|
Status));
|
|
#endif
|
|
Status = WmipSetDosError(WmipNtStatusToDosError(Status));
|
|
}
|
|
|
|
try {
|
|
WmipEnterPMCritSection();
|
|
for (i=0; i< HandleCount; i++) {
|
|
pHandleEntry = NULL;
|
|
Head = TraceHandleListHeadPtr;
|
|
WmipAssert(Head);
|
|
Next = Head->Flink;
|
|
while (Next != Head) {
|
|
pEntry = CONTAINING_RECORD(Next, TRACELOG_CONTEXT, Entry);
|
|
Next = Next->Flink;
|
|
|
|
if (SavedArray[i] == pEntry->TraceHandle) {
|
|
pEntry->fProcessed = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
WmipLeavePMCritSection();
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
#ifdef DBG
|
|
WmipDebugPrint(("TRACE: WmipProcessTraceLog threw exception %X\n",
|
|
Status));
|
|
#endif
|
|
Status = WmipSetDosError(WmipNtStatusToDosError(Status));
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
WmipFree(Properties);
|
|
return Status;
|
|
}
|
|
|
|
ULONG
|
|
WMIAPI
|
|
CloseTrace(
|
|
IN TRACEHANDLE TraceHandle
|
|
)
|
|
{
|
|
WmipInitProcessHeap();
|
|
if ((TraceHandle == 0) ||
|
|
(TraceHandle == (TRACEHANDLE)INVALID_HANDLE_VALUE))
|
|
return ERROR_INVALID_HANDLE;
|
|
return WmipFreeTraceHandle(TraceHandle);
|
|
}
|
|
|
|
VOID
|
|
WmipGuidMapCallback(
|
|
PLIST_ENTRY GuidMapListHeadPtr,
|
|
PEVENT_TRACE pEvent
|
|
)
|
|
{
|
|
PTRACEGUIDMAP GuidMap;
|
|
|
|
WmipInitProcessHeap();
|
|
|
|
if (pEvent == NULL)
|
|
return;
|
|
|
|
GuidMap = (PTRACEGUIDMAP) pEvent->MofData;
|
|
if (GuidMap != NULL) {
|
|
WmipAddGuidHandleToGuidMapList(GuidMapListHeadPtr, GuidMap->GuidMapHandle, &GuidMap->Guid);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void
|
|
WmipCleanupTraceLog(
|
|
PTRACELOG_CONTEXT pContext
|
|
)
|
|
{
|
|
ULONG Size;
|
|
|
|
//
|
|
// Free up the realtime context arrays and buffers
|
|
//
|
|
|
|
WmipEnterPMCritSection();
|
|
|
|
if (pContext->IsRealTime) {
|
|
if (pContext->Root != NULL) {
|
|
WmipFree(pContext->Root);
|
|
}
|
|
WmipFreeRealTimeContext(pContext->RealTimeCxt);
|
|
}
|
|
else {
|
|
if (pContext->Handle != NULL) {
|
|
NtClose(pContext->Handle);
|
|
pContext->Handle = NULL;
|
|
}
|
|
}
|
|
|
|
if (pContext->BufferList != NULL) {
|
|
WmipMemFree(pContext->BufferList);
|
|
}
|
|
if (pContext->BufferCacheSpace != NULL) {
|
|
WmipMemFree(pContext->BufferCacheSpace);
|
|
}
|
|
|
|
WmipCleanupGuidMapList(&pContext->GuidMapListHead);
|
|
|
|
//
|
|
// The following fields need to be reset since the caller
|
|
// may call ProcessTrace again with the same handle
|
|
//
|
|
Size = sizeof(TRACELOG_CONTEXT) - FIELD_OFFSET(TRACELOG_CONTEXT, fProcessed);
|
|
RtlZeroMemory(&pContext->fProcessed, Size);
|
|
InitializeListHead (&pContext->GuidMapListHead);
|
|
|
|
WmipLeavePMCritSection();
|
|
|
|
|
|
//
|
|
// TODO: We need to use a ref count mechanism before deleting the
|
|
// EventMapList and CallbackLists
|
|
//
|
|
|
|
|
|
// if (EventMapList != NULL) {
|
|
// HeapFree(GetProcessHeap(), 0, EventMapList);
|
|
// EventMapList = NULL;
|
|
// }
|
|
// WmipFreeCallbackList();
|
|
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
WMIAPI
|
|
WmiGetFirstTraceOffset(
|
|
IN PWMIBUFFERINFO BufferInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This is the private API for buffer walking for cluster/
|
|
debugger support.
|
|
|
|
Returns the Offset to the first event.
|
|
|
|
Arguments:
|
|
|
|
|
|
Returned Value:
|
|
|
|
Status code
|
|
|
|
--*/
|
|
{
|
|
PVOID pBuffer;
|
|
PWMI_BUFFER_HEADER pHeader;
|
|
PLONG LastByte;
|
|
|
|
if (BufferInfo == NULL) {
|
|
return 0;
|
|
}
|
|
pBuffer = BufferInfo->Buffer;
|
|
|
|
if (pBuffer == NULL) {
|
|
return 0;
|
|
}
|
|
pHeader = (PWMI_BUFFER_HEADER) pBuffer;
|
|
|
|
switch(BufferInfo->BufferSource) {
|
|
case WMIBS_CURRENT_LIST:
|
|
{
|
|
// TODO: Fix GlennP's debugger problem in 2195
|
|
//
|
|
// ULONG lMask = ~((ULONG)0);
|
|
pHeader->Wnode.BufferSize = BufferInfo->BufferSize;
|
|
pHeader->ClientContext.Alignment = (UCHAR)BufferInfo->Alignment;
|
|
// if (BufferInfo->ProcessorNumber < lMask) {
|
|
// pHeader->ClientContext.ProcessorNumber = (UCHAR)BufferInfo->ProcessorNumber;
|
|
// }
|
|
pHeader->Offset = pHeader->CurrentOffset;
|
|
break;
|
|
}
|
|
case WMIBS_FREE_LIST:
|
|
{
|
|
pHeader->Offset = pHeader->CurrentOffset;
|
|
|
|
if (pHeader->SavedOffset > 0)
|
|
pHeader->Offset = pHeader->SavedOffset;
|
|
|
|
if (pHeader->Offset == 0) {
|
|
pHeader->Offset = sizeof(WMI_BUFFER_HEADER);
|
|
}
|
|
|
|
pHeader->Wnode.BufferSize = BufferInfo->BufferSize;
|
|
break;
|
|
}
|
|
case WMIBS_TRANSITION_LIST:
|
|
{
|
|
if (pHeader->SavedOffset > 0) {
|
|
pHeader->Offset = pHeader->SavedOffset;
|
|
}
|
|
break;
|
|
}
|
|
case WMIBS_FLUSH_LIST:
|
|
{
|
|
if (pHeader->SavedOffset > 0) {
|
|
pHeader->Offset = pHeader->SavedOffset;
|
|
}
|
|
pHeader->Wnode.BufferSize = BufferInfo->BufferSize;
|
|
break;
|
|
}
|
|
case WMIBS_LOG_FILE:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (BufferInfo->BufferSource != WMIBS_LOG_FILE) {
|
|
LastByte = (PLONG) ((PUCHAR)pHeader+ pHeader->Offset);
|
|
if (pHeader->Offset <= (BufferInfo->BufferSize - sizeof(ULONG)) ) {
|
|
|
|
*LastByte = -1;
|
|
}
|
|
}
|
|
|
|
return sizeof(WMI_BUFFER_HEADER);
|
|
}
|
|
|
|
ULONG
|
|
WmipConvertEnumToTraceType(
|
|
WMI_HEADER_TYPE eTraceType
|
|
)
|
|
{
|
|
switch(eTraceType) {
|
|
case WMIHT_SYSTEM32:
|
|
return TRACE_HEADER_TYPE_SYSTEM32;
|
|
case WMIHT_SYSTEM64:
|
|
return TRACE_HEADER_TYPE_SYSTEM64;
|
|
case WMIHT_EVENT_TRACE:
|
|
return TRACE_HEADER_TYPE_FULL_HEADER;
|
|
case WMIHT_EVENT_INSTANCE:
|
|
return TRACE_HEADER_TYPE_INSTANCE;
|
|
case WMIHT_TIMED:
|
|
return TRACE_HEADER_TYPE_TIMED;
|
|
case WMIHT_ULONG32:
|
|
return TRACE_HEADER_TYPE_ULONG32;
|
|
case WMIHT_WNODE:
|
|
return TRACE_HEADER_TYPE_WNODE_HEADER;
|
|
case WMIHT_MESSAGE:
|
|
return TRACE_HEADER_TYPE_MESSAGE;
|
|
case WMIHT_PERFINFO32:
|
|
return TRACE_HEADER_TYPE_PERFINFO32;
|
|
case WMIHT_PERFINFO64:
|
|
return TRACE_HEADER_TYPE_PERFINFO64;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
WMI_HEADER_TYPE
|
|
WmipConvertTraceTypeToEnum(
|
|
ULONG TraceType
|
|
)
|
|
{
|
|
switch(TraceType) {
|
|
case TRACE_HEADER_TYPE_SYSTEM32:
|
|
return WMIHT_SYSTEM32;
|
|
case TRACE_HEADER_TYPE_SYSTEM64:
|
|
return WMIHT_SYSTEM64;
|
|
case TRACE_HEADER_TYPE_FULL_HEADER:
|
|
return WMIHT_EVENT_TRACE;
|
|
case TRACE_HEADER_TYPE_INSTANCE:
|
|
return WMIHT_EVENT_INSTANCE;
|
|
case TRACE_HEADER_TYPE_TIMED:
|
|
return WMIHT_TIMED;
|
|
case TRACE_HEADER_TYPE_ULONG32:
|
|
return WMIHT_ULONG32;
|
|
case TRACE_HEADER_TYPE_WNODE_HEADER:
|
|
return WMIHT_WNODE;
|
|
case TRACE_HEADER_TYPE_MESSAGE:
|
|
return WMIHT_MESSAGE;
|
|
case TRACE_HEADER_TYPE_PERFINFO32:
|
|
return WMIHT_PERFINFO32;
|
|
case TRACE_HEADER_TYPE_PERFINFO64:
|
|
return WMIHT_PERFINFO64;
|
|
default:
|
|
return WMIHT_NONE;
|
|
}
|
|
}
|
|
|
|
|
|
WMI_HEADER_TYPE
|
|
WMIAPI
|
|
WmiGetTraceHeader(
|
|
IN PVOID LogBuffer,
|
|
IN ULONG Offset,
|
|
OUT ULONG *Size
|
|
)
|
|
{
|
|
ULONG Status = ERROR_SUCCESS;
|
|
ULONG TraceType;
|
|
|
|
try {
|
|
|
|
TraceType = WmipGetNextEventOffsetType(
|
|
(PUCHAR)LogBuffer,
|
|
Offset,
|
|
Size
|
|
);
|
|
|
|
return WmipConvertTraceTypeToEnum(TraceType);
|
|
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
#ifdef DBG
|
|
WmipDebugPrint(("TRACE: WmiGetTraceHeader threw exception %X\n",
|
|
Status));
|
|
#endif
|
|
Status = WmipSetDosError(WmipNtStatusToDosError(Status));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
ULONG
|
|
WMIAPI
|
|
WmiParseTraceEvent(
|
|
IN PVOID LogBuffer,
|
|
IN ULONG Offset,
|
|
IN WMI_HEADER_TYPE HeaderType,
|
|
IN OUT PVOID EventInfo,
|
|
IN ULONG EventInfoSize
|
|
)
|
|
{
|
|
|
|
return WmipParseTraceEvent(NULL, LogBuffer, Offset, HeaderType, EventInfo, EventInfoSize);
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
WmipParseTraceEvent(
|
|
IN PTRACELOG_CONTEXT pContext,
|
|
IN PVOID LogBuffer,
|
|
IN ULONG Offset,
|
|
IN WMI_HEADER_TYPE HeaderType,
|
|
IN OUT PVOID EventInfo,
|
|
IN ULONG EventInfoSize
|
|
)
|
|
{
|
|
PWMI_BUFFER_HEADER Header = (PWMI_BUFFER_HEADER)LogBuffer;
|
|
ULONG Status = ERROR_SUCCESS;
|
|
PVOID pEvent;
|
|
|
|
if ( (LogBuffer == NULL) ||
|
|
(EventInfo == NULL) ||
|
|
(EventInfoSize < sizeof(EVENT_TRACE_HEADER)) )
|
|
{
|
|
return (ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
Status = WmipCreateGuidMapping();
|
|
if (Status != ERROR_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
try {
|
|
|
|
RtlZeroMemory(EventInfo, sizeof(EVENT_TRACE));
|
|
|
|
pEvent = (void*) ((PUCHAR)LogBuffer + Offset);
|
|
|
|
WmipCopyCurrentEvent(pContext,
|
|
pEvent,
|
|
EventInfo,
|
|
WmipConvertEnumToTraceType(HeaderType),
|
|
(PWMI_BUFFER_HEADER)LogBuffer
|
|
);
|
|
|
|
( (PEVENT_TRACE)EventInfo)->ClientContext = Header->Wnode.ClientContext;
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
#ifdef DBG
|
|
WmipDebugPrint(("TRACE: WmipParseTraceEvent threw exception %X\n",
|
|
Status));
|
|
#endif
|
|
Status = WmipSetDosError(WmipNtStatusToDosError(Status));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// RealTime Routines
|
|
//
|
|
|
|
|
|
PVOID
|
|
WmipAllocTraceBuffer(
|
|
PTRACELOG_REALTIME_CONTEXT RTCxt,
|
|
ULONG BufferSize
|
|
)
|
|
{
|
|
PVOID Buffer = NULL;
|
|
PTRACE_BUFFER_HEADER Header;
|
|
PLIST_ENTRY Head, Next;
|
|
PTRACERT_BUFFER_LIST_ENTRY ListEntry;
|
|
PTRACE_BUFFER_SPACE WmipTraceBufferSpace;
|
|
|
|
WmipEnterPMCritSection();
|
|
WmipTraceBufferSpace = RTCxt->WmipTraceBufferSpace;
|
|
Head = &WmipTraceBufferSpace->FreeListHead;
|
|
Next = Head->Flink;
|
|
while (Head != Next) {
|
|
ListEntry = CONTAINING_RECORD(Next, TRACERT_BUFFER_LIST_ENTRY, Entry);
|
|
Next = Next->Flink;
|
|
if (ListEntry->Size == BufferSize) {
|
|
goto foundList;
|
|
}
|
|
}
|
|
//
|
|
// No list for this bufferSize was found. So go Ahead and allocate one.
|
|
//
|
|
ListEntry = WmipAlloc(sizeof(TRACERT_BUFFER_LIST_ENTRY));
|
|
if (ListEntry == NULL) {
|
|
WmipSetDosError(ERROR_OUTOFMEMORY);
|
|
WmipLeavePMCritSection();
|
|
return NULL;
|
|
}
|
|
RtlZeroMemory(ListEntry, sizeof(TRACERT_BUFFER_LIST_ENTRY));
|
|
ListEntry->Size = BufferSize;
|
|
InitializeListHead(&ListEntry->BufferListHead);
|
|
InsertHeadList(&WmipTraceBufferSpace->FreeListHead, &ListEntry->Entry);
|
|
|
|
foundList:
|
|
//
|
|
// Now look for a free buffer in this list
|
|
//
|
|
Head = &ListEntry->BufferListHead;
|
|
Next = Head->Flink;
|
|
while (Head != Next) {
|
|
Header = CONTAINING_RECORD( Next, TRACE_BUFFER_HEADER, Entry );
|
|
if (((PWNODE_HEADER)Header)->BufferSize == BufferSize) {
|
|
RemoveEntryList(&Header->Entry);
|
|
Buffer = (PVOID)Header;
|
|
break;
|
|
}
|
|
Next = Next->Flink;
|
|
}
|
|
WmipLeavePMCritSection();
|
|
//
|
|
// If No Free Buffers are found we try to allocate one and return.
|
|
//
|
|
if (Buffer == NULL) {
|
|
PVOID Space;
|
|
ULONG SizeLeft = WmipTraceBufferSpace->Reserved -
|
|
WmipTraceBufferSpace->Committed;
|
|
if (SizeLeft < BufferSize) {
|
|
WmipSetDosError(ERROR_OUTOFMEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
Space = (PVOID)( (PCHAR)WmipTraceBufferSpace->Space +
|
|
WmipTraceBufferSpace->Committed );
|
|
|
|
Buffer = WmipMemCommit( Space, BufferSize );
|
|
|
|
if (Buffer != NULL) {
|
|
WmipTraceBufferSpace->Committed += BufferSize;
|
|
}
|
|
|
|
}
|
|
return (Buffer);
|
|
}
|
|
VOID
|
|
WmipFreeTraceBuffer(
|
|
PTRACELOG_REALTIME_CONTEXT RTCxt,
|
|
PVOID Buffer
|
|
)
|
|
{
|
|
PTRACE_BUFFER_HEADER Header = (PTRACE_BUFFER_HEADER)Buffer;
|
|
PLIST_ENTRY Head, Next;
|
|
ULONG BufferSize = Header->Wnode.BufferSize;
|
|
PTRACERT_BUFFER_LIST_ENTRY ListEntry;
|
|
PLIST_ENTRY BufferList = NULL;
|
|
PTRACE_BUFFER_SPACE WmipTraceBufferSpace;
|
|
|
|
WmipEnterPMCritSection();
|
|
WmipTraceBufferSpace = RTCxt->WmipTraceBufferSpace;
|
|
Head = &WmipTraceBufferSpace->FreeListHead;
|
|
Next = Head->Flink;
|
|
while (Head != Next) {
|
|
ListEntry = CONTAINING_RECORD(Next, TRACERT_BUFFER_LIST_ENTRY, Entry);
|
|
Next = Next->Flink;
|
|
if (ListEntry->Size == BufferSize) {
|
|
BufferList = &ListEntry->BufferListHead;
|
|
break;
|
|
}
|
|
}
|
|
if (BufferList != NULL) {
|
|
|
|
InsertHeadList(BufferList, &Header->Entry);
|
|
}
|
|
else {
|
|
|
|
// We shoule not get here. If we do the buffer->Size is
|
|
// Corrupted.
|
|
WmipAssert(BufferList == NULL);
|
|
}
|
|
WmipLeavePMCritSection();
|
|
}
|
|
|
|
|
|
//
|
|
// TODO: If two threads called processtrace for the same RT stream, how can we fire
|
|
// two callbacks
|
|
//
|
|
|
|
|
|
ULONG
|
|
WmipRealTimeCallback(
|
|
IN PWNODE_HEADER Wnode,
|
|
IN ULONG_PTR RTContext //LogFileIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine is called when a real time buffer becomes available.
|
|
The buffer delivered by WMI is copied to a local pool of ring buffers.
|
|
Each realtime data feed maintains its own pool of ring buffers and the
|
|
LogFileIndex passed back via the Wnode (ProviderId field)
|
|
identifies the stream to which the buffer is destined.
|
|
|
|
|
|
Arguments:
|
|
|
|
Wnode Buffer
|
|
LogFileIndex Index of the Input stream from which this buffer came.
|
|
|
|
Returned Value:
|
|
|
|
Status Code.
|
|
|
|
--*/
|
|
{
|
|
ULONG index;
|
|
PTRACELOG_REALTIME_CONTEXT Context = (PTRACELOG_REALTIME_CONTEXT) RTContext;
|
|
PWNODE_HEADER pHeader;
|
|
PWMI_CLIENT_CONTEXT ClientContext;
|
|
|
|
//
|
|
// Assumes that the number of LogFiles is less than the MAXLOGGERS.
|
|
//
|
|
// Get the LogFileIndex to which this buffer is destined through the
|
|
// Logger Historical Context.
|
|
|
|
ClientContext = (PWMI_CLIENT_CONTEXT)&Wnode->ClientContext;
|
|
//
|
|
// If we can't use this buffer for whatever reason, we return and
|
|
// the return code is always ERROR_SUCCESS.
|
|
//
|
|
|
|
|
|
//
|
|
// Circular FIFO queue of MAXBUFFERS to hold the buffers.
|
|
// Producer to Fill it and Consumer to Null it.
|
|
//
|
|
|
|
index = (Context->BuffersProduced % MAXBUFFERS);
|
|
if (Context->RealTimeBufferPool[index] == NULL) { //Empty slot found.
|
|
pHeader = (PWNODE_HEADER) WmipAllocTraceBuffer(Context, Wnode->BufferSize);
|
|
if (pHeader == NULL) {
|
|
return ERROR_SUCCESS;
|
|
}
|
|
RtlCopyMemory(pHeader, Wnode, Wnode->BufferSize); // One more copy!?
|
|
Context->RealTimeBufferPool[index] = pHeader;
|
|
Context->BuffersProduced++;
|
|
NtSetEvent(Context->MoreDataEvent, NULL); //Signal the dc there's more data.
|
|
}
|
|
else { // No Empty Slots found.
|
|
Context->BufferOverflow++; // Simply let the buffer go.
|
|
}
|
|
|
|
//
|
|
// wmi service maintains only the Delta buffersLost since the last time
|
|
// it was reported. The Count is zeroed once it is reported in a delivered
|
|
// buffer. This means I can add it directly.
|
|
//
|
|
Context->BufferOverflow += Wnode->CountLost;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
WmipSetupRealTimeContext(
|
|
PTRACEHANDLE HandleArray,
|
|
PEVENT_TRACE_LOGFILEW *Logfiles,
|
|
ULONG LogfileCount
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine sets up the context to process real time buffers.
|
|
The real time buffers delivered will be copied and kept in a circular
|
|
buffer pool until the ProcessTracelog routine can consume it.
|
|
|
|
Arguments:
|
|
|
|
LogFile Array of Logfiles being processed.
|
|
LogFileCount Number of Logfiles in the Array.
|
|
|
|
Returned Value:
|
|
|
|
Status Code.
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
ULONG Status;
|
|
USHORT LoggerId;
|
|
ULONG TotalBufferSize = 0;
|
|
SYSTEM_BASIC_INFORMATION SystemInfo;
|
|
ULONG RealTimeRegistered = FALSE;
|
|
|
|
|
|
Status = WmipCreateGuidMapping();
|
|
if (Status != ERROR_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
|
|
for (i=0; i < LogfileCount; i++) {
|
|
if (Logfiles[i]->LogFileMode & EVENT_TRACE_REAL_TIME_MODE) {
|
|
TotalBufferSize += Logfiles[i]->BufferSize; // * SystemInfo.NumberOfProcessors;
|
|
}
|
|
}
|
|
if (TotalBufferSize == 0)
|
|
TotalBufferSize = DEFAULT_REALTIME_BUFFER_SIZE;
|
|
|
|
//
|
|
// Initialize the real time data feed Structures.
|
|
//
|
|
|
|
for (i=0; i < LogfileCount; i++) {
|
|
PTRACELOG_REALTIME_CONTEXT RTCxt;
|
|
PTRACELOG_CONTEXT pContext;
|
|
PTRACE_BUFFER_LIST_ENTRY pListEntry;
|
|
LARGE_INTEGER Frequency;
|
|
ULONGLONG Counter = 0;
|
|
|
|
|
|
if (Logfiles[i]->LogFileMode & EVENT_TRACE_REAL_TIME_MODE) {
|
|
|
|
pContext = WmipLookupTraceHandle(HandleArray[i]);
|
|
if (pContext == NULL) {
|
|
return WmipSetDosError(ERROR_OUTOFMEMORY);
|
|
}
|
|
pContext->IsRealTime = TRUE;
|
|
pContext->Handle = NULL;
|
|
Logfiles[i]->Context = pContext;
|
|
Logfiles[i]->BuffersRead = 0;
|
|
|
|
pContext->EndOfFile = TRUE;
|
|
|
|
|
|
//
|
|
// Save the flags from OpenTrace at this time before the first
|
|
// buffer callback which will erase it.
|
|
//
|
|
|
|
pContext->ConversionFlags = Logfiles[i]->LogfileHeader.ReservedFlags;
|
|
|
|
pContext->UsePerfClock = Logfiles[i]->LogfileHeader.ReservedFlags;
|
|
|
|
//
|
|
// If the conversion flags are set, adjust UsePerfClock accordingly.
|
|
//
|
|
if (pContext->ConversionFlags & EVENT_TRACE_USE_RAWTIMESTAMP ) {
|
|
pContext->UsePerfClock = EVENT_TRACE_CLOCK_RAW;
|
|
}
|
|
|
|
//
|
|
// Fill in the StartTime, Frequency and StartPerfClock fields
|
|
//
|
|
|
|
Status = NtQueryPerformanceCounter((PLARGE_INTEGER)&Counter,
|
|
&Frequency);
|
|
pContext->StartPerfClock.QuadPart = Counter;
|
|
pContext->PerfFreq.QuadPart = Frequency.QuadPart;
|
|
pContext->StartTime.QuadPart = WmipGetSystemTime();
|
|
|
|
RTCxt = (PTRACELOG_REALTIME_CONTEXT)WmipAlloc(
|
|
sizeof(TRACELOG_REALTIME_CONTEXT));
|
|
|
|
if (RTCxt == NULL) {
|
|
return WmipSetDosError(ERROR_OUTOFMEMORY);
|
|
}
|
|
|
|
RtlZeroMemory(RTCxt, sizeof(TRACELOG_REALTIME_CONTEXT));
|
|
|
|
RTCxt->MoreDataEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (RTCxt->MoreDataEvent == NULL) {
|
|
return WmipSetDosError(ERROR_OBJECT_NOT_FOUND);
|
|
}
|
|
|
|
//
|
|
// Save the RTCxt in a global pContext array so that the
|
|
// notification callback from WMI can get at it through the
|
|
// logfile index i.
|
|
//
|
|
LoggerId = (USHORT)Logfiles[i]->Filled; // get the stashed LoggerId.
|
|
pContext->LoggerId = LoggerId;
|
|
|
|
pContext->RealTimeCxt = RTCxt;
|
|
|
|
RTCxt->InstanceGuid = Logfiles[i]->LogfileHeader.LogInstanceGuid;
|
|
|
|
//
|
|
// Allocate the buffer space to receive the ral time buffers
|
|
//
|
|
|
|
if ( !RealTimeRegistered) {
|
|
ULONG SizeReserved;
|
|
PVOID BufferSpace;
|
|
|
|
//
|
|
// Right before starting to receive the realtime buffers
|
|
// get a dump of the GuidMaps if any.
|
|
///
|
|
|
|
WmipDumpGuidMaps(NULL, &pContext->GuidMapListHead, TRUE);
|
|
|
|
RealTimeRegistered = TRUE;
|
|
|
|
RTCxt->WmipTraceBufferSpace = (PTRACE_BUFFER_SPACE)WmipAlloc(
|
|
sizeof(TRACE_BUFFER_SPACE));
|
|
|
|
if (RTCxt->WmipTraceBufferSpace == NULL) {
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
RtlZeroMemory(RTCxt->WmipTraceBufferSpace, sizeof(TRACE_BUFFER_SPACE));
|
|
InitializeListHead(&RTCxt->WmipTraceBufferSpace->FreeListHead);
|
|
|
|
SizeReserved = MAXBUFFERS *
|
|
TotalBufferSize;
|
|
|
|
|
|
BufferSpace = WmipMemReserve( SizeReserved );
|
|
if (BufferSpace == NULL) {
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
RTCxt->WmipTraceBufferSpace->Reserved = SizeReserved;
|
|
RTCxt->WmipTraceBufferSpace->Space = BufferSpace;
|
|
|
|
}
|
|
//
|
|
// For Every Logger Stream we need to register with WMI
|
|
// for buffer notification with its Security Guid.
|
|
//
|
|
Status = WmiNotificationRegistration(
|
|
(const LPGUID) &RTCxt->InstanceGuid,
|
|
TRUE,
|
|
WmipRealTimeCallback,
|
|
(ULONG_PTR)RTCxt,
|
|
NOTIFICATION_CALLBACK_DIRECT
|
|
);
|
|
if (Status != ERROR_SUCCESS) {
|
|
return Status;
|
|
}
|
|
//
|
|
// Allocate Room to process one event
|
|
//
|
|
|
|
pListEntry = (PTRACE_BUFFER_LIST_ENTRY) WmipAlloc( sizeof(TRACE_BUFFER_LIST_ENTRY) );
|
|
if (pListEntry == NULL) {
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
RtlZeroMemory(pListEntry, sizeof(TRACE_BUFFER_LIST_ENTRY) );
|
|
|
|
pContext->Root = pListEntry;
|
|
|
|
}
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
ULONG
|
|
WmipLookforRealTimeBuffers(
|
|
PEVENT_TRACE_LOGFILEW logfile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine checks to see if there are any real time buffers
|
|
ready for consumption. If so, it sets up the CurrentBuffer and
|
|
the CurrentEvent for this logfile stream. If no buffers are available
|
|
simply sets the EndOfFile to be true.
|
|
|
|
Arguments:
|
|
|
|
logfile Current Logfile being processed.
|
|
|
|
Returned Value:
|
|
|
|
ERROR_SUCCESS Successfully moved to the next event.
|
|
|
|
--*/
|
|
{
|
|
ULONG index;
|
|
ULONG BuffersRead;
|
|
PVOID pBuffer;
|
|
PEVENT_TRACE pEvent;
|
|
PTRACELOG_CONTEXT pContext;
|
|
PTRACELOG_REALTIME_CONTEXT RTCxt;
|
|
PWMI_BUFFER_HEADER pHeader;
|
|
WMI_HEADER_TYPE HeaderType = WMIHT_NONE;
|
|
ULONG Size;
|
|
ULONG Offset;
|
|
ULONG Status;
|
|
|
|
|
|
if (logfile == NULL) {
|
|
return WmipSetDosError(ERROR_INVALID_PARAMETER);
|
|
}
|
|
pContext = logfile->Context;
|
|
|
|
RTCxt = pContext->RealTimeCxt;
|
|
|
|
if (RTCxt == NULL) {
|
|
return WmipSetDosError(ERROR_INVALID_DATA);
|
|
}
|
|
|
|
|
|
if (pContext->EndOfFile != TRUE) {
|
|
|
|
pBuffer = pContext->BufferCache[0].Buffer;
|
|
pEvent = &pContext->Root->Event;
|
|
Status = ERROR_SUCCESS;
|
|
Size = 0;
|
|
if ((HeaderType = WmiGetTraceHeader(pBuffer, pContext->Root->BufferOffset, &Size)) != WMIHT_NONE) {
|
|
if (Size > 0) {
|
|
Status = WmipParseTraceEvent(pContext, pBuffer, pContext->Root->BufferOffset, HeaderType, pEvent, sizeof(EVENT_TRACE));
|
|
pContext->Root->BufferOffset += Size;
|
|
}
|
|
}
|
|
pContext->Root->EventSize = Size;
|
|
|
|
if ( ( Size > 0) && (Status == ERROR_SUCCESS) ) {
|
|
logfile->CurrentTime = pEvent->Header.TimeStamp.QuadPart;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
else {
|
|
//
|
|
// When the current buffer is exhausted, make the
|
|
// BufferCallback
|
|
//
|
|
if (logfile->BufferCallback) {
|
|
ULONG bRetVal;
|
|
try {
|
|
bRetVal = (*logfile->BufferCallback) (logfile);
|
|
if (!bRetVal) {
|
|
return ERROR_CANCELLED;
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
pContext->EndOfFile = TRUE;
|
|
Status = GetExceptionCode();
|
|
#ifdef DBG
|
|
WmipDebugPrint(("TRACE: BufferCallback threw exception %X\n",
|
|
Status));
|
|
#endif
|
|
WmipSetDosError(WmipNtStatusToDosError(Status));
|
|
return ERROR_CANCELLED; // so that realtime also cleans up.
|
|
}
|
|
}
|
|
WmipFreeTraceBuffer(RTCxt, pBuffer);
|
|
}
|
|
}
|
|
|
|
pContext->EndOfFile = TRUE;
|
|
logfile->CurrentTime = 0;
|
|
|
|
BuffersRead = logfile->BuffersRead;
|
|
// Check to see if there are more buffers to consume.
|
|
if (BuffersRead < RTCxt->BuffersProduced) {
|
|
pContext->EndOfFile = FALSE;
|
|
index = (BuffersRead % MAXBUFFERS);
|
|
if ( RTCxt->RealTimeBufferPool[index] != NULL) {
|
|
PWMI_CLIENT_CONTEXT ClientContext;
|
|
PWNODE_HEADER Wnode;
|
|
|
|
pBuffer = (char*) (RTCxt->RealTimeBufferPool[index]);
|
|
pContext->BufferCache[0].Buffer = pBuffer;
|
|
RTCxt->RealTimeBufferPool[index] = NULL;
|
|
|
|
Wnode = (PWNODE_HEADER)pContext->BufferCache[0].Buffer;
|
|
|
|
pHeader = (PWMI_BUFFER_HEADER)pContext->BufferCache[0].Buffer;
|
|
|
|
Offset = sizeof(WMI_BUFFER_HEADER);
|
|
|
|
pEvent = &pContext->Root->Event;
|
|
|
|
if ((HeaderType = WmiGetTraceHeader(pBuffer, Offset, &Size)) != WMIHT_NONE) {
|
|
if (Size == 0)
|
|
return ERROR_INVALID_DATA;
|
|
Status = WmipParseTraceEvent(pContext, pBuffer, Offset, HeaderType, pEvent, sizeof(EVENT_TRACE));
|
|
|
|
if (Status != ERROR_SUCCESS) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
Offset += Size;
|
|
|
|
pContext->Root->BufferOffset = Offset;
|
|
pContext->Root->EventSize = Size;
|
|
|
|
logfile->CurrentTime = pEvent->Header.TimeStamp.QuadPart;
|
|
|
|
// Since the RealTime Logger may have started after
|
|
// the consumer started, we have to get the buffersize
|
|
// like this.
|
|
logfile->BufferSize = Wnode->BufferSize;
|
|
logfile->Filled = (ULONG)pHeader->Offset;
|
|
logfile->EventsLost = pHeader->EventsLost;
|
|
|
|
logfile->BuffersRead++;
|
|
}
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
ULONG
|
|
WmipCheckForRealTimeLoggers(
|
|
PEVENT_TRACE_LOGFILEW *Logfiles,
|
|
ULONG LogfileCount,
|
|
ULONG Unicode)
|
|
{
|
|
ULONG Status;
|
|
TRACEHANDLE LoggerHandle = 0;
|
|
ULONG i;
|
|
|
|
for (i=0; i < LogfileCount; i++) {
|
|
//
|
|
// Check to see if this is a RealTime Datafeed
|
|
//
|
|
if (Logfiles[i]->LogFileMode & EVENT_TRACE_REAL_TIME_MODE) {
|
|
//
|
|
// Using the LoggerName, Query the Logger to determine
|
|
// whether this is a Kernel or Usermode realtime logger.
|
|
//
|
|
RtlZeroMemory(&Properties, sizeof(Properties) );
|
|
Properties.TraceProp.Wnode.BufferSize = sizeof(Properties);
|
|
|
|
|
|
if (Unicode)
|
|
Status = ControlTraceW(LoggerHandle,
|
|
(LPWSTR)Logfiles[i]->LoggerName,
|
|
&Properties.TraceProp,
|
|
EVENT_TRACE_CONTROL_QUERY);
|
|
else
|
|
Status = ControlTraceA(LoggerHandle,
|
|
(LPSTR)Logfiles[i]->LoggerName,
|
|
(PEVENT_TRACE_PROPERTIES)&Properties,
|
|
EVENT_TRACE_CONTROL_QUERY);
|
|
//
|
|
// If the Logger is still around and the Real Time bit
|
|
// is still set continue processing. Otherwise quit.
|
|
//
|
|
if ((Status == ERROR_SUCCESS) && (Properties.TraceProp.LogFileMode & EVENT_TRACE_REAL_TIME_MODE) ){
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
#ifdef DBG
|
|
//
|
|
// We are expecting to see ERROR_WMI_INSTANCE_NOT_FOUND when the logger has gone away.
|
|
// Any other error is abnormal.
|
|
//
|
|
if ( Status != ERROR_WMI_INSTANCE_NOT_FOUND ) {
|
|
WmipDebugPrint(("WET: WmipCheckForRealTimeLoggers abnormal failure. Status %X\n", Status));
|
|
}
|
|
#endif
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void
|
|
WmipFreeRealTimeContext(
|
|
PTRACELOG_REALTIME_CONTEXT RTCxt
|
|
)
|
|
{
|
|
ULONG Status;
|
|
PTRACERT_BUFFER_LIST_ENTRY ListEntry;
|
|
PLIST_ENTRY Head, Next;
|
|
|
|
if (RTCxt != NULL) {
|
|
Status = WmiNotificationRegistration(
|
|
(const LPGUID) &RTCxt->InstanceGuid,
|
|
FALSE,
|
|
WmipRealTimeCallback,
|
|
0,
|
|
NOTIFICATION_CALLBACK_DIRECT
|
|
);
|
|
}
|
|
|
|
if (RTCxt->MoreDataEvent != NULL) {
|
|
NtClose(RTCxt->MoreDataEvent);
|
|
}
|
|
|
|
if (RTCxt->WmipTraceBufferSpace != NULL) {
|
|
WmipMemFree(RTCxt->WmipTraceBufferSpace->Space);
|
|
Head = &RTCxt->WmipTraceBufferSpace->FreeListHead;
|
|
Next = Head->Flink;
|
|
while (Head != Next) {
|
|
ListEntry = CONTAINING_RECORD(Next, TRACERT_BUFFER_LIST_ENTRY, Entry);
|
|
Next = Next->Flink;
|
|
RemoveEntryList(&ListEntry->Entry);
|
|
WmipFree(ListEntry);
|
|
}
|
|
WmipFree(RTCxt->WmipTraceBufferSpace);
|
|
RTCxt->WmipTraceBufferSpace = NULL;
|
|
}
|
|
|
|
WmipFree(RTCxt);
|
|
}
|
|
|
|
ULONG
|
|
WmipProcessRealTimeTraces(
|
|
PTRACEHANDLE HandleArray,
|
|
PEVENT_TRACE_LOGFILEW *Logfiles,
|
|
ULONG LogfileCount,
|
|
LONGLONG StartTime,
|
|
LONGLONG EndTime,
|
|
ULONG Unicode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Main entry point to process RealTime trace data streams.
|
|
|
|
|
|
Arguments:
|
|
|
|
Logfiles Array of logfile structures with LoggerNames of the RT stream
|
|
LogfileCount Number of RealTime trace streams to process
|
|
StartTime StartTime for windowing data
|
|
EndTime EndTime for windowing data
|
|
|
|
|
|
Returned Value:
|
|
|
|
ERROR_SUCCESS Successfully processed data from realtime trace stream
|
|
|
|
--*/
|
|
{
|
|
ULONG Status;
|
|
BOOL Done = FALSE;
|
|
ULONG i, j;
|
|
PTRACELOG_CONTEXT pContext;
|
|
HANDLE EventArray[MAXLOGGERS];
|
|
NTSTATUS NtStatus;
|
|
LARGE_INTEGER timeout = {(ULONG)(-1 * 10 * 1000 * 1000 * 10), -1}; // Wait for 10 seconds
|
|
|
|
//
|
|
// Register for RealTime Callbacks
|
|
//
|
|
|
|
Status = WmipSetupRealTimeContext( HandleArray, Logfiles, LogfileCount);
|
|
if (Status != ERROR_SUCCESS) {
|
|
goto DoCleanup;
|
|
}
|
|
|
|
//
|
|
// Build the Handle Array
|
|
//
|
|
|
|
for (j=0; j < LogfileCount; j++) {
|
|
pContext = (PTRACELOG_CONTEXT)Logfiles[j]->Context;
|
|
EventArray[j] = pContext->RealTimeCxt->MoreDataEvent;
|
|
}
|
|
|
|
|
|
//
|
|
// Event Processing Loop
|
|
//
|
|
|
|
while (!Done) {
|
|
|
|
LONGLONG nextTimeStamp;
|
|
BOOL EventInRange;
|
|
PEVENT_TRACE_LOGFILEW logfile;
|
|
ULONG j;
|
|
PTRACELOG_CONTEXT pContext;
|
|
//
|
|
// Check to see if end of file has been reached on all the
|
|
// files.
|
|
//
|
|
|
|
logfile = NULL;
|
|
nextTimeStamp = 0;
|
|
|
|
for (j=0; j < LogfileCount; j++) {
|
|
pContext = (PTRACELOG_CONTEXT)Logfiles[j]->Context;
|
|
|
|
if ((pContext->EndOfFile) &&
|
|
(Logfiles[j]->LogFileMode & EVENT_TRACE_REAL_TIME_MODE)) {
|
|
WmipLookforRealTimeBuffers(Logfiles[j]);
|
|
}
|
|
|
|
if (pContext->EndOfFile)
|
|
continue;
|
|
if (nextTimeStamp == 0) {
|
|
nextTimeStamp = Logfiles[j]->CurrentTime;
|
|
logfile = Logfiles[j];
|
|
}
|
|
else if (nextTimeStamp > Logfiles[j]->CurrentTime) {
|
|
nextTimeStamp = Logfiles[j]->CurrentTime;
|
|
logfile = Logfiles[j];
|
|
}
|
|
}
|
|
|
|
if (logfile == NULL) {
|
|
//
|
|
// If no logfile with events found, wait on the realtime event.
|
|
// If no realtime datafeed, then we are done.
|
|
//
|
|
|
|
NtStatus = NtWaitForMultipleObjects(LogfileCount, &EventArray[0], WaitAny, FALSE, &timeout);
|
|
|
|
if (NtStatus == STATUS_TIMEOUT) {
|
|
//
|
|
// If we timed out, then check to see if the loggers have gone away.
|
|
//
|
|
if ( !WmipCheckForRealTimeLoggers(Logfiles, LogfileCount, Unicode) ) {
|
|
break;
|
|
}
|
|
}
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// if the Next event timestamp is not within the window of
|
|
// analysis, we do not fire the event callbacks.
|
|
//
|
|
|
|
EventInRange = TRUE;
|
|
|
|
if ((StartTime != 0) && (StartTime > nextTimeStamp))
|
|
EventInRange = FALSE;
|
|
if ((EndTime != 0) && (EndTime < nextTimeStamp))
|
|
EventInRange = FALSE;
|
|
|
|
//
|
|
// Make the Event Callbacks
|
|
//
|
|
|
|
if (EventInRange) {
|
|
PEVENT_TRACE pEvent = &pContext->Root->Event;
|
|
Status = WmipDoEventCallbacks( logfile, pEvent);
|
|
if (Status != ERROR_SUCCESS) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now advance to next event.
|
|
//
|
|
|
|
Status = WmipLookforRealTimeBuffers(logfile);
|
|
Done = (Status == ERROR_CANCELLED);
|
|
}
|
|
|
|
DoCleanup:
|
|
for (i=0; i < LogfileCount; i++) {
|
|
pContext = (PTRACELOG_CONTEXT)Logfiles[i]->Context;
|
|
if (pContext != NULL) {
|
|
WmipCleanupTraceLog(pContext);
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
ULONG
|
|
WMIAPI
|
|
WmiOpenTraceWithCursor(
|
|
IN PWMI_MERGE_ETL_CURSOR LogCursor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Main entry point to process Merged ETL file.
|
|
|
|
|
|
Arguments:
|
|
|
|
LogCursor pointer to WMI_MERGE_ETL_CURSOR
|
|
|
|
Returned Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
ULONG DosStatus = ERROR_INVALID_PARAMETER;
|
|
NTSTATUS Status;
|
|
PTRACELOG_CONTEXT HandleEntry = NULL;
|
|
TRACEHANDLE TraceHandle = (TRACEHANDLE)INVALID_HANDLE_VALUE;
|
|
PEVENT_TRACE_LOGFILEW Logfile;
|
|
PTRACELOG_CONTEXT pContext;
|
|
ULONG BufferSize;
|
|
PWMI_BUFFER_HEADER BufferHeader;
|
|
ULONG CpuNum;
|
|
BOOLEAN CpuBufferFound;
|
|
|
|
WmipInitProcessHeap();
|
|
|
|
if (LogCursor != NULL) {
|
|
LogCursor->Base = NULL;
|
|
LogCursor->TraceMappingHandle = NULL;
|
|
LogCursor->CursorVersion = WMI_MERGE_ETL_CURSOR_VERSION;
|
|
|
|
Logfile = &LogCursor->Logfile;
|
|
HandleEntry = WmipAllocateTraceHandle();
|
|
if (HandleEntry == NULL) {
|
|
DosStatus = ERROR_OUTOFMEMORY;
|
|
} else {
|
|
TraceHandle = HandleEntry->TraceHandle;
|
|
try {
|
|
DosStatus = WmipCopyLogfileInfo( HandleEntry,
|
|
Logfile,
|
|
TRUE
|
|
);
|
|
if (DosStatus == ERROR_SUCCESS) {
|
|
DosStatus = WmipCreateGuidMapping();
|
|
if (DosStatus == ERROR_SUCCESS) {
|
|
DosStatus = WmipProcessLogHeader( &HandleEntry->TraceHandle,
|
|
&Logfile,
|
|
1,
|
|
TRUE,
|
|
FALSE
|
|
);
|
|
}
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
DosStatus = WmipNtStatusToDosError( GetExceptionCode() );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DosStatus == ERROR_SUCCESS) {
|
|
//
|
|
// Now Make sure the bit was set, indicating a MERGED ETL
|
|
//
|
|
|
|
if ((LogCursor->Logfile.LogFileMode & EVENT_TRACE_RELOG_MODE) == 0) {
|
|
//
|
|
// It is not Merged ETL.
|
|
//
|
|
DosStatus = ERROR_BAD_FORMAT;
|
|
} else {
|
|
//
|
|
// Now find out the number of CPU's, Current event, etc.
|
|
//
|
|
pContext = LogCursor->Logfile.Context;
|
|
//
|
|
// Now Create a file Mapping
|
|
//
|
|
LogCursor->TraceMappingHandle =
|
|
CreateFileMapping(pContext->Handle,
|
|
0,
|
|
PAGE_READONLY,
|
|
0,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (LogCursor->TraceMappingHandle == NULL) {
|
|
DosStatus = GetLastError();
|
|
return DosStatus;
|
|
}
|
|
|
|
//
|
|
// MapView of the file
|
|
//
|
|
LogCursor->Base = MapViewOfFile(LogCursor->TraceMappingHandle,
|
|
FILE_MAP_READ,
|
|
0,
|
|
0,
|
|
0);
|
|
if (LogCursor->Base == NULL) {
|
|
DosStatus = GetLastError();
|
|
return DosStatus;
|
|
}
|
|
|
|
//
|
|
// Now find the first event of each CPU
|
|
//
|
|
pContext = LogCursor->Logfile.Context;
|
|
BufferSize = pContext->BufferSize;
|
|
LogCursor->CurrentCpu = 0;
|
|
|
|
for (CpuNum = 0; CpuNum < LogCursor->Logfile.LogfileHeader.NumberOfProcessors; CpuNum++) {
|
|
CpuBufferFound = FALSE;
|
|
while (CpuBufferFound == FALSE) {
|
|
BufferHeader = (PWMI_BUFFER_HEADER)
|
|
((UCHAR*) LogCursor->Base +
|
|
LogCursor->BufferCursor[CpuNum].CurrentBufferOffset.QuadPart);
|
|
|
|
if (BufferHeader->ClientContext.ProcessorNumber == CpuNum) {
|
|
CpuBufferFound = TRUE;
|
|
LogCursor->BufferCursor[CpuNum].BufferHeader = BufferHeader;
|
|
} else {
|
|
LogCursor->BufferCursor[CpuNum].CurrentBufferOffset.QuadPart += BufferSize;
|
|
if ((LogCursor->BufferCursor[CpuNum].CurrentBufferOffset.QuadPart/BufferSize) >=
|
|
LogCursor->Logfile.LogfileHeader.BuffersWritten) {
|
|
//
|
|
// Scanned the whole file;
|
|
//
|
|
LogCursor->BufferCursor[CpuNum].NoMoreEvents = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (CpuBufferFound) {
|
|
//
|
|
// Found the buffer, set the offset
|
|
//
|
|
ULONG Size;
|
|
WMI_HEADER_TYPE HeaderType = WMIHT_NONE;
|
|
PVOID pBuffer;
|
|
|
|
LogCursor->BufferCursor[CpuNum].BufferHeader = BufferHeader;
|
|
LogCursor->BufferCursor[CpuNum].CurrentEventOffset = sizeof(WMI_BUFFER_HEADER);
|
|
|
|
//
|
|
// Initialize the first event in each CPU Stream.
|
|
//
|
|
pBuffer = LogCursor->BufferCursor[CpuNum].BufferHeader;
|
|
|
|
HeaderType = WmiGetTraceHeader(pBuffer,
|
|
LogCursor->BufferCursor[CpuNum].CurrentEventOffset,
|
|
&Size);
|
|
|
|
if (HeaderType != WMIHT_NONE) {
|
|
WmipParseTraceEvent(pContext,
|
|
pBuffer,
|
|
LogCursor->BufferCursor[CpuNum].CurrentEventOffset,
|
|
HeaderType,
|
|
&LogCursor->BufferCursor[CpuNum].CurrentEvent,
|
|
sizeof(EVENT_TRACE));
|
|
|
|
LogCursor->BufferCursor[CpuNum].CurrentEventOffset += Size;
|
|
LogCursor->CurrentCpu = CpuNum;
|
|
|
|
} else {
|
|
//
|
|
// There is no event in this buffer.
|
|
//
|
|
DosStatus = ERROR_FILE_CORRUPT;
|
|
return DosStatus;
|
|
}
|
|
|
|
}
|
|
}
|
|
for (CpuNum = 0; CpuNum < LogCursor->Logfile.LogfileHeader.NumberOfProcessors; CpuNum++) {
|
|
//
|
|
// Find the first event for whole trace.
|
|
//
|
|
if (LogCursor->BufferCursor[CpuNum].NoMoreEvents == FALSE) {
|
|
if (LogCursor->BufferCursor[LogCursor->CurrentCpu].CurrentEvent.Header.TimeStamp.QuadPart >
|
|
LogCursor->BufferCursor[CpuNum].CurrentEvent.Header.TimeStamp.QuadPart) {
|
|
LogCursor->CurrentCpu = CpuNum;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if ( HandleEntry != NULL) {
|
|
WmipFreeTraceHandle(TraceHandle);
|
|
TraceHandle = (TRACEHANDLE)INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
WmipSetDosError(DosStatus);
|
|
return DosStatus;
|
|
}
|
|
|
|
|
|
ULONG
|
|
WMIAPI
|
|
WmiCloseTraceWithCursor(
|
|
IN PWMI_MERGE_ETL_CURSOR LogCursor
|
|
)
|
|
{
|
|
ULONG Status = ERROR_INVALID_PARAMETER;
|
|
|
|
if (LogCursor != NULL) {
|
|
if (LogCursor->Base != NULL) {
|
|
if (UnmapViewOfFile(LogCursor->Base) == FALSE) {
|
|
Status = GetLastError();
|
|
return Status;
|
|
} else {
|
|
Status = ERROR_SUCCESS;
|
|
}
|
|
} else {
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (Status != ERROR_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
if (LogCursor->TraceMappingHandle != NULL) {
|
|
if (CloseHandle(LogCursor->TraceMappingHandle) == FALSE) {
|
|
Status = GetLastError();
|
|
return Status;
|
|
} else {
|
|
Status = ERROR_SUCCESS;
|
|
}
|
|
} else {
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
WMIAPI
|
|
WmiConvertTimestamp(
|
|
OUT PLARGE_INTEGER DestTime,
|
|
IN PLARGE_INTEGER SrcTime,
|
|
IN PWMI_MERGE_ETL_CURSOR LogCursor
|
|
)
|
|
{
|
|
WmipCalculateCurrentTime(DestTime, SrcTime, LogCursor->Logfile.Context);
|
|
}
|
|
|
|
|
|
ULONG
|
|
WMIAPI
|
|
WmiGetNextEvent(
|
|
IN PWMI_MERGE_ETL_CURSOR LogCursor
|
|
)
|
|
{
|
|
ULONG CurrentCpu = LogCursor->CurrentCpu;
|
|
ULONG Size;
|
|
WMI_HEADER_TYPE HeaderType = WMIHT_NONE;
|
|
PVOID pBuffer;
|
|
PWMI_BUFFER_HEADER BufferHeader;
|
|
ULONG BufferSize;
|
|
PTRACELOG_CONTEXT pContext;
|
|
ULONG CpuNum;
|
|
NTSTATUS Status;
|
|
ULONG i;
|
|
BOOLEAN CpuBufferFound = FALSE;
|
|
BOOLEAN MoreEvents = FALSE;
|
|
|
|
if (LogCursor == NULL) {
|
|
return MoreEvents;
|
|
}
|
|
|
|
//
|
|
// Advance to the next event of this current CPU
|
|
//
|
|
retry:
|
|
|
|
pBuffer = LogCursor->BufferCursor[CurrentCpu].BufferHeader;
|
|
|
|
HeaderType = WmiGetTraceHeader(pBuffer,
|
|
LogCursor->BufferCursor[CurrentCpu].CurrentEventOffset,
|
|
&Size);
|
|
|
|
pContext = LogCursor->Logfile.Context;
|
|
if (HeaderType == WMIHT_NONE) {
|
|
//
|
|
// End of current buffer, advance to the next buffer for this CPU
|
|
//
|
|
BufferSize = pContext->BufferSize;
|
|
|
|
LogCursor->BufferCursor[CurrentCpu].CurrentBufferOffset.QuadPart
|
|
= LogCursor->BufferCursor[CurrentCpu].CurrentBufferOffset.QuadPart
|
|
+ BufferSize;
|
|
|
|
if ((LogCursor->BufferCursor[CurrentCpu].CurrentBufferOffset.QuadPart/BufferSize) >=
|
|
LogCursor->Logfile.LogfileHeader.BuffersWritten) {
|
|
//
|
|
// Scanned the whole file;
|
|
//
|
|
LogCursor->BufferCursor[CurrentCpu].NoMoreEvents = TRUE;
|
|
} else {
|
|
while (CpuBufferFound == FALSE) {
|
|
BufferHeader = (PWMI_BUFFER_HEADER)
|
|
((UCHAR*) LogCursor->Base +
|
|
LogCursor->BufferCursor[CurrentCpu].CurrentBufferOffset.QuadPart);
|
|
|
|
if (BufferHeader->ClientContext.ProcessorNumber == CurrentCpu) {
|
|
CpuBufferFound = TRUE;
|
|
} else {
|
|
LogCursor->BufferCursor[CurrentCpu].CurrentBufferOffset.QuadPart += BufferSize;
|
|
if ((LogCursor->BufferCursor[CurrentCpu].CurrentBufferOffset.QuadPart/BufferSize) >=
|
|
LogCursor->Logfile.LogfileHeader.BuffersWritten) {
|
|
//
|
|
// Scanned the whole file;
|
|
//
|
|
LogCursor->BufferCursor[CurrentCpu].NoMoreEvents = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (CpuBufferFound) {
|
|
//
|
|
// Found the buffer, set the offset
|
|
//
|
|
LogCursor->BufferCursor[CurrentCpu].BufferHeader = BufferHeader;
|
|
LogCursor->BufferCursor[CurrentCpu].CurrentEventOffset = sizeof(WMI_BUFFER_HEADER);
|
|
goto retry;
|
|
} else {
|
|
//
|
|
// No more buffer in this CPU stream.
|
|
//
|
|
LogCursor->BufferCursor[CurrentCpu].NoMoreEvents = TRUE;
|
|
}
|
|
} else {
|
|
WmipParseTraceEvent(pContext,
|
|
pBuffer,
|
|
LogCursor->BufferCursor[CurrentCpu].CurrentEventOffset,
|
|
HeaderType,
|
|
&LogCursor->BufferCursor[CurrentCpu].CurrentEvent,
|
|
sizeof(EVENT_TRACE));
|
|
|
|
LogCursor->BufferCursor[CurrentCpu].CurrentEventOffset += Size;
|
|
|
|
MoreEvents = TRUE;
|
|
}
|
|
|
|
//
|
|
// No more events in current CPU.
|
|
//
|
|
if (MoreEvents == FALSE) {
|
|
for (CurrentCpu=0; CurrentCpu<LogCursor->Logfile.LogfileHeader.NumberOfProcessors; CurrentCpu++) {
|
|
if (LogCursor->BufferCursor[CurrentCpu].NoMoreEvents == FALSE) {
|
|
LogCursor->CurrentCpu = CurrentCpu;
|
|
MoreEvents = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now find the CPU that has the next event
|
|
//
|
|
if (MoreEvents == TRUE) {
|
|
for (i=0; i<LogCursor->Logfile.LogfileHeader.NumberOfProcessors; i++) {
|
|
if (LogCursor->BufferCursor[i].NoMoreEvents == FALSE) {
|
|
if (LogCursor->BufferCursor[LogCursor->CurrentCpu].CurrentEvent.Header.TimeStamp.QuadPart >
|
|
LogCursor->BufferCursor[i].CurrentEvent.Header.TimeStamp.QuadPart) {
|
|
LogCursor->CurrentCpu = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Finish finding the next event.
|
|
//
|
|
return MoreEvents;
|
|
}
|
|
|
|
#endif
|
|
|