You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1608 lines
52 KiB
1608 lines
52 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
tracesup.c
|
|
|
|
Abstract:
|
|
|
|
Data Consumer Processing for version 1.2 and above
|
|
|
|
|
|
Author:
|
|
|
|
07-May-2002 Melur Raghuraman
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
|
|
#include "tracep.h"
|
|
|
|
#define TIME_IN_RANGE(x, a, b) ((a == 0) || (x >= a)) && ((b == 0) || (x <= b))
|
|
|
|
ULONG WmipTraceDebugLevel=0;
|
|
|
|
// 0 No Debugging. Default
|
|
// 1 Errors only
|
|
// 2 API Level Messages
|
|
// 4 Buffer Level Messages
|
|
// 8 Event Level Messages
|
|
// 16 All messages. Maximum
|
|
//
|
|
|
|
ULONG
|
|
EtwpProcessTraceLogEx(
|
|
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. It goes through each
|
|
event till the end of file, firing event callbacks (if any) along the 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.
|
|
|
|
--*/
|
|
{
|
|
PTRACELOG_CONTEXT pContext;
|
|
ULONG i;
|
|
ULONG Status;
|
|
LONGLONG CurrentTime = StartTime;
|
|
LONGLONG PreviousTime = 0;
|
|
LIST_ENTRY StreamListHead;
|
|
|
|
//
|
|
// This temporary list is a sorted list of streams. They are in
|
|
// ascending order of TimeStamp. When each stream events are
|
|
// exhausted they are dropped from this list. When this list is
|
|
// empty, we are done.
|
|
//
|
|
InitializeListHead(&StreamListHead);
|
|
|
|
Status = EtwpSetupLogFileStreams( &StreamListHead,
|
|
HandleArray,
|
|
Logfiles,
|
|
LogfileCount,
|
|
StartTime,
|
|
EndTime,
|
|
Unicode
|
|
);
|
|
if (Status != ERROR_SUCCESS) {
|
|
goto Cleanup;
|
|
}
|
|
//
|
|
// At this point, we have the local StreamList in sorted order for
|
|
// the first event.
|
|
//
|
|
|
|
while (!IsListEmpty(&StreamListHead) ) {
|
|
PLIST_ENTRY Next;
|
|
PTRACE_STREAM_CONTEXT pStream;
|
|
BOOLEAN EventInRange;
|
|
|
|
Next = RemoveHeadList( &StreamListHead );
|
|
pStream = CONTAINING_RECORD(Next, TRACE_STREAM_CONTEXT, Entry);
|
|
|
|
CurrentTime = pStream->CurrentEvent.Header.TimeStamp.QuadPart;
|
|
|
|
//
|
|
// Check to see if TimeStamp is forward moving...
|
|
//
|
|
|
|
if (ETW_LOG_MAX()) {
|
|
if (CurrentTime < PreviousTime) {
|
|
DbgPrint("ETW: TimeStamp error. Current %I64u Previous %I64u\n",
|
|
CurrentTime, PreviousTime);
|
|
}
|
|
PreviousTime = CurrentTime;
|
|
}
|
|
|
|
//
|
|
// Make The Callback for the Current Event
|
|
//
|
|
|
|
if ( TIME_IN_RANGE(CurrentTime, StartTime, EndTime) ) {
|
|
|
|
pStream->CbCount++;
|
|
Status = EtwpDoEventCallbacks( &pStream->pContext->Logfile,
|
|
&pStream->CurrentEvent
|
|
);
|
|
pStream->pContext->LastTimeStamp = CurrentTime;
|
|
}
|
|
else {
|
|
if (ETW_LOG_MAX()) {
|
|
DbgPrint("ETW: EventTime %I64u not in Range %I64u-%I64u\n",
|
|
CurrentTime, StartTime, EndTime);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Advance to next event for the stream
|
|
//
|
|
|
|
Status = EtwpAdvanceToNewEventEx(&StreamListHead, pStream);
|
|
//
|
|
// If the caller indicates to quit via the buffer callback, quit
|
|
//
|
|
if (Status == ERROR_CANCELLED)
|
|
{
|
|
if (ETW_LOG_API()) {
|
|
DbgPrint("ETW: Processing Cancelled \n");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
for (i=0; i < LogfileCount; i++) {
|
|
pContext = (PTRACELOG_CONTEXT)Logfiles[i]->Context;
|
|
if (pContext != NULL) {
|
|
|
|
if ((Status == ERROR_SUCCESS) &&
|
|
(pContext->ConversionFlags & EVENT_TRACE_READ_BEHIND) ) {
|
|
EtwpCleanupTraceLog(pContext, TRUE);
|
|
}
|
|
else {
|
|
EtwpCleanupTraceLog(pContext, FALSE);
|
|
}
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// EtwpSetupLogFileStreams will set up the stream for each logfile.
|
|
//
|
|
ULONG
|
|
EtwpSetupLogFileStreams(
|
|
PLIST_ENTRY pStreamListHead,
|
|
PTRACEHANDLE HandleArray,
|
|
PEVENT_TRACE_LOGFILEW *Logfiles,
|
|
ULONG LogfileCount,
|
|
LONGLONG StartTime,
|
|
LONGLONG EndTime,
|
|
ULONG Unicode
|
|
)
|
|
{
|
|
ULONG Status = ERROR_SUCCESS;
|
|
long NumProc;
|
|
PTRACELOG_CONTEXT pContext;
|
|
long i, j;
|
|
|
|
//
|
|
// Breakdown each logfile into streams
|
|
//
|
|
|
|
for (i=0; i<(long)LogfileCount; i++) {
|
|
|
|
NumProc = Logfiles[i]->LogfileHeader.NumberOfProcessors;
|
|
pContext = Logfiles[i]->Context;
|
|
|
|
if (NumProc == 0) NumProc = 1;
|
|
|
|
//
|
|
// Before Setting up the streams find the last buffer range
|
|
//
|
|
EtwpGetLastBufferWithMarker( pContext );
|
|
|
|
|
|
//
|
|
// Set up the Generic Streams
|
|
//
|
|
|
|
for (j = 0; j < NumProc; j++) {
|
|
|
|
Status = EtwpAddTraceStream( pStreamListHead,
|
|
pContext,
|
|
WMI_BUFFER_TYPE_GENERIC,
|
|
StartTime,
|
|
EndTime,
|
|
j
|
|
);
|
|
|
|
//
|
|
// The LogFileHeader is logged as a RunDown buffer.
|
|
//
|
|
|
|
|
|
Status = EtwpAddTraceStream( pStreamListHead,
|
|
pContext,
|
|
WMI_BUFFER_TYPE_RUNDOWN,
|
|
StartTime,
|
|
EndTime,
|
|
j
|
|
);
|
|
|
|
if (Logfiles[i]->IsKernelTrace) {
|
|
if ( PerfIsGroupOnInGroupMask(PERF_CONTEXT_SWITCH, &pContext->PerfGroupMask) ) {
|
|
Status = EtwpAddTraceStream( pStreamListHead,
|
|
pContext,
|
|
WMI_BUFFER_TYPE_CTX_SWAP,
|
|
StartTime, EndTime,
|
|
j
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// This routine will find the last buffer in the logfile with
|
|
// WMI_BUFFER_FLAG_FLUSH_MARKER set. We can only process upto
|
|
// this point.
|
|
//
|
|
|
|
|
|
|
|
ULONG
|
|
EtwpGetLastBufferWithMarker(
|
|
PTRACELOG_CONTEXT pContext
|
|
)
|
|
{
|
|
|
|
ULONGLONG ReadPosition;
|
|
PVOID pTmpBuffer;
|
|
ULONG BufferSize;
|
|
ULONG nBytesRead=0;
|
|
PWMI_BUFFER_HEADER pHeader;
|
|
ULONG Status;
|
|
|
|
EtwpAssert(pContext != NULL);
|
|
|
|
pContext->MaxReadPosition = 0;
|
|
BufferSize = pContext->BufferSize;
|
|
|
|
if (ETW_LOG_MAX()) {
|
|
|
|
ReadPosition = 0;
|
|
|
|
pTmpBuffer = EtwpAlloc(BufferSize);
|
|
|
|
if (pTmpBuffer == NULL) {
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
dumpmore:
|
|
pContext->AsynchRead.Offset = (DWORD)(ReadPosition & 0xFFFFFFFF);
|
|
pContext->AsynchRead.OffsetHigh = (DWORD)(ReadPosition >> 32);
|
|
|
|
Status = EtwpSynchReadFile(pContext->Handle,
|
|
pTmpBuffer,
|
|
BufferSize,
|
|
&nBytesRead,
|
|
&pContext->AsynchRead);
|
|
|
|
if (nBytesRead == 0) {
|
|
DbgPrint("End OF File reached\n");
|
|
EtwpFree(pTmpBuffer);
|
|
}
|
|
else {
|
|
|
|
PWMI_BUFFER_HEADER pHeader = (PWMI_BUFFER_HEADER) pTmpBuffer;
|
|
PWMI_CLIENT_CONTEXT ClientContext = (PWMI_CLIENT_CONTEXT)&pHeader->Wnode.ClientContext;
|
|
|
|
DbgPrint("ReadPos: %I64u BufferType %d BufferFlag %d Proc %d \n",
|
|
ReadPosition, pHeader->BufferType, pHeader->BufferFlag, ClientContext->ProcessorNumber);
|
|
|
|
ReadPosition += BufferSize;
|
|
goto dumpmore;
|
|
|
|
}
|
|
}
|
|
|
|
if (pContext->Logfile.LogFileMode & EVENT_TRACE_FILE_MODE_CIRCULAR) {
|
|
pContext->MaxReadPosition = pContext->Logfile.LogfileHeader.MaximumFileSize * 1024 * 1024;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
ReadPosition = pContext->BufferCount * pContext->BufferSize;
|
|
|
|
if ( (pContext->ConversionFlags & EVENT_TRACE_READ_BEHIND) !=
|
|
EVENT_TRACE_READ_BEHIND) {
|
|
pContext->MaxReadPosition = ReadPosition;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
if (ReadPosition < BufferSize) {
|
|
if (ETW_LOG_ERROR()) {
|
|
DbgPrint("ETW: ReadPosition %I64u is less than BufferSize %d \n",
|
|
ReadPosition, BufferSize);
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
//
|
|
// Set the ReadPosition to the start of last buffer
|
|
//
|
|
|
|
ReadPosition -= BufferSize;
|
|
pTmpBuffer = EtwpAlloc(BufferSize);
|
|
|
|
if (pTmpBuffer == NULL) {
|
|
if (ETW_LOG_ERROR()) {
|
|
DbgPrint("ETW: Allocation Failed %d Bytes, Line %d\n",
|
|
BufferSize, __LINE__);
|
|
}
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
Retry:
|
|
|
|
pContext->AsynchRead.Offset = (DWORD)(ReadPosition & 0xFFFFFFFF);
|
|
pContext->AsynchRead.OffsetHigh = (DWORD)(ReadPosition >> 32);
|
|
|
|
Status = EtwpSynchReadFile(pContext->Handle,
|
|
pTmpBuffer,
|
|
BufferSize,
|
|
&nBytesRead,
|
|
&pContext->AsynchRead);
|
|
|
|
if (nBytesRead == 0) {
|
|
EtwpDebugPrint(("ETW Error: No data in file. \n"));
|
|
pContext->MaxReadPosition = 0;
|
|
EtwpFree(pTmpBuffer);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
pHeader = (PWMI_BUFFER_HEADER) pTmpBuffer;
|
|
|
|
if (pHeader->BufferFlag & WMI_BUFFER_FLAG_FLUSH_MARKER) {
|
|
//
|
|
// Found the Marker. It is safe to process events upto this point.
|
|
//
|
|
if (ETW_LOG_BUFFER()) {
|
|
DbgPrint("ETW: Found Flush Marker at %I64u\n", ReadPosition);
|
|
}
|
|
pContext->MaxReadPosition = ReadPosition;
|
|
EtwpFree(pTmpBuffer);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
if (ReadPosition < BufferSize) {
|
|
EtwpAssert(ReadPosition == 0);
|
|
pContext->MaxReadPosition = 0;
|
|
EtwpFree(pTmpBuffer);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
else {
|
|
ReadPosition -= BufferSize;
|
|
goto Retry;
|
|
}
|
|
|
|
if (ETW_LOG_ERROR()) {
|
|
DbgPrint("ETW: Could not find Last Marker. Corrupt File!\n");
|
|
}
|
|
|
|
EtwpAssert(FALSE);
|
|
|
|
EtwpFree(pTmpBuffer);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
ULONG
|
|
EtwpAddTraceStream(
|
|
PLIST_ENTRY pStreamListHead,
|
|
PTRACELOG_CONTEXT pContext,
|
|
USHORT StreamType,
|
|
LONGLONG StartTime,
|
|
LONGLONG EndTime,
|
|
ULONG ProcessorNumber
|
|
)
|
|
{
|
|
ULONG Status = ERROR_SUCCESS;
|
|
PTRACE_STREAM_CONTEXT pStream;
|
|
|
|
|
|
pStream = (PTRACE_STREAM_CONTEXT) EtwpAlloc( sizeof(TRACE_STREAM_CONTEXT) );
|
|
if (pStream == NULL) {
|
|
if (ETW_LOG_ERROR()) {
|
|
DbgPrint("ETW: Allocation Failed %d Bytes, Line %d\n",
|
|
sizeof(TRACE_STREAM_CONTEXT), __LINE__);
|
|
}
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
RtlZeroMemory(pStream, sizeof(TRACE_STREAM_CONTEXT) );
|
|
|
|
pStream->Type = StreamType;
|
|
pStream->ProcessorNumber = ProcessorNumber;
|
|
|
|
pStream->pContext = pContext;
|
|
|
|
|
|
EtwpAssert( pContext != NULL );
|
|
EtwpAssert(pContext->BufferSize != 0);
|
|
|
|
pStream->StreamBuffer = EtwpAlloc(pContext->BufferSize);
|
|
|
|
if (pStream->StreamBuffer == NULL) {
|
|
if (ETW_LOG_ERROR()) {
|
|
DbgPrint("ETW: Allocation Failed %d Bytes, Line %d\n",
|
|
pContext->BufferSize, __LINE__);
|
|
}
|
|
EtwpFree(pStream);
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
//
|
|
// For Circular, Jump off to First Buffer
|
|
// For Non-circular with StartTime specified, try to use the cached offset
|
|
//
|
|
|
|
if (pContext->Logfile.LogFileMode & EVENT_TRACE_FILE_MODE_CIRCULAR) {
|
|
if (StreamType != WMI_BUFFER_TYPE_RUNDOWN) {
|
|
pStream->ReadPosition = pContext->FirstBuffer *
|
|
pContext->BufferSize;
|
|
}
|
|
else {
|
|
pStream->ReadPosition = 0;
|
|
}
|
|
}
|
|
else {
|
|
if ( (StartTime > 0) && (StartTime >= pContext->LastTimeStamp) ) {
|
|
pStream->ReadPosition = pContext->OldMaxReadPosition +
|
|
pContext->BufferSize;
|
|
|
|
if (pStream->ReadPosition > pContext->MaxReadPosition) {
|
|
EtwpFree(pStream->StreamBuffer);
|
|
EtwpFree(pStream);
|
|
return Status;
|
|
}
|
|
}
|
|
}
|
|
|
|
Status = EtwpGetNextBuffer(pStream);
|
|
|
|
//
|
|
// If Read failed, it is not fatal. There are no
|
|
// events in this stream. Set the stream to not active and
|
|
// continue.
|
|
|
|
if (pStream->bActive) {
|
|
if (ETW_LOG_BUFFER()) {
|
|
DbgPrint("ETW: Added Stream %d Proc %d ReadPosition %I64u\n",
|
|
pStream->Type, pStream->ProcessorNumber, pStream->ReadPosition);
|
|
}
|
|
EtwpAdvanceToNewEventEx(pStreamListHead, pStream);
|
|
InsertTailList(&pContext->StreamListHead, &pStream->AllocEntry);
|
|
}
|
|
else {
|
|
EtwpFree(pStream->StreamBuffer);
|
|
EtwpFree(pStream);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
EtwpGetNextBuffer(
|
|
PTRACE_STREAM_CONTEXT pStream
|
|
)
|
|
{
|
|
PEVENT_TRACE_LOGFILE LogFile;
|
|
PTRACELOG_CONTEXT pContext;
|
|
HANDLE hFile;
|
|
NTSTATUS NtStatus;
|
|
ULONG BufferSize;
|
|
ULONGLONG ReadPosition;
|
|
PWMI_CLIENT_CONTEXT ClientContext;
|
|
ULONG nBytesRead=0;
|
|
PWMI_BUFFER_HEADER pHeader;
|
|
ULONG Status;
|
|
ULONGLONG FirstOffset, LastOffset, StartOffset;
|
|
|
|
ULONG ProcessorNumber = pStream->ProcessorNumber;
|
|
|
|
pContext = pStream->pContext;
|
|
LogFile = &pContext->Logfile;
|
|
hFile = pContext->Handle;
|
|
|
|
BufferSize = pContext->BufferSize;
|
|
|
|
pStream->EventCount = 0;
|
|
|
|
FirstOffset = pContext->FirstBuffer * BufferSize;
|
|
LastOffset = pContext->LastBuffer * BufferSize;
|
|
StartOffset = pContext->StartBuffer * BufferSize;
|
|
|
|
//
|
|
// Need to handle FileType properly here.
|
|
//
|
|
retry:
|
|
|
|
ReadPosition = pStream->ReadPosition;
|
|
|
|
if (ReadPosition > pContext->MaxReadPosition) {
|
|
//
|
|
// Only Valid for Sequential. For read behind mode, we do not
|
|
// process past the MaxReadPosition.
|
|
//
|
|
|
|
if (pContext->ConversionFlags & EVENT_TRACE_READ_BEHIND) {
|
|
if (ETW_LOG_BUFFER()) {
|
|
DbgPrint("ETW: Reached MaxReadPosition for Stream %d Proc %d\n", pStream->Type, pStream->ProcessorNumber);
|
|
}
|
|
pStream->bActive = FALSE;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (pContext->Logfile.LogFileMode & EVENT_TRACE_FILE_MODE_CIRCULAR) {
|
|
|
|
//
|
|
// If we have scanned once and reached FirstBuffer, then we are done.
|
|
//
|
|
|
|
if ( (ReadPosition >= FirstOffset) && pStream->ScanDone) {
|
|
if (ETW_LOG_BUFFER()) {
|
|
DbgPrint("ETW: Stream %d Proc %d Circular Mode Done.\n",
|
|
pStream->Type, pStream->ProcessorNumber);
|
|
}
|
|
pStream->bActive = FALSE;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
pContext->AsynchRead.Offset = (DWORD)(ReadPosition & 0xFFFFFFFF);
|
|
pContext->AsynchRead.OffsetHigh = (DWORD)(ReadPosition >> 32);
|
|
|
|
Status = EtwpSynchReadFile(pContext->Handle,
|
|
(LPVOID)pStream->StreamBuffer,
|
|
BufferSize,
|
|
&nBytesRead,
|
|
&pContext->AsynchRead);
|
|
|
|
if (nBytesRead == 0) {
|
|
|
|
if (pContext->Logfile.LogFileMode & EVENT_TRACE_FILE_MODE_CIRCULAR) {
|
|
if (ReadPosition >= LastOffset && !pStream->ScanDone) {
|
|
|
|
//
|
|
// Rundown stream ends when we reach EOF
|
|
//
|
|
|
|
if (pStream->Type == WMI_BUFFER_TYPE_RUNDOWN) {
|
|
pStream->ScanDone = TRUE;
|
|
pStream->bActive = FALSE;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
pStream->ReadPosition = StartOffset;
|
|
pStream->ScanDone = TRUE;
|
|
goto retry;
|
|
}
|
|
else {
|
|
pStream->bActive = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
pStream->bActive = FALSE;
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
pStream->ReadPosition += BufferSize;
|
|
|
|
pHeader = (PWMI_BUFFER_HEADER) pStream->StreamBuffer;
|
|
|
|
ClientContext = (PWMI_CLIENT_CONTEXT)&pHeader->Wnode.ClientContext;
|
|
|
|
if ( ClientContext->ProcessorNumber
|
|
>= LogFile->LogfileHeader.NumberOfProcessors) {
|
|
ClientContext->ProcessorNumber = (UCHAR) 0;
|
|
}
|
|
|
|
if ( (pStream->Type != pHeader->BufferType) ||
|
|
(ClientContext->ProcessorNumber != ProcessorNumber) ) {
|
|
goto retry;
|
|
}
|
|
|
|
//
|
|
// If we got here, then we have the right buffer. Set the first
|
|
// offset so that events can parsed.
|
|
//
|
|
|
|
pStream->CurrentOffset = sizeof(WMI_BUFFER_HEADER);
|
|
|
|
pStream->bActive = TRUE;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
EtwpAdvanceToNewEventEx(
|
|
PLIST_ENTRY pStreamListHead,
|
|
PTRACE_STREAM_CONTEXT pStream
|
|
)
|
|
{
|
|
ULONG Size=0;
|
|
WMI_HEADER_TYPE HdrType = WMIHT_NONE;
|
|
PVOID pBuffer;
|
|
ULONG Offset;
|
|
PTRACELOG_CONTEXT pContext;
|
|
PEVENT_TRACE pEvent;
|
|
ULONG Status;
|
|
PEVENT_TRACE_LOGFILEW logfile;
|
|
//
|
|
// This routine advances to next event for this stream
|
|
//
|
|
Retry:
|
|
|
|
pBuffer = pStream->StreamBuffer;
|
|
Offset = pStream->CurrentOffset;
|
|
pContext = pStream->pContext;
|
|
|
|
EtwpAssert(pBuffer != NULL);
|
|
EtwpAssert(pContext != NULL);
|
|
|
|
pEvent = &pStream->CurrentEvent;
|
|
|
|
if ((HdrType = WmiGetTraceHeader(pBuffer, Offset, &Size)) != WMIHT_NONE) {
|
|
if (Size > 0) {
|
|
LONGLONG TimeStamp, NextTimeStamp;
|
|
PLIST_ENTRY Head, Next;
|
|
PTRACE_STREAM_CONTEXT CurrentStream;
|
|
|
|
EtwpParseTraceEvent( pContext,
|
|
pBuffer,
|
|
Offset,
|
|
HdrType,
|
|
pEvent,
|
|
sizeof(EVENT_TRACE)
|
|
);
|
|
pStream->CurrentOffset += Size;
|
|
pStream->EventCount++;
|
|
|
|
|
|
Head = pStreamListHead;
|
|
Next = Head->Flink;
|
|
TimeStamp = pStream->CurrentEvent.Header.TimeStamp.QuadPart;
|
|
|
|
while (Head != Next) {
|
|
CurrentStream = CONTAINING_RECORD(Next, TRACE_STREAM_CONTEXT, Entry);
|
|
NextTimeStamp = CurrentStream->CurrentEvent.Header.TimeStamp.QuadPart;
|
|
if (TimeStamp < NextTimeStamp) {
|
|
|
|
InsertHeadList(Next->Blink, &pStream->Entry);
|
|
break;
|
|
|
|
}
|
|
//
|
|
// In case of a Tie in TimeStamps, we try to order the events
|
|
// using Sequence numbers, if available. FieldTypeFlags in the
|
|
// header indicates if this event has a sequence number.
|
|
//
|
|
else if (TimeStamp == NextTimeStamp) {
|
|
USHORT pFlags = pStream->CurrentEvent.Header.FieldTypeFlags;
|
|
USHORT cFlags = CurrentStream->CurrentEvent.Header.FieldTypeFlags;
|
|
|
|
if (pFlags & EVENT_TRACE_USE_SEQUENCE) {
|
|
if ( (cFlags & EVENT_TRACE_USE_SEQUENCE) &&
|
|
(pStream->CurrentEvent.InstanceId <
|
|
CurrentStream->CurrentEvent.InstanceId)) {
|
|
InsertHeadList(Next->Blink, &pStream->Entry);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
InsertHeadList(Next->Blink, &pStream->Entry);
|
|
break;
|
|
}
|
|
}
|
|
Next = Next->Flink;
|
|
}
|
|
|
|
if (Next == Head) {
|
|
InsertTailList(Head, &pStream->Entry);
|
|
}
|
|
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// No more events in this buffer. Advance to next buffer and retry.
|
|
//
|
|
|
|
logfile = &pContext->Logfile;
|
|
|
|
if (logfile->BufferCallback) {
|
|
ULONG bRetVal;
|
|
PWMI_BUFFER_HEADER pHeader = (PWMI_BUFFER_HEADER)pBuffer;
|
|
logfile->Filled = (ULONG)pHeader->Offset;
|
|
logfile->BuffersRead++;
|
|
try {
|
|
bRetVal = (*logfile->BufferCallback) (logfile);
|
|
if (!bRetVal) {
|
|
return ERROR_CANCELLED;
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
EtwpDebugPrint(("TRACE: BufferCallback threw exception %X\n",
|
|
Status));
|
|
EtwpSetDosError(EtwpNtStatusToDosError(Status));
|
|
return ERROR_CANCELLED; // so that realtime also cleans up.
|
|
}
|
|
}
|
|
|
|
if (ETW_LOG_BUFFER()) {
|
|
PWMI_BUFFER_HEADER pHeader = (PWMI_BUFFER_HEADER)pBuffer;
|
|
DbgPrint("ETW: %d Type %d Flag %d Proc %d Events %d Filled %d Offset %d ReadPos %I64u TimeStamp %I64u\n",
|
|
logfile->BuffersRead, pStream->Type, pHeader->BufferFlag, pStream->ProcessorNumber, pStream->EventCount, logfile->Filled,
|
|
pStream->CurrentOffset, pStream->ReadPosition, pContext->LastTimeStamp);
|
|
}
|
|
|
|
Status = EtwpGetNextBuffer(pStream);
|
|
|
|
if (pStream->bActive) {
|
|
goto Retry;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
WMI_HEADER_TYPE
|
|
WMIAPI
|
|
WmiGetTraceHeader(
|
|
IN PVOID LogBuffer,
|
|
IN ULONG Offset,
|
|
OUT ULONG *Size
|
|
)
|
|
{
|
|
ULONG Status = ERROR_SUCCESS;
|
|
ULONG TraceType;
|
|
|
|
try {
|
|
|
|
TraceType = EtwpGetNextEventOffsetType(
|
|
(PUCHAR)LogBuffer,
|
|
Offset,
|
|
Size
|
|
);
|
|
|
|
return EtwpConvertTraceTypeToEnum(TraceType);
|
|
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
EtwpDebugPrint(("TRACE: WmiGetTraceHeader threw exception %X\n",
|
|
Status));
|
|
Status = EtwpSetDosError(EtwpNtStatusToDosError(Status));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ULONG
|
|
EtwpGetNextEventOffsetType(
|
|
PUCHAR pBuffer,
|
|
ULONG Offset,
|
|
PULONG RetSize
|
|
)
|
|
{
|
|
ULONG nSize;
|
|
ULONG TraceMarker;
|
|
ULONG TraceType = 0;
|
|
PWMI_BUFFER_HEADER Header;
|
|
ULONG Alignment;
|
|
ULONG BufferSize;
|
|
|
|
if (RetSize != NULL) {
|
|
*RetSize = 0;
|
|
}
|
|
if (pBuffer == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
Header = (PWMI_BUFFER_HEADER)pBuffer;
|
|
|
|
Alignment = Header->ClientContext.Alignment;
|
|
BufferSize = Header->Wnode.BufferSize;
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
if (Alignment != 0) {
|
|
nSize = (ULONG) ALIGN_TO_POWER2(nSize, Alignment);
|
|
}
|
|
|
|
//
|
|
// Check for larger than BufferSize
|
|
//
|
|
|
|
if (nSize >= BufferSize) {
|
|
return 0;
|
|
}
|
|
|
|
if (RetSize != NULL) {
|
|
*RetSize = nSize;
|
|
}
|
|
|
|
return TraceType;
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
EtwpParseTraceEvent(
|
|
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 = EtwpCreateGuidMapping();
|
|
if (Status != ERROR_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
try {
|
|
|
|
RtlZeroMemory(EventInfo, sizeof(EVENT_TRACE));
|
|
|
|
pEvent = (void*) ((PUCHAR)LogBuffer + Offset);
|
|
|
|
EtwpCopyCurrentEvent(pContext,
|
|
pEvent,
|
|
EventInfo,
|
|
EtwpConvertEnumToTraceType(HeaderType),
|
|
(PWMI_BUFFER_HEADER)LogBuffer
|
|
);
|
|
|
|
( (PEVENT_TRACE)EventInfo)->ClientContext = Header->Wnode.ClientContext;
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
EtwpDebugPrint(("TRACE: EtwpParseTraceEvent threw exception %X\n",
|
|
Status));
|
|
Status = EtwpSetDosError(EtwpNtStatusToDosError(Status));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
ULONG
|
|
EtwpCopyCurrentEvent(
|
|
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 EtwpSetDosError(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 = EtwpGroupTypeToGuid(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;
|
|
|
|
EtwpCalculateCurrentTime( &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 = EtwpGroupTypeToGuid(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;
|
|
|
|
EtwpCalculateCurrentTime( &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 = EtwpGroupTypeToGuid(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;
|
|
|
|
EtwpCalculateCurrentTime( &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)
|
|
);
|
|
EtwpCalculateCurrentTime( &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:
|
|
{
|
|
// new scheme using EVENT_INSTANCE_GUID_HEADER
|
|
if (((pContext->Logfile.LogfileHeader.VersionDetail.SubVersion >= 1) &&
|
|
(pContext->Logfile.LogfileHeader.VersionDetail.SubMinorVersion >= 1)) ||
|
|
pContext->Logfile.LogFileMode & EVENT_TRACE_REAL_TIME_MODE) {
|
|
PEVENT_INSTANCE_GUID_HEADER pInstanceHeader;
|
|
pInstanceHeader = (PEVENT_INSTANCE_GUID_HEADER) pHeader;
|
|
RtlZeroMemory(pEvent, sizeof(EVENT_TRACE));
|
|
pWnode = (PEVENT_TRACE_HEADER) &pEvent->Header;
|
|
// EVENT_INSTANCE_GUID_HEADER is the same as the first part of the EVENT_TRACE.
|
|
// No need to copy IIDs and parent GUID
|
|
RtlCopyMemory(pWnode,
|
|
pInstanceHeader,
|
|
sizeof(EVENT_INSTANCE_GUID_HEADER)
|
|
);
|
|
EtwpCalculateCurrentTime( &pWnode->TimeStamp,
|
|
&pInstanceHeader->TimeStamp,
|
|
pContext );
|
|
|
|
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_GUID_HEADER)) {
|
|
|
|
pEvent->MofData = (PVOID) ((char*)pInstanceHeader +
|
|
sizeof(EVENT_INSTANCE_GUID_HEADER));
|
|
pEvent->MofLength = pInstanceHeader->Size -
|
|
sizeof(EVENT_INSTANCE_GUID_HEADER);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
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)
|
|
);
|
|
EtwpCalculateCurrentTime( &pWnode->TimeStamp,
|
|
&pInstanceHeader->TimeStamp,
|
|
pContext );
|
|
|
|
pEvent->InstanceId = pInstanceHeader->InstanceId;
|
|
pEvent->ParentInstanceId = pInstanceHeader->ParentInstanceId;
|
|
|
|
pGuid = EtwpGuidMapHandleToGuid(&pContext->GuidMapListHead, pInstanceHeader->RegHandle);
|
|
if (pGuid != NULL) {
|
|
pEvent->Header.Guid = *pGuid;
|
|
}
|
|
else {
|
|
RtlZeroMemory(&pEvent->Header.Guid, sizeof(GUID));
|
|
}
|
|
|
|
if (pInstanceHeader->ParentRegHandle != (ULONGLONG)0) {
|
|
pGuid = EtwpGuidMapHandleToGuid(
|
|
&pContext->GuidMapListHead,
|
|
pInstanceHeader->ParentRegHandle);
|
|
if (pGuid != NULL) {
|
|
pEvent->ParentGuid = *pGuid;
|
|
}
|
|
#ifdef DBG
|
|
else {
|
|
EtwpAssert(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;
|
|
EtwpCalculateCurrentTime( &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 pTmpWnode = (PWNODE_HEADER) pHeader;
|
|
RtlZeroMemory(pEvent, sizeof(EVENT_TRACE));
|
|
RtlCopyMemory(&pEvent->Header, pTmpWnode, sizeof(WNODE_HEADER));
|
|
pEvent->MofData = (PVOID) pTmpWnode;
|
|
pEvent->MofLength = pTmpWnode->BufferSize;
|
|
break;
|
|
}
|
|
case TRACE_HEADER_TYPE_MESSAGE:
|
|
{
|
|
PMESSAGE_TRACE pMsg = (PMESSAGE_TRACE) pHeader;
|
|
USHORT MessageFlags = 0;
|
|
UCHAR * pMessageData;
|
|
ULONG MessageLength;
|
|
|
|
RtlZeroMemory(pEvent, sizeof(EVENT_TRACE));
|
|
RtlCopyMemory(&pEvent->Header, pMsg, sizeof(MESSAGE_TRACE_HEADER));
|
|
//
|
|
// Now Process the Trace Message header options
|
|
//
|
|
|
|
pMessageData = (char *)pMsg + sizeof(MESSAGE_TRACE_HEADER);
|
|
MessageLength = pMsg->MessageHeader.Size;
|
|
MessageFlags = ((PMESSAGE_TRACE_HEADER)pEvent)->Packet.OptionFlags;
|
|
|
|
// Note that the order in which these are added is critical New
|
|
// entries must be added at the end!
|
|
//
|
|
// [First Entry] Sequence Number
|
|
if (MessageFlags&TRACE_MESSAGE_SEQUENCE) {
|
|
if (MessageLength >= sizeof(ULONG)) {
|
|
RtlCopyMemory(&pEvent->InstanceId, pMessageData, sizeof(ULONG));
|
|
pMessageData += sizeof(ULONG);
|
|
MessageLength -= sizeof(ULONG);
|
|
//
|
|
// Software tracing tools look at this (overlapped) field, so
|
|
// we should not overwrite it.
|
|
//
|
|
pEvent->Header.FieldTypeFlags |= EVENT_TRACE_USE_SEQUENCE;
|
|
} else {
|
|
goto TraceMessageShort;
|
|
}
|
|
}
|
|
|
|
// [Second Entry] GUID ? or CompnentID ?
|
|
if (MessageFlags&TRACE_MESSAGE_COMPONENTID) {
|
|
if (MessageLength >= sizeof(ULONG)) {
|
|
RtlCopyMemory(&pEvent->Header.Guid,pMessageData,sizeof(ULONG)) ;
|
|
pMessageData += sizeof(ULONG);
|
|
MessageLength -= sizeof(ULONG);
|
|
} else {
|
|
goto TraceMessageShort;
|
|
}
|
|
} else if (MessageFlags&TRACE_MESSAGE_GUID) { // Can't have both
|
|
if (MessageLength >= sizeof(GUID)) {
|
|
RtlCopyMemory(&pEvent->Header.Guid,pMessageData, sizeof(GUID));
|
|
pMessageData += sizeof(GUID);
|
|
MessageLength -= sizeof(GUID);
|
|
} else {
|
|
goto TraceMessageShort;
|
|
}
|
|
}
|
|
|
|
// [Third Entry] Timestamp?
|
|
if (MessageFlags&TRACE_MESSAGE_TIMESTAMP) {
|
|
LARGE_INTEGER TimeStamp;
|
|
if (MessageLength >= sizeof(LARGE_INTEGER)) {
|
|
RtlCopyMemory(&TimeStamp,pMessageData,sizeof(LARGE_INTEGER));
|
|
pMessageData += sizeof(LARGE_INTEGER);
|
|
MessageLength -= sizeof(LARGE_INTEGER);
|
|
EtwpCalculateCurrentTime( &pEvent->Header.TimeStamp,
|
|
&TimeStamp,
|
|
pContext );
|
|
} else {
|
|
goto TraceMessageShort;
|
|
}
|
|
}
|
|
|
|
// [Fourth Entry] System Information?
|
|
if (MessageFlags&TRACE_MESSAGE_SYSTEMINFO) {
|
|
if (MessageLength >= 2 * sizeof(ULONG)) {
|
|
RtlCopyMemory(&pEvent->Header.ThreadId, pMessageData, sizeof(ULONG)) ;
|
|
pMessageData += sizeof(ULONG);
|
|
MessageLength -=sizeof(ULONG);
|
|
RtlCopyMemory(&pEvent->Header.ProcessId,pMessageData, sizeof(ULONG)) ;
|
|
pMessageData += sizeof(ULONG);
|
|
MessageLength -=sizeof(ULONG);
|
|
} else {
|
|
goto TraceMessageShort;
|
|
}
|
|
}
|
|
//
|
|
// Add New Header Entries immediately before this comment!
|
|
//
|
|
|
|
TraceMessageShort:
|
|
|
|
if (UseBasePtr) {
|
|
pEvent->MofData = (PVOID)pHeader;
|
|
pEvent->MofLength = pMsg->MessageHeader.Size;
|
|
}
|
|
else {
|
|
pEvent->MofData = (PVOID)&(pMsg->Data) ;
|
|
if (pMsg->MessageHeader.Size >= sizeof(MESSAGE_TRACE_HEADER) ) {
|
|
pEvent->MofLength = pMsg->MessageHeader.Size - sizeof(MESSAGE_TRACE_HEADER);
|
|
} else {
|
|
pEvent->MofLength = 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default: // Assumed to be REAL WNODE
|
|
break;
|
|
}
|
|
|
|
return EtwpSetDosError(ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
LPGUID
|
|
EtwpGroupTypeToGuid(
|
|
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.
|
|
|
|
This routine assumes that the EventMapList is available for use.
|
|
It is allocated once via ProcessTrace and never deleted.
|
|
|
|
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
|
|
EtwpCalculateCurrentTime (
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|