windows-nt/Source/XPSP1/NT/base/ntos/wmi/traceapi.c

2560 lines
78 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
traceapi.c
Abstract:
This is the source file that implements the published routines of
the performance event tracing and logging facility. These routines are
be declared in ntos\inc\wmi.h
Author:
Jee Fung Pang (jeepang) 03-Jan-2000
Revision History:
--*/
#include <ntos.h>
#include <evntrace.h>
#include "wmikmp.h"
#include <wmi.h>
#include "tracep.h"
extern SIZE_T MmMaximumNonPagedPoolInBytes;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, WmiStartTrace)
#pragma alloc_text(PAGE, WmiQueryTrace)
#pragma alloc_text(PAGE, WmiStopTrace)
#pragma alloc_text(PAGE, WmiUpdateTrace)
#pragma alloc_text(PAGE, WmiSetTraceBufferCallback)
#pragma alloc_text(PAGE, WmiFlushTrace)
#pragma alloc_text(PAGE, WmiQueryTraceInformation)
// #pragma alloc_text(PAGEWMI, NtTraceEvent)
// #pragma alloc_text(PAGEWMI, WmiTraceEvent)
#pragma alloc_text(PAGEWMI, WmiTraceKernelEvent)
// #pragma alloc_text(PAGEWMI, WmiTraceFastEvent)
// #pragma alloc_text(PAGEWMI, WmiTraceLongEvent)
// #pragma alloc_text(PAGEWMI, WmiTraceMessage)
// #pragma alloc_text(PAGEWMI, WmiTraceMessageVa)
#pragma alloc_text(PAGEWMI, WmiTraceUserMessage)
// #pragma alloc_text(PAGEWMI, WmiGetClock)
// #pragma alloc_text(PAGEWMI, WmiGetClockType)
#pragma alloc_text(PAGEWMI, WmiSetMark)
#endif
//
// Trace Control APIs
//
NTKERNELAPI
NTSTATUS
WmiStartTrace(
IN OUT PWMI_LOGGER_INFORMATION LoggerInfo
)
/*++
Routine Description:
This routine is used to create and start an event tracing session.
NOTE: A special instance (KERNEL_LOGGER) is reserved exclusively for
logging kernel tracing.
To turn on KERNEL_LOGGER, LoggerInfo->Wnode.Guid should be set to
SystemTraceControlGuid, and sufficient space must be provided in
LoggerInfo->LoggerName.
To turn on other loggers, simply provide a name in LoggerName. The
logger id will be returned.
Arguments:
LoggerInfo a pointer to the structure for the logger's control
and status information
Return Value:
The status of performing the action requested.
--*/
{
NTSTATUS status;
PWCHAR LogFileName = NULL;
HANDLE FileHandle = NULL;
ULONG DelayOpen;
PAGED_CODE();
if (LoggerInfo == NULL)
return STATUS_INVALID_PARAMETER;
//
// We assume that the caller is always kernel mode
// First, we try and see it is a delay create.
// If not, if we can even open the file
//
DelayOpen = LoggerInfo->LogFileMode & EVENT_TRACE_DELAY_OPEN_FILE_MODE;
if (!DelayOpen) {
if (LoggerInfo->LogFileName.Buffer != NULL) { // && !delay_create
status = WmipCreateNtFileName(
LoggerInfo->LogFileName.Buffer,
&LogFileName);
if (!NT_SUCCESS(status))
return status;
status = WmipCreateDirectoryFile(LogFileName,
FALSE,
&FileHandle,
LoggerInfo->LogFileMode & EVENT_TRACE_FILE_MODE_APPEND);
if (LogFileName != NULL) {
ExFreePool(LogFileName);
}
if (!NT_SUCCESS(status)) {
return status;
}
ZwClose(FileHandle);
}
}
status = WmipStartLogger(LoggerInfo);
if (NT_SUCCESS(status)) {
status = WmiFlushTrace(LoggerInfo);
}
return status;
}
NTKERNELAPI
NTSTATUS
WmiQueryTrace(
IN OUT PWMI_LOGGER_INFORMATION LoggerInfo
)
/*++
Routine Description:
This routine is called to query the status of a tracing session.
Caller must pass in either the Logger Name or a valid Logger Id/Handle.
Arguments:
LoggerInfo a pointer to the structure for the logger's control
and status information
Return Value:
The status of performing the action requested.
--*/
{
PAGED_CODE();
if (LoggerInfo == NULL)
return STATUS_INVALID_PARAMETER;
return WmipQueryLogger(LoggerInfo, NULL);
}
NTKERNELAPI
NTSTATUS
WmiStopTrace(
IN PWMI_LOGGER_INFORMATION LoggerInfo
)
/*++
Routine Description:
It is called by WmipIoControl in wmi.c, with IOCTL_WMI_STOP_LOGGER
to stop an instance of the logger. If the logger is the kernel logger,
it will also turn off kernel tracing and unlock the routines previously
locked. It will also free all the context of the logger.
Calls StopLoggerInstance to do the actual work.
Arguments:
LoggerInfo a pointer to the structure for the logger's control
and status information
Return Value:
The status of performing the action requested.
--*/
{
PWMI_LOGGER_CONTEXT LoggerContext = NULL;
NTSTATUS Status;
LARGE_INTEGER TimeOut = {(ULONG)(-200 * 1000 * 1000 * 10), -1};
ACCESS_MASK DesiredAccess = TRACELOG_GUID_ENABLE;
ULONG LoggerId;
#if DBG
LONG RefCount;
#endif
PAGED_CODE();
if (LoggerInfo == NULL)
return STATUS_INVALID_PARAMETER;
TraceDebug((1, "WmiStopTrace: %d\n",
LoggerInfo->Wnode.HistoricalContext));
#if DBG
Status = WmipVerifyLoggerInfo(LoggerInfo, &LoggerContext, "WmiStopTrace");
#else
Status = WmipVerifyLoggerInfo(LoggerInfo, &LoggerContext);
#endif
if (!NT_SUCCESS(Status) || (LoggerContext == NULL))
return Status;
LoggerId = LoggerContext->LoggerId;
TraceDebug((1, "WmiStopTrace: Stopping %X %d slot %X\n",
LoggerContext, LoggerId, WmipLoggerContext[LoggerId]));
if (LoggerContext->KernelTraceOn)
DesiredAccess |= TRACELOG_ACCESS_KERNEL_LOGGER;
Status = WmipCheckGuidAccess(
(LPGUID) &EventTraceGuid,
DesiredAccess
);
if (!NT_SUCCESS(Status)) {
#ifndef WMI_MUTEX_FREE
InterlockedDecrement(&LoggerContext->MutexCount);
TraceDebug((1, "WmiStopTrace: Release mutex1 %d %d\n",
LoggerId, LoggerContext->MutexCount));
WmipReleaseMutex(&LoggerContext->LoggerMutex);
#endif
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((1, "WmiStopTrace: Status1=%X %d %d->%d\n",
Status, LoggerId, RefCount+1, RefCount));
return Status;
}
if (!IsEqualGUID(&LoggerContext->InstanceGuid, &EventTraceGuid)) {
Status = WmipCheckGuidAccess(
(LPGUID) &LoggerContext->InstanceGuid,
DesiredAccess
);
if (!NT_SUCCESS(Status)) {
#ifndef WMI_MUTEX_FREE
InterlockedDecrement(&LoggerContext->MutexCount);
TraceDebug((1, "WmiStopTrace: Release mutex2 %d %d\n",
LoggerId, LoggerContext->MutexCount));
WmipReleaseMutex(&LoggerContext->LoggerMutex);
#endif
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((1, "WmiStopTrace: Status2=%X %d %d->%d\n",
Status, LoggerId, RefCount+1, RefCount));
return Status;
}
}
//
// Reset the Event inside the mutex to be sure
// before waiting on it.
KeResetEvent(&LoggerContext->FlushEvent);
Status = WmipStopLoggerInstance (LoggerContext);
#ifndef WMI_MUTEX_FREE
InterlockedDecrement(&LoggerContext->MutexCount);
TraceDebug((1, "WmiStopTrace: Release mutex3 %d %d\n",
LoggerId, LoggerContext->MutexCount));
WmipReleaseMutex(&LoggerContext->LoggerMutex); // Let others in
#endif
if (NT_SUCCESS(Status)) {
LIST_ENTRY TraceGuidMap;
InitializeListHead(&TraceGuidMap);
if (LoggerId == WmipKernelLogger)
WmipKernelLogger = KERNEL_LOGGER;
else if (LoggerId == WmipEventLogger)
WmipEventLogger = 0XFFFFFFFF;
else
Status = WmipDisableTraceProviders(LoggerId, &TraceGuidMap );
if (LoggerInfo != NULL) {
ULONG GuidMapBuffers = 0;
if (NT_SUCCESS(LoggerContext->LoggerStatus)) {
LONG Buffers;
Status = STATUS_TIMEOUT;
Buffers = LoggerContext->BuffersAvailable;
//
// If all buffers are accounted for and the logfile handle
// is NULL, then there is no reason to wait.
//
if ( (Buffers == LoggerContext->NumberOfBuffers) &&
(LoggerContext->LogFileHandle == NULL) ) {
Status = STATUS_SUCCESS;
}
//
// We need to wait for the logger thread to flush
//
while (Status == STATUS_TIMEOUT) {
Status = KeWaitForSingleObject(
&LoggerContext->FlushEvent,
Executive,
KernelMode,
FALSE,
&TimeOut
);
/* if (LoggerContext->NumberOfBuffers
== LoggerContext->BuffersAvailable)
break;
else if (LoggerContext->BuffersAvailable == Buffers) {
TraceDebug((1,
"WmiStopTrace: Logger %d hung %d != %d\n",
LoggerId, Buffers, LoggerContext->NumberOfBuffers));
KeResetEvent(&LoggerContext->FlushEvent);
// break;
}
*/
TraceDebug((1, "WmiStopTrace: Wait status=%X\n",Status));
}
}
//
// For application loggers, dump the guidmap at the end.
//
if ( (LoggerId != WmipKernelLogger) &&
(LoggerId != WmipEventLogger) &&
(! IsListEmpty(&TraceGuidMap) ) )
{
GuidMapBuffers = WmipDumpGuidMaps(LoggerContext, &TraceGuidMap);
}
//
// Required for Query to work
// But since CollectionOn is FALSE, it should be safe
//
Status = WmipQueryLogger(
LoggerInfo,
LoggerContext
);
LoggerInfo->Checksum64 = GuidMapBuffers;
}
KeSetEvent(&LoggerContext->LoggerEvent, 0, FALSE); // release context
}
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((1, "WmiStopTrace: Stopped status=%X %d %d->%d\n",
Status, LoggerId, RefCount+1, RefCount));
return Status;
}
NTKERNELAPI
NTSTATUS
WmiUpdateTrace(
IN OUT PWMI_LOGGER_INFORMATION LoggerInfo
)
/*++
Routine Description:
It is called by WmipIoControl in wmi.c, with IOCTL_WMI_UPDATE_LOGGER
to update certain characteristics of a running logger.
Arguments:
LoggerInfo a pointer to the structure for the logger's control
and status information
Return Value:
The status of performing the action requested.
--*/
{
NTSTATUS Status;
ULONG Max_Buffers;
PWMI_LOGGER_CONTEXT LoggerContext = NULL;
LARGE_INTEGER OneSecond = {(ULONG)(-1 * 1000 * 1000 * 10), -1};
ACCESS_MASK DesiredAccess = TRACELOG_GUID_ENABLE;
LARGE_INTEGER TimeOut = {(ULONG)(-20 * 1000 * 1000 * 10), -1};
ULONG EnableFlags, TmpFlags;
KPROCESSOR_MODE RequestorMode;
ULONG LoggerMode, LoggerId, NewMode;
UNICODE_STRING NewLogFileName;
UNICODE_STRING OldFilePattern;
PTRACE_ENABLE_FLAG_EXTENSION FlagExt;
PERFINFO_GROUPMASK *PerfGroupMasks=NULL;
ULONG GroupMaskSize;
SECURITY_QUALITY_OF_SERVICE ServiceQos;
#if DBG
LONG RefCount;
#endif
PAGED_CODE();
//
// see if Logger is running properly first. Error checking will be done
// in WmiQueryTrace
//
if (LoggerInfo == NULL)
return STATUS_INVALID_PARAMETER;
EnableFlags = LoggerInfo->EnableFlags;
TraceDebug((1, "WmiUpdateTrace: %d\n",
LoggerInfo->Wnode.HistoricalContext));
#if DBG
Status = WmipVerifyLoggerInfo(LoggerInfo, &LoggerContext, "WmiUpdateTrace");
#else
Status = WmipVerifyLoggerInfo(LoggerInfo, &LoggerContext);
#endif
if (!NT_SUCCESS(Status) || (LoggerContext == NULL))
return Status;
OldFilePattern.Buffer = NULL;
LoggerId = LoggerContext->LoggerId;
// at this point, LoggerContext must be non-NULL
LoggerMode = LoggerContext->LoggerMode; // local copy
NewMode = LoggerInfo->LogFileMode;
//
// First, check to make sure that you cannot turn on certain modes
// in UpdateTrace()
//
if ( ((NewMode & EVENT_TRACE_FILE_MODE_SEQUENTIAL) &&
(NewMode & EVENT_TRACE_FILE_MODE_CIRCULAR)) ||
((NewMode & EVENT_TRACE_USE_GLOBAL_SEQUENCE) &&
(NewMode & EVENT_TRACE_USE_LOCAL_SEQUENCE)) ||
(!(LoggerMode & EVENT_TRACE_FILE_MODE_CIRCULAR) &&
(NewMode & EVENT_TRACE_FILE_MODE_CIRCULAR)) ||
// Cannot support append to circular
((NewMode & EVENT_TRACE_FILE_MODE_CIRCULAR) &&
(NewMode & EVENT_TRACE_FILE_MODE_APPEND))
) {
#ifndef WMI_MUTEX_FREE
InterlockedDecrement(&LoggerContext->MutexCount);
TraceDebug((1, "WmiUpdateTrace: Release mutex1 %d %d\n",
LoggerContext->LoggerId, LoggerContext->MutexCount));
WmipReleaseMutex(&LoggerContext->LoggerMutex);
#endif
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((1, "WmiUpdateTrace: Status2=%X %d %d->%d\n",
Status, LoggerId, RefCount+1, RefCount));
return STATUS_INVALID_PARAMETER;
}
//
// support turn on or off real time dynamically
//
if (NewMode & EVENT_TRACE_REAL_TIME_MODE) {
LoggerMode |= EVENT_TRACE_REAL_TIME_MODE;
DesiredAccess |= TRACELOG_CREATE_REALTIME;
} else {
if (LoggerMode & EVENT_TRACE_REAL_TIME_MODE)
DesiredAccess |= TRACELOG_CREATE_REALTIME; // turn off real time
LoggerMode &= ~EVENT_TRACE_REAL_TIME_MODE;
}
if (NewMode & EVENT_TRACE_BUFFERING_MODE) {
LoggerMode |= EVENT_TRACE_BUFFERING_MODE;
}
else {
LoggerMode &= ~EVENT_TRACE_BUFFERING_MODE;
}
if (LoggerContext->KernelTraceOn)
DesiredAccess |= TRACELOG_ACCESS_KERNEL_LOGGER;
if (LoggerInfo->LogFileHandle != NULL)
DesiredAccess |= TRACELOG_CREATE_ONDISK;
Status = WmipCheckGuidAccess(
(LPGUID) &EventTraceGuid,
DesiredAccess
);
if (!NT_SUCCESS(Status)) {
#ifndef WMI_MUTEX_FREE
InterlockedDecrement(&LoggerContext->MutexCount);
TraceDebug((1, "WmiUpdateTrace: Release mutex1 %d %d\n",
LoggerContext->LoggerId, LoggerContext->MutexCount));
WmipReleaseMutex(&LoggerContext->LoggerMutex);
#endif
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((1, "WmiUpdateTrace: Status2=%X %d %d->%d\n",
Status, LoggerId, RefCount+1, RefCount));
return Status;
}
if (!IsEqualGUID(&LoggerContext->InstanceGuid, &EventTraceGuid)) {
Status = WmipCheckGuidAccess(
(LPGUID) &LoggerContext->InstanceGuid,
DesiredAccess
);
if (!NT_SUCCESS(Status)) {
#ifndef WMI_MUTEX_FREE
InterlockedDecrement(&LoggerContext->MutexCount);
TraceDebug((1, "WmiUpdateTrace: Release mutex2 %d %d\n",
LoggerId, LoggerContext->MutexCount));
WmipReleaseMutex(&LoggerContext->LoggerMutex);
#endif
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((1, "WmiUpdateTrace: Status3=%X %d %d->%d\n",
Status, LoggerId,
RefCount+1, RefCount));
return Status;
}
}
RtlZeroMemory(&NewLogFileName, sizeof(UNICODE_STRING));
RequestorMode = KeGetPreviousMode();
if (LoggerInfo->LogFileHandle != NULL) {
PFILE_OBJECT fileObject;
OBJECT_HANDLE_INFORMATION handleInformation;
ACCESS_MASK grantedAccess;
BOOLEAN bDelayOpenFlag = FALSE;
bDelayOpenFlag = (LoggerContext->LogFileHandle == NULL &&
(LoggerContext->LoggerMode & EVENT_TRACE_DELAY_OPEN_FILE_MODE));
if (LoggerInfo->LogFileName.Buffer == NULL) {
Status = STATUS_INVALID_PARAMETER;
goto ReleaseAndExit;
}
// Save the new LogFileName
//
try {
if (RequestorMode != KernelMode) {
ProbeForRead(
LoggerInfo->LogFileName.Buffer,
LoggerInfo->LogFileName.Length,
sizeof (UCHAR) );
}
RtlCreateUnicodeString(
&NewLogFileName,
LoggerInfo->LogFileName.Buffer);
}
except (EXCEPTION_EXECUTE_HANDLER) {
if (NewLogFileName.Buffer != NULL) {
RtlFreeUnicodeString(&NewLogFileName);
}
#ifndef WMI_MUTEX_FREE
InterlockedDecrement(&LoggerContext->MutexCount);
TraceDebug((1, "WmiUpdateTrace: Release mutex3 %d %d\n",
LoggerId, LoggerContext->MutexCount));
WmipReleaseMutex(&LoggerContext->LoggerMutex);
#endif
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((1, "WmiUpdateTrace: Status5=EXCEPTION %d %d->%d\n",
LoggerId, RefCount+1, RefCount));
return GetExceptionCode();
}
// Switching to a new logfile. This routine does not put any
// headers into the logfile. The headers should be written out
// by UpdateTrace() in user-mode.
//
fileObject = NULL;
Status = ObReferenceObjectByHandle(
LoggerInfo->LogFileHandle,
0L,
IoFileObjectType,
RequestorMode,
(PVOID *) &fileObject,
&handleInformation);
if (!NT_SUCCESS(Status)) {
goto ReleaseAndExit;
}
if (RequestorMode != KernelMode) {
grantedAccess = handleInformation.GrantedAccess;
if (!SeComputeGrantedAccesses(grantedAccess, FILE_WRITE_DATA)) {
ObDereferenceObject( fileObject );
Status = STATUS_ACCESS_DENIED;
goto ReleaseAndExit;
}
}
ObDereferenceObject(fileObject); // Referenced in WmipCreateLogFile
// Obtain the security context here so we can use it
// later to impersonate the user, which we will do
// if we cannot access the file as SYSTEM. This
// usually occurs if the file is on a remote machine.
//
ServiceQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
ServiceQos.ImpersonationLevel = SecurityImpersonation;
ServiceQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
ServiceQos.EffectiveOnly = TRUE;
Status = SeCreateClientSecurity(
CONTAINING_RECORD(KeGetCurrentThread(), ETHREAD, Tcb),
&ServiceQos,
FALSE,
& LoggerContext->ClientSecurityContext);
if (!NT_SUCCESS(Status)) {
goto ReleaseAndExit;
}
if (LoggerInfo->Checksum != NULL) {
if (LoggerContext->LoggerHeader == NULL) {
LoggerContext->LoggerHeader =
ExAllocatePoolWithTag(
PagedPool,
sizeof(WNODE_HEADER) + sizeof(TRACE_LOGFILE_HEADER),
TRACEPOOLTAG);
}
if (LoggerContext->LoggerHeader != NULL) {
RtlCopyMemory(
LoggerContext->LoggerHeader,
LoggerInfo->Checksum,
sizeof(WNODE_HEADER) + sizeof(TRACE_LOGFILE_HEADER));
}
}
// We try to update the file name using LoggerContext->NewLogFileName.
// This is freed by WmipCreateLogFile() in the logger thread.
// This have to be NULL.
if (NewLogFileName.Buffer != NULL) {
if (NewMode & EVENT_TRACE_FILE_MODE_NEWFILE) {
if (LoggerContext->LogFilePattern.Buffer != NULL) {
OldFilePattern = LoggerContext->LogFilePattern;
}
LoggerContext->LogFilePattern = NewLogFileName;
LoggerContext->LoggerMode |= EVENT_TRACE_FILE_MODE_NEWFILE;
LoggerMode |= EVENT_TRACE_FILE_MODE_NEWFILE;
}
else {
ASSERT(LoggerContext->NewLogFileName.Buffer == NULL);
LoggerContext->NewLogFileName = NewLogFileName;
}
}
else {
Status = STATUS_INVALID_PARAMETER;
goto ReleaseAndExit;
}
//
// Reset the event inside the mutex before waiting on it.
//
KeResetEvent(&LoggerContext->FlushEvent);
ZwClose(LoggerInfo->LogFileHandle);
LoggerInfo->LogFileHandle = NULL;
// must turn on flag just before releasing semaphore
LoggerContext->RequestFlag |= REQUEST_FLAG_NEW_FILE;
// Wake up the logger thread (system) to change the file
Status = WmipNotifyLogger(LoggerContext);
if (!NT_SUCCESS(Status)) {
goto ReleaseAndExit;
}
// use the same event initialized by start logger
//
KeWaitForSingleObject(
&LoggerContext->FlushEvent,
Executive,
KernelMode,
FALSE,
&TimeOut
);
KeResetEvent(&LoggerContext->FlushEvent);
Status = LoggerContext->LoggerStatus;
if (!NT_SUCCESS(Status) || !LoggerContext->CollectionOn) {
goto ReleaseAndExit;
}
if (bDelayOpenFlag && (LoggerContext->LoggerId == WmipKernelLogger)) {
LONG PerfLogInTransition;
//
// This is a update call from advapi32.dll after RunDown.
// Call PerfInfoStartLog.
//
if (EnableFlags & EVENT_TRACE_FLAG_EXTENSION) {
FlagExt = (PTRACE_ENABLE_FLAG_EXTENSION) &EnableFlags;
if ((FlagExt->Length == 0) || (FlagExt->Offset == 0)) {
Status = STATUS_INVALID_PARAMETER;
goto ReleaseAndExit;
}
if ((FlagExt->Length * sizeof(ULONG)) >
(LoggerInfo->Wnode.BufferSize - FlagExt->Offset)) {
Status = STATUS_INVALID_PARAMETER;
goto ReleaseAndExit;
}
GroupMaskSize = FlagExt->Length * sizeof(ULONG);
if (GroupMaskSize < sizeof(PERFINFO_GROUPMASK)) {
GroupMaskSize = sizeof(PERFINFO_GROUPMASK);
}
} else {
GroupMaskSize = sizeof(PERFINFO_GROUPMASK);
}
LoggerContext->EnableFlagArray = (PULONG) WmipExtendBase(LoggerContext, GroupMaskSize);
if (LoggerContext->EnableFlagArray) {
PCHAR FlagArray;
RtlZeroMemory(LoggerContext->EnableFlagArray, GroupMaskSize);
if (EnableFlags & EVENT_TRACE_FLAG_EXTENSION) {
FlagArray = (PCHAR) (FlagExt->Offset + (PCHAR) LoggerInfo);
//
// Copy only the bytes actually supplied
//
RtlCopyMemory(LoggerContext->EnableFlagArray, FlagArray, FlagExt->Length * sizeof(ULONG));
EnableFlags = LoggerContext->EnableFlagArray[0];
} else {
LoggerContext->EnableFlagArray[0] = EnableFlags;
}
PerfLogInTransition =
InterlockedCompareExchange(&LoggerContext->PerfLogInTransition,
PERF_LOG_START_TRANSITION,
PERF_LOG_NO_TRANSITION);
if (PerfLogInTransition != PERF_LOG_NO_TRANSITION) {
Status = STATUS_ALREADY_DISCONNECTED;
goto ReleaseAndExit;
}
PerfGroupMasks = (PERFINFO_GROUPMASK *) &LoggerContext->EnableFlagArray[0];
Status = PerfInfoStartLog(PerfGroupMasks, PERFINFO_START_LOG_POST_BOOT);
PerfLogInTransition =
InterlockedExchange(&LoggerContext->PerfLogInTransition,
PERF_LOG_NO_TRANSITION);
ASSERT(PerfLogInTransition == PERF_LOG_START_TRANSITION);
if (!NT_SUCCESS(Status)) {
goto ReleaseAndExit;
}
} else {
Status = STATUS_NO_MEMORY;
goto ReleaseAndExit;
}
}
}
if (LoggerContext->KernelTraceOn &&
LoggerId == WmipKernelLogger &&
IsEqualGUID(&LoggerInfo->Wnode.Guid, &SystemTraceControlGuid)) {
TmpFlags = (~LoggerContext->EnableFlags & EnableFlags);
if (TmpFlags != 0) {
WmipEnableKernelTrace(TmpFlags);
}
TmpFlags = (LoggerContext->EnableFlags & ~EnableFlags);
if (TmpFlags != 0) {
WmipDisableKernelTrace(TmpFlags);
}
LoggerContext->EnableFlags = EnableFlags;
}
//
// Cap Maximum Buffers to Max_Buffers
//
if ( LoggerInfo->MaximumBuffers > 0 ) {
Max_Buffers = (LoggerContext->BufferSize > 0) ?
(ULONG) (MmMaximumNonPagedPoolInBytes
/ TRACE_MAXIMUM_NP_POOL_USAGE
/ LoggerContext->BufferSize)
: 0;
if (LoggerInfo->MaximumBuffers > Max_Buffers ) {
LoggerInfo->MaximumBuffers = Max_Buffers;
}
if (LoggerInfo->MaximumBuffers > LoggerContext->MaximumBuffers) {
LoggerContext->MaximumBuffers = LoggerInfo->MaximumBuffers;
}
}
#ifdef NTPERF
if (PERFINFO_IS_LOGGING_TO_PERFMEM()) {
//
// Logging to Perfmem. The Maximum should be the perfmem size.
//
LoggerContext->MaximumBuffers = PerfQueryBufferSizeBytes()/LoggerContext->BufferSize;
}
#endif //NTPERF
// Allow changing of FlushTimer
if (LoggerInfo->FlushTimer > 0) {
#ifdef WMI_NON_BLOCKING
LoggerContext->FlushTimer = LoggerInfo->FlushTimer;
#else
LoggerContext->FlushTimer.QuadPart =
LoggerInfo->FlushTimer * OneSecond.QuadPart;
#endif //WMI_NON_BLOCKING
}
if (OldFilePattern.Buffer != NULL) {
RtlFreeUnicodeString(&OldFilePattern);
}
if (NewMode & EVENT_TRACE_KD_FILTER_MODE) {
LoggerMode |= EVENT_TRACE_KD_FILTER_MODE;
LoggerContext->BufferCallback = &KdReportTraceData;
}
else {
LoggerMode &= ~EVENT_TRACE_KD_FILTER_MODE;
if (LoggerContext->BufferCallback == &KdReportTraceData) {
LoggerContext->BufferCallback = NULL;
}
}
if (LoggerContext->LoggerMode & EVENT_TRACE_DELAY_OPEN_FILE_MODE) {
LoggerContext->LoggerMode = LoggerMode;
}
else {
LoggerContext->LoggerMode =
(LoggerMode & ~EVENT_TRACE_DELAY_OPEN_FILE_MODE);
}
Status = WmipQueryLogger(LoggerInfo, LoggerContext);
ReleaseAndExit:
#ifndef WMI_MUTEX_FREE
InterlockedDecrement(&LoggerContext->MutexCount);
TraceDebug((1, "WmiUpdateTrace: Release mutex5 %d %d\n",
LoggerId, LoggerContext->MutexCount));
WmipReleaseMutex(&LoggerContext->LoggerMutex);
#endif
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((1, "WmiUpdateTrace: %d %d->%d\n",
LoggerId, RefCount+1, RefCount));
return Status;
}
NTKERNELAPI
NTSTATUS
WmiFlushTrace(
IN OUT PWMI_LOGGER_INFORMATION LoggerInfo
)
/*++
Routine Description:
It is called by WmipIoControl in wmi.c, with IOCTL_WMI_FLUSH_LOGGER
to flush all the buffers out of a particular logger
Arguments:
LoggerInfo a pointer to the structure for the logger's control
and status information
Return Value:
The status of performing the action requested.
--*/
{
NTSTATUS Status;
PWMI_LOGGER_CONTEXT LoggerContext = NULL;
ULONG LoggerId;
#if DBG
LONG RefCount;
#endif
PAGED_CODE();
//
// see if Logger is running properly first. Error checking will be done
// in WmiQueryTrace
//
if (LoggerInfo == NULL)
return STATUS_INVALID_PARAMETER;
TraceDebug((1, "WmiFlushTrace: %d\n",
LoggerInfo->Wnode.HistoricalContext));
#if DBG
Status = WmipVerifyLoggerInfo(LoggerInfo, &LoggerContext, "WmiFlushTrace");
#else
Status = WmipVerifyLoggerInfo(LoggerInfo, &LoggerContext);
#endif
if (!NT_SUCCESS(Status) || (LoggerContext == NULL))
return Status;
LoggerId = LoggerContext->LoggerId;
Status = WmipFlushLogger(LoggerContext, TRUE);
if (NT_SUCCESS(Status)) {
Status = WmipQueryLogger(LoggerInfo, LoggerContext);
}
#ifndef WMI_MUTEX_FREE
InterlockedDecrement(&LoggerContext->MutexCount);
TraceDebug((1, "WmiFlushTrace: Release mutex %d %d\n",
LoggerContext->LoggerId, LoggerContext->MutexCount));
WmipReleaseMutex(&LoggerContext->LoggerMutex);
#endif
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((1, "WmiFlushTrace: %d %d->%d\n",
LoggerId, RefCount+1, RefCount));
return Status;
}
//
// Trace Provider APIs
//
NTKERNELAPI
NTSTATUS
FASTCALL
WmiGetClockType(
IN TRACEHANDLE LoggerHandle,
OUT WMI_CLOCK_TYPE *ClockType
)
/*++
Routine Description:
This is called by anyone internal to find the clock type
that is in use with a logger specified by the LoggerHandle
Arguments:
LoggerHandle Handle to a tracelog session
Return Value:
The clock type
--*/
{
ULONG LoggerId;
#if DBG
LONG RefCount;
#endif
PWMI_LOGGER_CONTEXT LoggerContext;
LoggerId = WmiGetLoggerId(LoggerHandle);
if (LoggerId < 1 || LoggerId >= MAXLOGGERS)
return STATUS_INVALID_HANDLE;
#if DBG
RefCount =
#endif
WmipReferenceLogger(LoggerId);
TraceDebug((4, "WmiGetClockType: %d %d->%d\n",
LoggerId, RefCount-1, RefCount));
LoggerContext = WmipGetLoggerContext( LoggerId );
if (!WmipIsValidLogger(LoggerContext)) {
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((4, "WmiGetClockType: Status=%X %d %d->%d\n",
STATUS_WMI_INSTANCE_NOT_FOUND,
LoggerId, RefCount+1, RefCount));
return STATUS_WMI_INSTANCE_NOT_FOUND;
}
*ClockType = WMICT_SYSTEMTIME; // Default Clock Type
if (LoggerContext->UsePerfClock & EVENT_TRACE_CLOCK_PERFCOUNTER) {
*ClockType = WMICT_PERFCOUNTER;
}
else if (LoggerContext->UsePerfClock & EVENT_TRACE_CLOCK_CPUCYCLE) {
*ClockType = WMICT_CPUCYCLE;
}
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
return STATUS_SUCCESS;
}
NTKERNELAPI
LONG64
FASTCALL
WmiGetClock(
IN WMI_CLOCK_TYPE ClockType,
IN PVOID Context
)
/*++
Routine Description:
This is called anyone internal to use a particular clock for
sequencing events.
Arguments:
ClockType Should use WMICT_DEFAULT most of the time.
Other clock types are for perf group.
Context Only used for process/thread times
Return Value:
The clock value
--*/
{
LARGE_INTEGER Clock;
switch (ClockType) {
case WMICT_DEFAULT :
Clock.QuadPart = (*WmiGetCpuClock)();
break;
case WMICT_SYSTEMTIME:
Clock.QuadPart = WmipGetSystemTime();
break;
case WMICT_PERFCOUNTER:
Clock.QuadPart = WmipGetPerfCounter();
break;
case WMICT_PROCESS : // defaults to Process times for now
{
PEPROCESS Process = (PEPROCESS) Context;
if (Process == NULL)
Process = PsGetCurrentProcess();
else {
ObReferenceObject(Process);
}
Clock.HighPart = Process->Pcb.KernelTime;
Clock.LowPart = Process->Pcb.UserTime;
if (Context) {
ObDereferenceObject(Process);
}
break;
}
case WMICT_THREAD : // defaults to Thread times for now
{
PETHREAD Thread = (PETHREAD) Context;
if (Thread == NULL)
Thread = PsGetCurrentThread();
else {
ObReferenceObject(Thread);
}
Clock.HighPart = Thread->Tcb.KernelTime;
Clock.LowPart = Thread->Tcb.UserTime;
if (Context) {
ObDereferenceObject(Thread);
}
break;
}
default :
KeQuerySystemTime(&Clock);
}
return ((LONG64) Clock.QuadPart);
}
NTSYSCALLAPI
NTSTATUS
NTAPI
NtTraceEvent(
IN HANDLE TraceHandle,
IN ULONG Flags,
IN ULONG FieldSize,
IN PVOID Fields
)
/*++
Routine Description:
This routine is used by WMI data providers to trace events.
It calls different tracing functions depending on the Flags.
Arguments:
TraceHandle LoggerId
Flags Flags that indicate the type of the data being passed
FieldSize Size of the Fields
Fields Pointer to actual data (events)
Return Value:
STATUS_SUCCESS if the event trace is recorded successfully
--*/
{
NTSTATUS Status;
if (Flags & ETW_NT_FLAGS_TRACE_HEADER) {
retry:
Status = WmiTraceEvent((PWNODE_HEADER)Fields, KeGetPreviousMode());
if (Status == STATUS_NO_MEMORY) {
//
// This logging is from user mode, try to allocate more buffer.
//
PWNODE_HEADER Wnode = (PWNODE_HEADER) Fields;
ULONG LoggerId = WmiGetLoggerId(Wnode->HistoricalContext);
PWMI_LOGGER_CONTEXT LoggerContext;
if (LoggerId < 1 || LoggerId >= MAXLOGGERS) {
Status = STATUS_INVALID_HANDLE;
} else {
WmipReferenceLogger(LoggerId);
LoggerContext = WmipGetLoggerContext(LoggerId);
//
// Make sure collection is still on before allocate more
// free buffers. This makes sure that logger thread
// can free all allocated buffers.
//
if (WmipIsValidLogger(LoggerContext) &&
LoggerContext->CollectionOn)
{
if (WmipAllocateFreeBuffers (LoggerContext, 1) == 1) {
WmipDereferenceLogger(LoggerId);
InterlockedDecrement((PLONG)&LoggerContext->EventsLost);
goto retry;
} else {
Status = STATUS_NO_MEMORY;
}
}
WmipDereferenceLogger(LoggerId);
}
}
}
else if (Flags & ETW_NT_FLAGS_TRACE_MESSAGE) {
if (FieldSize < sizeof(MESSAGE_TRACE_USER)) {
return (STATUS_UNSUCCESSFUL);
}
try {
ProbeForRead(
Fields,
FieldSize,
sizeof (UCHAR)
);
return (WmiTraceMessage((TRACEHANDLE)TraceHandle,
((PMESSAGE_TRACE_USER)Fields)->MessageFlags,
&((PMESSAGE_TRACE_USER)Fields)->MessageGuid,
((PMESSAGE_TRACE_USER)Fields)->MessageHeader.Packet.MessageNumber,
&((PMESSAGE_TRACE_USER)Fields)->Data,
((PMESSAGE_TRACE_USER)Fields)->DataSize,
NULL,0));
} except (EXCEPTION_EXECUTE_HANDLER) {
TraceDebug((1, "NtTraceEvent: (ETW_NT_FLAGS_TRACE_MESSAGE) Status=EXCEPTION\n"));
return GetExceptionCode();
}
}
else {
Status = STATUS_INVALID_PARAMETER;
}
return Status;
}
NTKERNELAPI
NTSTATUS
FASTCALL
WmiTraceEvent(
IN PWNODE_HEADER Wnode,
IN KPROCESSOR_MODE RequestorMode
)
/*++
Routine Description:
This routine is used by WMI data providers to trace events.
It expects the user to pass in the handle to the logger.
Also, the user cannot ask to log something that is larger than
the buffer size (minus buffer header).
This routine works at IRQL <= DISPATCH_LEVEL
Arguments:
Wnode The WMI node header that will be overloaded
Return Value:
STATUS_SUCCESS if the event trace is recorded successfully
--*/
{
PEVENT_TRACE_HEADER TraceRecord = (PEVENT_TRACE_HEADER) Wnode;
ULONG WnodeSize, Size, LoggerId, Flags, HeaderSize;
PWMI_BUFFER_HEADER BufferResource = NULL;
NTSTATUS Status;
PETHREAD Thread;
PWMI_LOGGER_CONTEXT LoggerContext;
ULONG Marker;
MOF_FIELD MofFields[MAX_MOF_FIELDS];
long MofCount = 0;
LONG LoggerLocked = 0;
#if DBG
LONG RefCount;
#endif
if (TraceRecord == NULL)
return STATUS_SEVERITY_WARNING;
try {
HeaderSize = sizeof(WNODE_HEADER); // same size as EVENT_TRACE_HEADER
if (RequestorMode != KernelMode) {
ProbeForRead(
TraceRecord,
sizeof (EVENT_TRACE_HEADER),
sizeof (UCHAR)
);
Marker = Wnode->BufferSize; // check the first DWORD flags
Size = Marker;
if (Marker & TRACE_HEADER_FLAG) {
if ( ((Marker & TRACE_HEADER_ENUM_MASK) >> 16)
== TRACE_HEADER_TYPE_INSTANCE )
HeaderSize = sizeof(EVENT_INSTANCE_HEADER);
Size = TraceRecord->Size;
}
}
else {
Size = Wnode->BufferSize; // take the first DWORD flags
Marker = Size;
if (Marker & TRACE_HEADER_FLAG) {
if ( ((Marker & TRACE_HEADER_ENUM_MASK) >> 16)
== TRACE_HEADER_TYPE_INSTANCE )
HeaderSize = sizeof(EVENT_INSTANCE_HEADER);
Size = TraceRecord->Size;
}
}
WnodeSize = Size; // WnodeSize is for the contiguous block
// Size is for what we want in buffer
Flags = Wnode->Flags;
if (!(Flags & WNODE_FLAG_LOG_WNODE) &&
!(Flags & WNODE_FLAG_TRACED_GUID)) {
Status = STATUS_UNSUCCESSFUL;
goto ErrorReturn;
}
LoggerId = WmiGetLoggerId(Wnode->HistoricalContext);
if (LoggerId < 1 || LoggerId >= MAXLOGGERS) {
Status = STATUS_INVALID_HANDLE;
goto ErrorReturn;
}
LoggerLocked = WmipReferenceLogger(LoggerId);
#if DBG
RefCount = LoggerLocked;
#endif
TraceDebug((4, "WmiTraceEvent: %d %d->%d\n",
LoggerId, RefCount-1, RefCount));
LoggerContext = WmipGetLoggerContext(LoggerId);
if (!WmipIsValidLogger(LoggerContext)) {
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((4, "WmiTraceEvent: Status1=%X %d %d->%d\n",
STATUS_INVALID_HANDLE, LoggerId,
RefCount+1, RefCount));
Status = STATUS_INVALID_HANDLE;
goto ErrorReturn;
}
if ((RequestorMode == KernelMode) &&
(LoggerContext->LoggerMode & EVENT_TRACE_USE_PAGED_MEMORY)) {
return STATUS_UNSUCCESSFUL;
}
if (Flags & WNODE_FLAG_USE_MOF_PTR) {
//
// Need to compute the total size required, since the MOF fields
// in Wnode merely contains pointers
//
long i;
PCHAR Offset = ((PCHAR)Wnode) + HeaderSize;
ULONG MofSize, MaxSize;
MaxSize = LoggerContext->BufferSize - sizeof(WMI_BUFFER_HEADER);
MofSize = WnodeSize - HeaderSize;
// allow only the maximum
if (MofSize > (sizeof(MOF_FIELD) * MAX_MOF_FIELDS)) {
WmipDereferenceLogger(LoggerId);
return STATUS_ARRAY_BOUNDS_EXCEEDED;
}
if (MofSize > 0) { // Make sure we can read the rest
if (RequestorMode != KernelMode) {
ProbeForRead( Offset, MofSize, sizeof (UCHAR) );
}
RtlCopyMemory(MofFields, Offset, MofSize);
}
Size = HeaderSize;
MofCount = MofSize / sizeof(MOF_FIELD);
for (i=0; i<MofCount; i++) {
MofSize = MofFields[i].Length;
if (MofSize >= (MaxSize - Size)) { // check for overflow first
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((4, "WmiTraceEvent: Status2=%X %d %d->%d\n",
STATUS_BUFFER_OVERFLOW, LoggerId,
RefCount+1, RefCount));
Status = STATUS_BUFFER_OVERFLOW;
goto ErrorReturn;
}
Size += MofSize;
// if ( (Size > MaxSize) || (Size < MofSize) )
// return STATUS_BUFFER_OVERFLOW;
}
}
if ( (Size > LoggerContext->BufferSize - sizeof(WMI_BUFFER_HEADER)) ||
( (Flags & WNODE_FLAG_TRACED_GUID) && (Size > 0xFFFF)) ) {
LoggerContext->EventsLost++;
#ifndef WMI_NON_BLOCKING
LoggerContext->ProcessorBuffers[KeGetCurrentProcessorNumber()]
->EventsLost++;
#endif //WMI_NON_BLOCKING
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((4, "WmiTraceEvent: Status3=%X %d %d->%d\n",
STATUS_BUFFER_OVERFLOW, LoggerId,
RefCount+1, RefCount));
Status = STATUS_BUFFER_OVERFLOW;
goto ErrorReturn;
}
if ((LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_CIRCULAR) &&
(LoggerContext->RequestFlag & REQUEST_FLAG_CIRCULAR_PERSIST)) {
if (! (Flags & WNODE_FLAG_PERSIST_EVENT) ) {
ULONG RequestFlag = LoggerContext->RequestFlag
& (~ ( REQUEST_FLAG_CIRCULAR_PERSIST
| REQUEST_FLAG_CIRCULAR_TRANSITION));
if (InterlockedCompareExchange(
(PLONG) &LoggerContext->RequestFlag,
RequestFlag | REQUEST_FLAG_CIRCULAR_TRANSITION,
RequestFlag | REQUEST_FLAG_CIRCULAR_PERSIST)) {
// All persistence events are fired in circular
// logfile, flush out all active buffers and flushlist
// buffers. Also mark the end of persistence event
// collection in circular logger.
//
// It is the provider's resposibility to ensure that
// no persist event fires after this point. If it did,
// that event may be overwritten during wrap around.
//
if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
Status = WmipFlushLogger(LoggerContext, TRUE);
}
}
}
}
// So, now reserve some space in logger buffer and set that to TraceRecord
TraceRecord = (PEVENT_TRACE_HEADER)
WmipReserveTraceBuffer(
LoggerContext,
Size,
&BufferResource
);
if (TraceRecord == NULL) {
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((4, "WmiTraceEvent: Status4=%X %d %d->%d\n",
STATUS_NO_MEMORY, LoggerId,
RefCount+1, RefCount));
Status = STATUS_NO_MEMORY;
goto ErrorReturn;
}
if (Flags & WNODE_FLAG_USE_MOF_PTR) {
//
// Now we need to probe and copy all the MOF data fields
//
PVOID MofPtr;
ULONG MofLen;
long i;
PCHAR TraceOffset = (PCHAR) TraceRecord + HeaderSize;
if (RequestorMode != KernelMode) {
ProbeForRead(Wnode, HeaderSize, sizeof(UCHAR));
}
RtlCopyMemory(TraceRecord, Wnode, HeaderSize);
TraceRecord->Size = (USHORT)Size; // reset to Total Size
for (i=0; i<MofCount; i++) {
MofPtr = (PVOID) MofFields[i].DataPtr;
MofLen = MofFields[i].Length;
if (MofPtr == NULL || MofLen == 0)
continue;
if (RequestorMode != KernelMode) {
ProbeForRead(MofPtr, MofLen, sizeof(UCHAR));
}
RtlCopyMemory(TraceOffset, MofPtr, MofLen);
TraceOffset += MofLen;
}
}
else {
if (RequestorMode != KernelMode) {
ProbeForRead(Wnode, Size, sizeof(UCHAR));
}
RtlCopyMemory(TraceRecord, Wnode, Size);
}
if (Flags & WNODE_FLAG_USE_GUID_PTR) {
PVOID GuidPtr = (PVOID) ((PEVENT_TRACE_HEADER)Wnode)->GuidPtr;
if (RequestorMode != KernelMode) {
ProbeForReadSmallStructure(GuidPtr, sizeof(GUID), sizeof(UCHAR));
}
RtlCopyMemory(&TraceRecord->Guid, GuidPtr, sizeof(GUID));
}
}
except (EXCEPTION_EXECUTE_HANDLER) {
if (BufferResource != NULL) {
WmipReleaseTraceBuffer ( BufferResource, LoggerContext );
}
else {
if (LoggerLocked) {
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
}
}
TraceDebug((4, "WmiTraceEvent: Status5=EXCEPTION %d %d->%d\n",
LoggerId, RefCount+1, RefCount));
return GetExceptionCode();
}
//
// By now, we have reserved space in the trace buffer
//
if (Marker & TRACE_HEADER_FLAG) {
if (! (WNODE_FLAG_USE_TIMESTAMP & TraceRecord->Flags) ) {
TraceRecord->TimeStamp.QuadPart = (*LoggerContext->GetCpuClock)();
}
Thread = PsGetCurrentThread();
if (Thread != NULL) {
TraceRecord->KernelTime = Thread->Tcb.KernelTime;
TraceRecord->UserTime = Thread->Tcb.UserTime;
TraceRecord->ThreadId = HandleToUlong(Thread->Cid.UniqueThread);
TraceRecord->ProcessId = HandleToUlong(Thread->Cid.UniqueProcess);
}
}
WmipReleaseTraceBuffer( BufferResource, LoggerContext );
TraceDebug((4, "WmiTraceEvent: %d %d->%d\n",
LoggerId, RefCount+1, RefCount));
return STATUS_SUCCESS;
ErrorReturn:
return Status;
}
NTKERNELAPI
NTSTATUS
WmiTraceKernelEvent(
IN ULONG GroupType, // Group/type code for kernel event
IN PVOID EventInfo, // Event data as defined in MOF
IN ULONG EventInfoLen, // Length of the event data
IN PETHREAD Thread ) // use NULL for current caller thread
/*++
Routine Description:
This routine is used by trace kernel events only. These events can
be charged to the given thread instead of the running thread. Because
it can be called by I/O events at DPC level, this routine cannot be
pageable when tracing is on.
This routine works at IRQL <= DISPATCH_LEVEL
Arguments:
GroupType a ULONG key to indicate the action to be taken
EventInfo a pointer to contiguous memory containing information
to be attached to event trace
EventInfoLen length of EventInfo
Thread Pointer to thread where event is to be charged.
Currently used by disk IO and thread events.
Return Value:
The status of performing the action requested
--*/
{
PSYSTEM_TRACE_HEADER Header;
ULONG Size;
PWMI_BUFFER_HEADER BufferResource = NULL;
PWMI_LOGGER_CONTEXT LoggerContext = WmipLoggerContext[WmipKernelLogger];
#if DBG
LONG RefCount;
#endif
#if DBG
RefCount =
#endif
WmipReferenceLogger(WmipKernelLogger);
TraceDebug((4, "WmiTraceKernelEvent: 0 %d->%d\n", RefCount-1, RefCount));
// Make sure that kernel logger is enabled first
if (!WmipIsValidLogger(LoggerContext)) {
WmipDereferenceLogger(WmipKernelLogger);
return STATUS_ALREADY_DISCONNECTED;
}
// Compute total size of event trace record
Size = sizeof(SYSTEM_TRACE_HEADER) + EventInfoLen;
if (Size > LoggerContext->BufferSize - sizeof(WMI_BUFFER_HEADER)) {
LoggerContext->EventsLost++;
#ifndef WMI_NON_BLOCKING
LoggerContext->ProcessorBuffers[KeGetCurrentProcessorNumber()]
->EventsLost++;
#endif //WMI_NON_BLOCKING
#if DBG
RefCount =
#endif
WmipDereferenceLogger(WmipKernelLogger);
TraceDebug((4, "WmiTraceKernelEvent: Status1=%X 0 %d->%d\n",
STATUS_BUFFER_OVERFLOW, RefCount+1, RefCount));
return STATUS_BUFFER_OVERFLOW;
}
// NOTE: we do not check for size here for reduce overhead, and because
// we trust ourselves to use WmiTraceLongEvent for large event traces
Header = (PSYSTEM_TRACE_HEADER)
WmipReserveTraceBuffer(
LoggerContext,
Size,
&BufferResource
);
if (Header == NULL) {
#if DBG
RefCount =
#endif
WmipDereferenceLogger(WmipKernelLogger);
TraceDebug((4, "WmiTraceKernelEvent: Status1=%X 0 %d->%d\n",
STATUS_NO_MEMORY, RefCount+1, RefCount));
return STATUS_NO_MEMORY;
}
// Get the current system time as time stamp for trace record
PerfTimeStamp(Header->SystemTime);
if (Thread == NULL) {
Thread = PsGetCurrentThread();
}
//
// Now copy the necessary information into the buffer
//
Header->Marker = SYSTEM_TRACE_MARKER;
Header->ThreadId = HandleToUlong(Thread->Cid.UniqueThread);
Header->ProcessId = HandleToUlong(Thread->Cid.UniqueProcess);
Header->KernelTime = Thread->Tcb.KernelTime;
Header->UserTime = Thread->Tcb.UserTime;
Header->Header = (GroupType << 16) + Size;
if (EventInfoLen > 0) {
RtlCopyMemory (
(UCHAR*) Header + sizeof(SYSTEM_TRACE_HEADER),
EventInfo, EventInfoLen);
}
#if DBG
RefCount = WmipRefCount[LoggerContext->LoggerId] - 1;
#endif
WmipReleaseTraceBuffer( BufferResource, LoggerContext );
TraceDebug((4, "WmiTraceKernelEvent: 0 %d->%d\n",
RefCount+1, RefCount));
return STATUS_SUCCESS;
}
#if 0
NTKERNELAPI
NTSTATUS
WmiTraceLongEvent(
IN ULONG GroupType,
IN PMOF_FIELD EventFields,
IN ULONG FieldCount,
IN PETHREAD Thread
)
/*++
Routine Description:
This routine is used by trace kernel events with long traces only.
These events can be charged to the given thread instead of the running
thread. Because it can be called by I/O events at DPC level, this routine
cannot be pageable when tracing is on.
This routine is used to trace filenames.
This routine works at IRQL <= DISPATCH_LEVEL
Arguments:
GroupType a ULONG key to indicate the action to be taken
EventFields an array of structures describing each field
to be attached to event trace
FieldCount number of array entries in EventFields
Thread Pointer to thread where event is to be charged.
Return Value:
The status of performing the action requested
--*/
{
PSYSTEM_TRACE_HEADER Header;
ULONG Size, i, maxLength;
PWMI_BUFFER_HEADER BufferResource;
NTSTATUS Status;
PWMI_LOGGER_CONTEXT LoggerContext = WmipLoggerContext[WmipKernelLogger];
char *auxInfo;
#if DBG
LONG RefCount;
#endif
// Make sure that kernel logger is enabled first
if (LoggerContext == NULL)
return STATUS_ALREADY_DISCONNECTED;
#if DBG
RefCount =
#endif
WmipReferenceLogger(WmipKernelLogger);
/* Compute total size of event trace record */
Size = sizeof(SYSTEM_TRACE_HEADER);
maxLength = LoggerContext->BufferSize / 2;
for (i=0; i<FieldCount; i++) {
// if (EventFields[i].Length > maxLength) {
// Size += maxLength / 2;
// break;
// }
Size += EventFields[i].Length;
}
if (Size > LoggerContext->BufferSize - sizeof(WMI_BUFFER_HEADER)) {
LoggerContext->EventsLost++;
LoggerContext->ProcessorBuffers[KeGetCurrentProcessorNumber()]
->EventsLost++;
#if DBG
RefCount =
#endif
WmipDereferenceLogger(WmipKernelLogger);
return STATUS_BUFFER_OVERFLOW;
}
Header = (PSYSTEM_TRACE_HEADER)
WmipReserveTraceBuffer(
LoggerContext,
Size,
&BufferResource
);
if (Header == NULL) {
#if DBG
RefCount =
#endif
WmipDereferenceLogger(WmipKernelLogger);
return STATUS_NO_MEMORY;
}
// Get the current system time as time stamp for trace record
Header->SystemTime.QuadPart = (*LoggerContext->GetCpuClock)();
if (Thread == NULL) {
Thread = PsGetCurrentThread();
}
//
// Now copy the necessary information into the buffer
//
Header->Marker = SYSTEM_TRACE_MARKER;
Header->ThreadId = HandleToUlong(Thread->Cid.UniqueThread);
Header->ProcessId = HandleToUlong(Thread->Cid.UniqueProcess);
Header->KernelTime = Thread->Tcb.KernelTime;
Header->UserTime = Thread->Tcb.UserTime;
Header->Header = (GroupType << 16) + Size;
auxInfo = (char *) Header + sizeof(SYSTEM_TRACE_HEADER);
for (i=0; i<FieldCount; i++) {
Size = EventFields[i].Length;
// For NT5, do not support large events
/* if (Size > maxLength) {
RtlCopyMemory(auxInfo, (PVOID) EventFields[i].DataPtr, maxLength/2);
EventFields[i].DataPtr = (ULONGLONG)
((char*) EventFields[i].DataPtr +
(maxLength / 2));
EventFields[i].Length -= maxLength / 2;
GroupType &= 0xFFFFFF00; // turn off event to info
WmiTraceLongEvent(GroupType,
&EventFields[i], FieldCount + 1 - i, Thread);
break;
}
*/
RtlCopyMemory ( auxInfo, (PVOID) EventFields[i].DataPtr, Size);
auxInfo += Size;
}
WmipReleaseTraceBuffer( BufferResource, LoggerContext );
return STATUS_SUCCESS;
}
#endif
NTKERNELAPI
NTSTATUS
FASTCALL
WmiTraceFastEvent(
IN PWNODE_HEADER Wnode
)
/*++
Routine Description:
This routine is used by short events using abbreviated header.
This routine should work at any IRQL.
Arguments:
Wnode Header of event to record
Return Value:
The status of performing the action requested
--*/
{
ULONG Size;
PTIMED_TRACE_HEADER Buffer;
PTIMED_TRACE_HEADER Header = (PTIMED_TRACE_HEADER) Wnode;
PWMI_BUFFER_HEADER BufferResource;
ULONG LoggerId = (ULONG) Header->LoggerId; // get the lower ULONG!!
PULONG Marker;
PWMI_LOGGER_CONTEXT LoggerContext;
#if DBG
LONG RefCount;
#endif
Marker = (PULONG) Wnode;
if ((LoggerId == WmipKernelLogger) || (LoggerId >= MAXLOGGERS))
return STATUS_INVALID_HANDLE;
if ((*Marker & 0xF0000000) == TRACE_HEADER_ULONG32_TIME) {
Size = Header->Size;
if (Size == 0)
return STATUS_INVALID_BUFFER_SIZE;
#if DBG
RefCount =
#endif
WmipReferenceLogger(LoggerId);
LoggerContext = WmipGetLoggerContext(LoggerId);
if (!WmipIsValidLogger(LoggerContext)) {
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
return STATUS_INVALID_HANDLE;
}
Buffer = WmipReserveTraceBuffer(LoggerContext, Size, &BufferResource);
if (Buffer == NULL) {
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
return STATUS_NO_MEMORY;
}
(* (PULONG) Buffer) = *Marker;
Buffer->EventId = Header->EventId;
Buffer->TimeStamp.QuadPart = (*LoggerContext->GetCpuClock)();
RtlCopyMemory(Buffer+1, Header+1, Size-(sizeof(TIMED_TRACE_HEADER)));
WmipReleaseTraceBuffer(BufferResource, LoggerContext);
return STATUS_SUCCESS;
}
TraceDebug((4, "WmiTraceFastEvent: Invalid header %X\n", *Marker));
return STATUS_INVALID_PARAMETER;
}
NTKERNELAPI
NTSTATUS
WmiTraceMessage(
IN TRACEHANDLE LoggerHandle,
IN ULONG MessageFlags,
IN LPGUID MessageGuid,
IN USHORT MessageNumber,
...
)
/*++
Routine Description:
This routine is used by WMI data providers to trace Messages.
It expects the user to pass in the handle to the logger.
Also, the user cannot ask to log something that is larger than
the buffer size (minus buffer header).
Arguments:
// IN TRACEHANDLE LoggerHandle - LoggerHandle obtained earlier
// IN ULONG MessageFlags, - Flags which both control what standard values are logged and
// the lower 16-bits are also included in the message header
// to control decoding
// IN PGUID MessageGuid, - Pointer to the message GUID of this set of messages or if
// TRACE_COMPONENTID is set the actual compnent ID
// IN USHORT MessageNumber, - The type of message being logged, associates it with the
// appropriate format string
// ... - List of arguments to be processed with the format string
// these are stored as pairs of
// PVOID - ptr to argument
// ULONG - size of argument
// and terminated by a pointer to NULL, length of zero pair.
Return Value:
STATUS_SUCCESS if the event trace is recorded successfully
NOTE:
this routine is called from WmiTraceUserMessage path via an IOCTL so the probes and
try/excepts have to be carefully managed
--*/
{
va_list MessageArgList ;
va_start(MessageArgList,MessageNumber);
return (WmiTraceMessageVa(LoggerHandle,
MessageFlags,
MessageGuid,
MessageNumber,
MessageArgList));
}
NTSTATUS
WmiTraceMessageVa(
IN TRACEHANDLE LoggerHandle,
IN ULONG MessageFlags,
IN LPGUID MessageGuid,
IN USHORT MessageNumber,
va_list MessageArgList
)
/*++ WmiTraceMessageVa
The VA version of WmiTraceMessage
NOTE:
this routine is called from WmiTraceUserMessage path via an IOCTL so the probes and
try/excepts have to be carefully managed
--*/
{
SSIZE_T dataBytes;
PMESSAGE_TRACE_HEADER Header;
PUCHAR pMessageData ;
PWMI_BUFFER_HEADER BufferResource = NULL ;
USHORT size ;
ULONG LoggerId = (ULONG)-1 ; // initialise so we don't unlock it if not set
ULONG SequenceNumber ;
PWMI_LOGGER_CONTEXT LoggerContext;
#if DBG
LONG RefCount;
#endif
// Set the LoggerId up here, and lock it.
// if we AV in the WmiUserTraceMessagePath we will
// be caught by the try/except in that routine
LoggerId = WmiGetLoggerId(LoggerHandle);
if (LoggerId < 1 || LoggerId >= MAXLOGGERS)
return STATUS_INVALID_HANDLE;
#if DBG
RefCount =
#endif
WmipReferenceLogger(LoggerId);
TraceDebug((4, "WmiTraceMessage: %d %d->%d\n",
LoggerId, RefCount-1, RefCount));
try {
//
// Determine the number bytes to follow header
//
dataBytes = 0;
{ // Allocation Block
va_list ap;
PCHAR source;
ap = MessageArgList ;
while ((source = va_arg (ap, PVOID)) != NULL) {
SSIZE_T elemBytes;
if ((elemBytes = va_arg (ap, SSIZE_T)) > 0) {
dataBytes += elemBytes;
}
}
} // end of allocation block
// Figure the size of the message including the header
size = (MessageFlags&TRACE_MESSAGE_SEQUENCE ? sizeof(ULONG):0) +
(MessageFlags&TRACE_MESSAGE_GUID ? sizeof(GUID):0) +
(MessageFlags&TRACE_MESSAGE_COMPONENTID ? sizeof(ULONG):0) +
(MessageFlags&(TRACE_MESSAGE_TIMESTAMP | TRACE_MESSAGE_PERFORMANCE_TIMESTAMP) ? sizeof(LARGE_INTEGER):0) +
(MessageFlags&TRACE_MESSAGE_SYSTEMINFO ? 2 * sizeof(ULONG):0) +
sizeof (MESSAGE_TRACE_HEADER) +
(USHORT)dataBytes ;
//
// Allocate Space in the Trace Buffer
//
// NOTE: we do not check for size here for reduce overhead, and because
// we trust ourselves to use WmiTraceLongEvent for large event traces (???)
LoggerContext = WmipGetLoggerContext(LoggerId);
if (!WmipIsValidLogger(LoggerContext)) {
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((4, "WmiTraceMessage: Status1=%X %d %d->%d\n",
STATUS_INVALID_HANDLE, LoggerId,
RefCount+1, RefCount));
return STATUS_INVALID_HANDLE;
}
if ((LoggerContext->RequestFlag & REQUEST_FLAG_CIRCULAR_PERSIST) &&
(LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_CIRCULAR)) {
// Unset REQUEST_FLAG_CIRCULAR_PERSIST flag
// Since persistent events will never be mixed with TraceMessage(),
// we'll just unset it once and for all without flushing.
LoggerContext->RequestFlag &= (~( REQUEST_FLAG_CIRCULAR_PERSIST
| REQUEST_FLAG_CIRCULAR_TRANSITION));
}
if ((KeGetPreviousMode() == KernelMode) &&
(LoggerContext->LoggerMode & EVENT_TRACE_USE_PAGED_MEMORY)) {
return STATUS_UNSUCCESSFUL;
}
if ((Header = (PMESSAGE_TRACE_HEADER)WmipReserveTraceBuffer(LoggerContext,size,&BufferResource)) == NULL) {
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((4, "WmiTraceMessage: %d %d->%d\n",
LoggerId, RefCount+1, RefCount));
return STATUS_NO_MEMORY;
}
//
// Sequence Number is returned in the Marker field of the buffer
//
SequenceNumber = Header->Marker ;
//
// Now copy the necessary information into the buffer
//
Header->Marker = TRACE_MESSAGE | TRACE_HEADER_FLAG ;
//
// Fill in Header.
//
Header->Size = size ;
Header->Packet.OptionFlags = ((USHORT)MessageFlags &
(TRACE_MESSAGE_SEQUENCE |
TRACE_MESSAGE_GUID |
TRACE_MESSAGE_COMPONENTID |
TRACE_MESSAGE_TIMESTAMP |
TRACE_MESSAGE_PERFORMANCE_TIMESTAMP |
TRACE_MESSAGE_SYSTEMINFO)) &
TRACE_MESSAGE_FLAG_MASK ;
// Message Number
Header->Packet.MessageNumber = MessageNumber ;
//
// Now add in the header options we counted.
//
pMessageData = &(((PMESSAGE_TRACE)Header)->Data);
//
// 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) {
*((PULONG)pMessageData) = SequenceNumber;
pMessageData += sizeof(ULONG) ;
}
// [Second Entry] GUID ? or CompnentID ?
if (MessageFlags&TRACE_MESSAGE_COMPONENTID) {
*((PULONG)pMessageData) = *((PULONG) MessageGuid);
pMessageData += sizeof(ULONG) ;
} else if (MessageFlags&TRACE_MESSAGE_GUID) { // Can't have both
*((LPGUID)pMessageData) = *MessageGuid;
pMessageData += sizeof(GUID) ;
}
// [Third Entry] Timestamp?
if (MessageFlags&TRACE_MESSAGE_TIMESTAMP) {
if (MessageFlags&TRACE_MESSAGE_PERFORMANCE_TIMESTAMP) {
*((PLARGE_INTEGER)pMessageData) = KeQueryPerformanceCounter(NULL);
} else {
KeQuerySystemTime((PLARGE_INTEGER)pMessageData);
}
pMessageData += sizeof(LARGE_INTEGER);
}
// [Fourth Entry] System Information?
// Note that some of this may NOT be valid depending on the current processing level
// however we assume that the post-processing may still find it useful
if (MessageFlags&TRACE_MESSAGE_SYSTEMINFO) {
PCLIENT_ID Cid; // avoid additional function calls
Cid = &(PsGetCurrentThread()->Cid);
// Executive Handles may be truncated
*((PULONG)pMessageData) = HandleToUlong(Cid->UniqueThread);
pMessageData += sizeof(ULONG) ;
*((PULONG)pMessageData) = HandleToUlong(Cid->UniqueProcess);
pMessageData += sizeof(ULONG) ;
}
//
// Add New Header Entries immediately before this comment!
//
//
// Now Copy in the Data.
//
{ // Allocation Block
va_list ap;
PUCHAR source;
ap = MessageArgList ;
while ((source = va_arg (ap, PVOID)) != NULL) {
SSIZE_T elemBytes, copiedBytes = 0 ;
if ((elemBytes = va_arg (ap, SSIZE_T)) > 0 ) {
if (dataBytes < copiedBytes + elemBytes) { // defend against bytes having changed
TraceDebug((1, "WmiTraceMessage: Invalid message - argument length changed"));
break; // So we don't overrun
}
RtlCopyMemory (pMessageData, source, elemBytes);
copiedBytes += elemBytes ;
pMessageData += elemBytes;
}
}
} // Allocation Block
} except (EXCEPTION_EXECUTE_HANDLER) {
if (BufferResource != NULL) {
WmipReleaseTraceBuffer ( BufferResource, LoggerContext ); // also unlocks the logger
} else {
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
}
TraceDebug((4, "WmiTraceMessage: Status6=EXCEPTION %d %d->%d\n",
LoggerId, RefCount+1, RefCount));
return GetExceptionCode();
}
//
// Buffer Complete, Release
//
WmipReleaseTraceBuffer( BufferResource, LoggerContext ); // Also unlocks the logger
TraceDebug((4, "WmiTraceMessage: %d %d->%d\n",
LoggerId, RefCount+1, RefCount));
//
// Return Success
//
return (STATUS_SUCCESS);
}
NTKERNELAPI
NTSTATUS
FASTCALL
WmiTraceUserMessage(
IN PMESSAGE_TRACE_USER pMessage,
IN ULONG MessageSize
)
/*++
Routine Description:
This routine is used by trace User messages only. it is called via an IOCTL
on the WMI Device.
Arguments:
pMessage a pointer to a Marshalled Message.
MessageSize size of that message.
Return Value:
The status of performing the action requested
--*/
{
if (MessageSize < sizeof(MESSAGE_TRACE_USER)) {
return (STATUS_UNSUCCESSFUL);
}
try {
ProbeForRead(
pMessage,
MessageSize,
sizeof (UCHAR)
);
return (WmiTraceMessage(pMessage->LoggerHandle,
pMessage->MessageFlags,
&pMessage->MessageGuid,
pMessage->MessageHeader.Packet.MessageNumber,
&pMessage->Data,pMessage->DataSize,
NULL,0));
} except (EXCEPTION_EXECUTE_HANDLER) {
TraceDebug((1, "WmiTraceUserMessage: Status=EXCEPTION\n"));
return GetExceptionCode();
}
}
NTKERNELAPI
NTSTATUS
WmiSetMark(
IN PWMI_SET_MARK_INFORMATION MarkInfo,
IN ULONG InBufferLen
)
/*++
Routine Description:
This routine sets a mark in the kernel logger.
Arguments:
MarkInfo - a pointer to a WMI_SET_MARK_INFORMATION strcture.
InBufferLen - Buffer Size.
Return Value:
status
--*/
{
NTSTATUS Status;
PERFINFO_HOOK_HANDLE Hook;
ULONG TotalBytes;
ULONG CopyBytes;
USHORT HookId;
if (PERFINFO_IS_ANY_GROUP_ON()) {
if (MarkInfo->Flag & WMI_SET_MARK_WITH_FLUSH) {
if (PERFINFO_IS_GROUP_ON(PERF_FOOTPRINT)) {
MmEmptyAllWorkingSets();
Status = MmPerfSnapShotValidPhysicalMemory();
}
}
HookId = PERFINFO_LOG_TYPE_MARK;
CopyBytes = InBufferLen - FIELD_OFFSET(WMI_SET_MARK_INFORMATION, Mark);
TotalBytes = CopyBytes + sizeof(WCHAR);
Status = PerfInfoReserveBytes(&Hook, HookId, TotalBytes);
if (NT_SUCCESS(Status)){
PWCHAR Mark = PERFINFO_HOOK_HANDLE_TO_DATA(Hook, PWCHAR);
RtlCopyMemory(Mark, &MarkInfo->Mark[0], CopyBytes);
Mark[CopyBytes / sizeof(WCHAR)] = UNICODE_NULL;
PERF_FINISH_HOOK(Hook);
}
} else {
Status = STATUS_WMI_SET_FAILURE;
}
return Status;
}
NTKERNELAPI
NTSTATUS
WmiSetTraceBufferCallback(
IN TRACEHANDLE TraceHandle,
IN WMI_TRACE_BUFFER_CALLBACK Callback,
IN PVOID Context
)
{
ULONG LoggerId;
#if DBG
LONG RefCount;
#endif
PWMI_LOGGER_CONTEXT LoggerContext;
PAGED_CODE();
if (TraceHandle == (TRACEHANDLE) 0) {
WmipGlobalBufferCallback = Callback;
return STATUS_SUCCESS;
}
LoggerId = WmiGetLoggerId(TraceHandle);
if (LoggerId == KERNEL_LOGGER_ID) {
LoggerId = WmipKernelLogger;
}
else if (LoggerId < 1 || LoggerId >= MAXLOGGERS)
return STATUS_INVALID_HANDLE;
#if DBG
RefCount =
#endif
WmipReferenceLogger(LoggerId);
TraceDebug((4, "WmiSetTraceBufferCallback: %d %d->%d\n",
LoggerId, RefCount-1, RefCount));
LoggerContext = WmipGetLoggerContext( LoggerId );
if (!WmipIsValidLogger(LoggerContext)) {
#if DBG
RefCount =
#endif
WmipDereferenceLogger(LoggerId);
TraceDebug((4, "WmiSetTraceBufferCallback: Status=%X %d %d->%d\n",
STATUS_WMI_INSTANCE_NOT_FOUND,
LoggerId, RefCount+1, RefCount));
return STATUS_WMI_INSTANCE_NOT_FOUND;
}
LoggerContext->BufferCallback = Callback;
LoggerContext->CallbackContext = Context;
return STATUS_SUCCESS;
}
NTKERNELAPI
NTSTATUS
WmiQueryTraceInformation(
IN TRACE_INFORMATION_CLASS TraceInformationClass,
OUT PVOID TraceInformation,
IN ULONG TraceInformationLength,
OUT PULONG RequiredLength OPTIONAL,
IN PVOID Buffer OPTIONAL
)
{
ULONG LoggerId;
ULONG EnableFlags;
ULONG EnableLevel;
ULONG LoggersLength;
TRACEHANDLE TraceHandle;
TRACEHANDLE AllHandles[MAXLOGGERS];
NTSTATUS Status = STATUS_SUCCESS;
PWNODE_HEADER Wnode = (PWNODE_HEADER) Buffer; // For most classes, but not all
PAGED_CODE();
try {
if (ARGUMENT_PRESENT(RequiredLength)) {
*RequiredLength = 0;
}
switch (TraceInformationClass) {
case TraceIdClass:
if (TraceInformationLength != sizeof( ULONG )) {
return STATUS_INFO_LENGTH_MISMATCH;
}
if (Wnode == NULL) {
return STATUS_INVALID_PARAMETER_MIX;
}
TraceHandle = Wnode->HistoricalContext;
if ((TraceHandle == 0) || (TraceHandle == (ULONG) -1)) {
return STATUS_INVALID_HANDLE;
}
LoggerId = WmiGetLoggerId(TraceHandle);
if (LoggerId > MAXLOGGERS) {
return STATUS_INVALID_HANDLE;
}
if (TraceInformation) {
*((PULONG)TraceInformation) = LoggerId;
}
if (ARGUMENT_PRESENT( RequiredLength )) {
*RequiredLength = sizeof( ULONG );
}
break;
case TraceHandleClass:
if (TraceInformationLength != sizeof(TRACEHANDLE)) {
return STATUS_INFO_LENGTH_MISMATCH;
}
if (Buffer == NULL) {
return STATUS_INVALID_PARAMETER_MIX;
}
LoggerId = *((PULONG) Buffer);
TraceHandle = 0;
TraceHandle = WmiSetLoggerId(LoggerId, &TraceHandle);
if (TraceInformation) {
*((PTRACEHANDLE)TraceInformation) = TraceHandle;
}
if (ARGUMENT_PRESENT( RequiredLength )) {
*RequiredLength = sizeof( TRACEHANDLE );
}
break;
case TraceEnableFlagsClass:
if (TraceInformationLength < sizeof(ULONG)) {
return STATUS_INFO_LENGTH_MISMATCH;
}
if (Wnode == NULL) {
return STATUS_INVALID_PARAMETER_MIX;
}
TraceHandle = Wnode->HistoricalContext;
if ((TraceHandle == 0) || (TraceHandle == (ULONG) -1)) {
return STATUS_INVALID_HANDLE;
}
EnableFlags = WmiGetLoggerEnableFlags(TraceHandle);
if (TraceInformation) {
*((PULONG)TraceInformation) = EnableFlags;
}
if (ARGUMENT_PRESENT( RequiredLength )) {
*RequiredLength = sizeof( ULONG );
}
break;
case TraceEnableLevelClass:
if (TraceInformationLength < sizeof(ULONG)) {
return STATUS_INFO_LENGTH_MISMATCH;
}
if (Wnode == NULL) {
return STATUS_INVALID_PARAMETER_MIX;
}
TraceHandle = Wnode->HistoricalContext;
if ((TraceHandle == 0) || (TraceHandle == (ULONG) -1)) {
return STATUS_INVALID_HANDLE;
}
EnableLevel = WmiGetLoggerEnableLevel(TraceHandle);
*((PULONG)TraceInformation) = EnableLevel;
if (ARGUMENT_PRESENT( RequiredLength )) {
*RequiredLength = sizeof( ULONG );
}
break;
case GlobalLoggerHandleClass:
if (TraceInformationLength != sizeof(TRACEHANDLE)) {
return STATUS_INFO_LENGTH_MISMATCH;
}
WmipReferenceLogger(WMI_GLOBAL_LOGGER_ID);
if (WmipLoggerContext[WMI_GLOBAL_LOGGER_ID] == NULL) {
TraceHandle = 0;
Status = STATUS_NOT_FOUND;
}
else {
TraceHandle = WmipLoggerContext[WMI_GLOBAL_LOGGER_ID]->LoggerId;
}
WmipDereferenceLogger(WMI_GLOBAL_LOGGER_ID);
if (TraceInformation) {
*((PTRACEHANDLE)TraceInformation) = TraceHandle;
}
if (ARGUMENT_PRESENT( RequiredLength )) {
*RequiredLength = sizeof( TRACEHANDLE );
}
break;
case EventLoggerHandleClass:
if (TraceInformationLength != sizeof(TRACEHANDLE)) {
return STATUS_INFO_LENGTH_MISMATCH;
}
LoggerId = WmipEventLogger;
if (WmipEventLogger == (ULONG) -1) {
TraceHandle = 0;
Status = STATUS_NOT_FOUND;
}
else {
TraceHandle = LoggerId;
}
if (TraceInformation) {
*((PTRACEHANDLE)TraceInformation) = TraceHandle;
}
if (ARGUMENT_PRESENT( RequiredLength )) {
*RequiredLength = sizeof( TRACEHANDLE );
}
break;
case AllLoggerHandlesClass:
// Returns all logger handles, except for kernel logger
if (TraceInformationLength < sizeof(TRACEHANDLE)) {
return STATUS_INFO_LENGTH_MISMATCH;
}
LoggersLength = 0;
for (LoggerId=1; LoggerId<MAXLOGGERS; LoggerId++) {
WmipReferenceLogger(LoggerId);
if (!WmipIsValidLogger(WmipLoggerContext[LoggerId])) {
AllHandles[LoggersLength] = 0;
}
else {
AllHandles[LoggersLength++] = LoggerId;
}
WmipDereferenceLogger(LoggerId);
}
LoggersLength *= sizeof(TRACEHANDLE);
if (TraceInformation && (LoggersLength > 0)) {
if (TraceInformationLength >= LoggersLength) {
RtlCopyMemory(TraceInformation, AllHandles, LoggersLength);
}
else {
RtlCopyMemory(TraceInformation, AllHandles, TraceInformationLength);
Status = STATUS_MORE_ENTRIES;
}
}
if (ARGUMENT_PRESENT( RequiredLength )) {
*RequiredLength = LoggersLength;
}
break;
case TraceHandleByNameClass:
// Returns a Trace Handle Given a Logger name as a UNICODE_STRING in buffer.
{
WMI_LOGGER_INFORMATION LoggerInfo;
PUNICODE_STRING uString = Buffer;
if (TraceInformationLength != sizeof(TraceHandle) ) {
return STATUS_INFO_LENGTH_MISMATCH;
}
if (uString == NULL) {
return STATUS_INVALID_PARAMETER;
}
if (uString->Buffer == NULL || uString->Length == 0) {
return STATUS_INVALID_PARAMETER;
}
RtlZeroMemory(&LoggerInfo, sizeof(LoggerInfo));
LoggerInfo.Wnode.BufferSize = sizeof(LoggerInfo);
LoggerInfo.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
RtlInitUnicodeString(&LoggerInfo.LoggerName, uString->Buffer);
Status = WmiQueryTrace(&LoggerInfo);
if (!NT_SUCCESS(Status)) {
return STATUS_NOT_FOUND;
}
TraceHandle = (TRACEHANDLE)LoggerInfo.Wnode.HistoricalContext;
if (TraceInformation) {
*((PTRACEHANDLE)TraceInformation) = TraceHandle;
}
if (ARGUMENT_PRESENT( RequiredLength )) {
*RequiredLength = sizeof( TRACEHANDLE );
}
}
break;
default :
return STATUS_INVALID_INFO_CLASS;
}
} except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
return Status;
}