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.
2575 lines
85 KiB
2575 lines
85 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
tracesup.c
|
|
|
|
Abstract:
|
|
|
|
This is the source file that implements the private routines of
|
|
the performance event tracing and logging facility. These routines
|
|
work on manipulating the LoggerContext table and synchronization
|
|
across event tracing sessions.
|
|
|
|
Author:
|
|
|
|
Jee Fung Pang (jeepang) 03-Jan-2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "wmikmp.h"
|
|
#include <ntos.h>
|
|
#include <evntrace.h>
|
|
|
|
#include <wmi.h>
|
|
#include "tracep.h"
|
|
|
|
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
|
#define KERNEL_LOGGER_CAPTION L"NT Kernel Logger"
|
|
#define DEFAULT_BUFFERS 2
|
|
#define DEFAULT_AGE_LIMIT 15 // 15 minutes
|
|
#define SEMAPHORE_LIMIT 1024
|
|
#define CONTEXT_SIZE PAGE_SIZE
|
|
#define DEFAULT_MAX_IRQL DISPATCH_LEVEL
|
|
#define DEFAULT_MAX_BUFFERS 20
|
|
|
|
ULONG WmipKernelLogger = KERNEL_LOGGER;
|
|
ULONG WmipEventLogger = 0XFFFFFFFF;
|
|
KGUARDED_MUTEX WmipTraceGuardedMutex;
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg("PAGEDATA")
|
|
#endif
|
|
ULONG WmipLoggerCount = 0;
|
|
HANDLE EtwpPageLockHandle = NULL;
|
|
PSECURITY_DESCRIPTOR EtwpDefaultTraceSecurityDescriptor;
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg()
|
|
#endif
|
|
|
|
extern SIZE_T MmMaximumNonPagedPoolInBytes;
|
|
extern SIZE_T MmSizeOfPagedPoolInBytes;
|
|
|
|
WMI_GET_CPUCLOCK_ROUTINE WmiGetCpuClock = &WmipGetSystemTime;
|
|
|
|
//
|
|
// Function prototypes for routines used locally
|
|
//
|
|
|
|
NTSTATUS
|
|
WmipLookupLoggerIdByName(
|
|
IN PUNICODE_STRING Name,
|
|
OUT PULONG LoggerId
|
|
);
|
|
|
|
PWMI_LOGGER_CONTEXT
|
|
WmipInitContext(
|
|
);
|
|
|
|
NTSTATUS
|
|
WmipAllocateTraceBufferPool(
|
|
IN PWMI_LOGGER_CONTEXT LoggerContext
|
|
);
|
|
|
|
NTSTATUS
|
|
WmipFreeTraceBufferPool(
|
|
IN PWMI_LOGGER_CONTEXT LoggerContext
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, WmipStartLogger)
|
|
#pragma alloc_text(PAGE, WmipQueryLogger)
|
|
#pragma alloc_text(PAGE, WmipStopLoggerInstance)
|
|
#pragma alloc_text(PAGE, WmipVerifyLoggerInfo)
|
|
#pragma alloc_text(PAGE, WmipExtendBase)
|
|
#pragma alloc_text(PAGE, WmipFreeLoggerContext)
|
|
#pragma alloc_text(PAGE, WmipInitContext)
|
|
#pragma alloc_text(PAGE, WmipAllocateTraceBufferPool)
|
|
#pragma alloc_text(PAGE, WmipFreeTraceBufferPool)
|
|
#pragma alloc_text(PAGE, WmipLookupLoggerIdByName)
|
|
#pragma alloc_text(PAGE, WmipShutdown)
|
|
#pragma alloc_text(PAGE, WmipFlushLogger)
|
|
#pragma alloc_text(PAGE, WmipNtDllLoggerInfo)
|
|
#pragma alloc_text(PAGE, WmipValidateClockType)
|
|
/* Look at the comments in the function body
|
|
#pragma alloc_text(PAGE, WmipDumpGuidMaps)
|
|
#pragma alloc_text(PAGE, WmipGetTraceBuffer)
|
|
*/
|
|
#pragma alloc_text(PAGEWMI, WmipNotifyLogger)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
WmipStartLogger(
|
|
IN OUT PWMI_LOGGER_INFORMATION LoggerInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
It is called by WmipIoControl in wmi.c, with IOCTL_WMI_START_LOGGER
|
|
to start up an instance of the logger. It basically creates and
|
|
initializes the logger instance context, and starts up a system
|
|
thread for the logger (WmipLogger()). If the user has requested to
|
|
turn on kernel tracing, it will also lock in the necessary routines
|
|
after the logger has started.
|
|
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;
|
|
ULONG LoggerId, EnableKernel, EnableFlags;
|
|
HANDLE ThreadHandle;
|
|
PWMI_LOGGER_CONTEXT LoggerContext;
|
|
LARGE_INTEGER TimeOut = {(ULONG)(-20 * 1000 * 1000 * 10), -1};
|
|
ACCESS_MASK DesiredAccess = TRACELOG_GUID_ENABLE;
|
|
PWMI_LOGGER_CONTEXT *ContextTable;
|
|
PFILE_OBJECT FileObject;
|
|
GUID InstanceGuid;
|
|
KPROCESSOR_MODE RequestorMode;
|
|
SECURITY_QUALITY_OF_SERVICE ServiceQos;
|
|
PTRACE_ENABLE_FLAG_EXTENSION FlagExt = NULL;
|
|
PERFINFO_GROUPMASK *PerfGroupMasks=NULL;
|
|
BOOLEAN IsGlobalForKernel = FALSE;
|
|
BOOLEAN IsKernelRealTimeNoFile = FALSE;
|
|
ULONG GroupMaskSize;
|
|
UNICODE_STRING FileName, LoggerName;
|
|
ULONG LogFileMode;
|
|
#if DBG
|
|
LONG RefCount;
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
if (LoggerInfo == NULL)
|
|
return STATUS_SEVERITY_ERROR;
|
|
|
|
//
|
|
// try and check for bogus parameter
|
|
// if the size is at least what we want, we have to assume it's valid
|
|
//
|
|
if (LoggerInfo->Wnode.BufferSize < sizeof(WMI_LOGGER_INFORMATION))
|
|
return STATUS_INVALID_BUFFER_SIZE;
|
|
|
|
if (! (LoggerInfo->Wnode.Flags & WNODE_FLAG_TRACED_GUID) )
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
LogFileMode = LoggerInfo->LogFileMode;
|
|
if ( (LogFileMode & EVENT_TRACE_FILE_MODE_SEQUENTIAL) &&
|
|
(LogFileMode & EVENT_TRACE_FILE_MODE_CIRCULAR) ) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ( (LogFileMode & EVENT_TRACE_USE_GLOBAL_SEQUENCE) &&
|
|
(LogFileMode & EVENT_TRACE_USE_LOCAL_SEQUENCE) ) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* if (LogFileMode & EVENT_TRACE_DELAY_OPEN_FILE_MODE) {
|
|
if ((LoggerInfo->LogFileName.Length == 0) ||
|
|
(LoggerInfo->LogFileName.Buffer == NULL) )
|
|
return STATUS_INVALID_PARAMETER;
|
|
}*/
|
|
if ( !(LogFileMode & EVENT_TRACE_REAL_TIME_MODE) ) {
|
|
if ( !(LogFileMode & EVENT_TRACE_DELAY_OPEN_FILE_MODE) )
|
|
if (LoggerInfo->LogFileHandle == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Cannot support append to circular
|
|
if ( (LogFileMode & EVENT_TRACE_FILE_MODE_CIRCULAR) &&
|
|
(LogFileMode & EVENT_TRACE_FILE_MODE_APPEND) ) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
if (LogFileMode & EVENT_TRACE_REAL_TIME_MODE) {
|
|
DesiredAccess |= TRACELOG_CREATE_REALTIME;
|
|
}
|
|
|
|
if ((LoggerInfo->LogFileHandle != NULL) ||
|
|
(LogFileMode & EVENT_TRACE_DELAY_OPEN_FILE_MODE)) {
|
|
DesiredAccess |= TRACELOG_CREATE_ONDISK;
|
|
}
|
|
|
|
EnableFlags = LoggerInfo->EnableFlags;
|
|
if (EnableFlags & EVENT_TRACE_FLAG_EXTENSION) {
|
|
FlagExt = (PTRACE_ENABLE_FLAG_EXTENSION) &EnableFlags;
|
|
|
|
if ((FlagExt->Length == 0) || (FlagExt->Offset == 0))
|
|
return STATUS_INVALID_PARAMETER;
|
|
if ((FlagExt->Length * sizeof(ULONG)) >
|
|
(LoggerInfo->Wnode.BufferSize - FlagExt->Offset))
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (LogFileMode & EVENT_TRACE_FILE_MODE_NEWFILE) {
|
|
if ((LoggerInfo->LogFileName.Buffer == NULL) ||
|
|
(LogFileMode & EVENT_TRACE_FILE_MODE_CIRCULAR) ||
|
|
(LoggerInfo->MaximumFileSize == 0) ||
|
|
IsEqualGUID(&LoggerInfo->Wnode.Guid, &SystemTraceControlGuid))
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (LogFileMode & EVENT_TRACE_USE_KBYTES_FOR_SIZE) {
|
|
// Default Minimum Buffers and default Buffer Size are computed
|
|
// later in the Context initialization after Context allocation.
|
|
// To avoid having to allocate memory for this error checking,
|
|
// we compute local parameters.
|
|
ULONG LocalMinBuffers = (ULONG)KeNumberProcessors + DEFAULT_BUFFERS;
|
|
ULONG LocalBufferSize = PAGE_SIZE / 1024;
|
|
ULONG LocalMaxBuffers;
|
|
SIZE_T WmiMaximumPoolInBytes;
|
|
|
|
if (LoggerInfo->BufferSize > 0) {
|
|
if (LoggerInfo->BufferSize > MAX_WMI_BUFFER_SIZE) {
|
|
LocalBufferSize = MAX_WMI_BUFFER_SIZE;
|
|
}
|
|
else {
|
|
LocalBufferSize = LoggerInfo->BufferSize;
|
|
}
|
|
}
|
|
if (LogFileMode & EVENT_TRACE_USE_PAGED_MEMORY) {
|
|
WmiMaximumPoolInBytes = MmSizeOfPagedPoolInBytes;
|
|
}
|
|
else {
|
|
WmiMaximumPoolInBytes = MmMaximumNonPagedPoolInBytes;
|
|
}
|
|
LocalMaxBuffers = (ULONG) (WmiMaximumPoolInBytes
|
|
/ TRACE_MAXIMUM_NP_POOL_USAGE
|
|
/ LocalBufferSize);
|
|
if (LoggerInfo->MaximumBuffers != 0 &&
|
|
LoggerInfo->MaximumBuffers < LocalMaxBuffers) {
|
|
LocalMaxBuffers = LoggerInfo->MaximumBuffers;
|
|
}
|
|
if (LocalMinBuffers < LoggerInfo->MinimumBuffers &&
|
|
LoggerInfo->MinimumBuffers < LocalMaxBuffers) {
|
|
LocalMinBuffers = LoggerInfo->MinimumBuffers;
|
|
}
|
|
// MaximumFileSize must be multiples of buffer size
|
|
if ((LoggerInfo->LogFileName.Buffer == NULL) ||
|
|
(LoggerInfo->MaximumFileSize == 0) ||
|
|
((LoggerInfo->MaximumFileSize % LocalBufferSize) != 0) ||
|
|
(LoggerInfo->MaximumFileSize < (LocalMinBuffers * LocalBufferSize))) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
RequestorMode = KeGetPreviousMode();
|
|
|
|
LoggerName.Buffer = NULL;
|
|
|
|
if (LoggerInfo->LoggerName.Length > 0) {
|
|
try {
|
|
if (RequestorMode != KernelMode) {
|
|
ProbeForRead(
|
|
LoggerInfo->LoggerName.Buffer,
|
|
LoggerInfo->LoggerName.Length,
|
|
sizeof (UCHAR) );
|
|
}
|
|
if (! RtlCreateUnicodeString(
|
|
&LoggerName,
|
|
LoggerInfo->LoggerName.Buffer) ) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
if (LoggerName.Buffer) {
|
|
RtlFreeUnicodeString(&LoggerName);
|
|
}
|
|
return GetExceptionCode();
|
|
}
|
|
Status = WmipLookupLoggerIdByName(&LoggerName, &LoggerId);
|
|
if (NT_SUCCESS(Status)) {
|
|
RtlFreeUnicodeString(&LoggerName);
|
|
return STATUS_OBJECT_NAME_COLLISION;
|
|
}
|
|
}
|
|
|
|
//
|
|
// TODO: Perhaps make the last entry of table point to another table?
|
|
//
|
|
ContextTable = (PWMI_LOGGER_CONTEXT *) &WmipLoggerContext[0];
|
|
|
|
// If NULL GUID is given, generate a random GUID.
|
|
RtlZeroMemory(&InstanceGuid, sizeof(GUID));
|
|
if (IsEqualGUID(&LoggerInfo->Wnode.Guid, &InstanceGuid)) {
|
|
Status = ExUuidCreate(&LoggerInfo->Wnode.Guid);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
else {
|
|
InstanceGuid = LoggerInfo->Wnode.Guid;
|
|
}
|
|
|
|
EnableKernel = IsEqualGUID(&InstanceGuid, &SystemTraceControlGuid);
|
|
|
|
if (EnableKernel) {
|
|
//
|
|
// Check if this is the Real-Time No LogFile case.
|
|
//
|
|
if ((LogFileMode & EVENT_TRACE_REAL_TIME_MODE) &&
|
|
!(LogFileMode & EVENT_TRACE_DELAY_OPEN_FILE_MODE)){
|
|
|
|
IsKernelRealTimeNoFile = TRUE;
|
|
|
|
}
|
|
//
|
|
// This prevents multiple threads from continuing beyond this
|
|
// point in the code. Only the first thread will progress.
|
|
//
|
|
if (InterlockedCompareExchangePointer( // if already running
|
|
&ContextTable[WmipKernelLogger], ContextTable, NULL) != NULL)
|
|
return STATUS_OBJECT_NAME_COLLISION;
|
|
|
|
LoggerId = WmipKernelLogger;
|
|
DesiredAccess |= TRACELOG_ACCESS_KERNEL_LOGGER;
|
|
}
|
|
else if (IsEqualGUID(&InstanceGuid, &GlobalLoggerGuid)) {
|
|
LoggerId = WMI_GLOBAL_LOGGER_ID;
|
|
if (InterlockedCompareExchangePointer( // if already running
|
|
&ContextTable[LoggerId], ContextTable, NULL) != NULL)
|
|
return STATUS_OBJECT_NAME_COLLISION;
|
|
if (EnableFlags & EVENT_TRACE_FLAG_EXTENSION) {
|
|
PULONG pFlag;
|
|
pFlag = (PULONG) ((PCHAR)LoggerInfo + FlagExt->Offset);
|
|
if (*pFlag != 0) {
|
|
EnableKernel = TRUE;
|
|
IsGlobalForKernel = TRUE;
|
|
WmipKernelLogger = LoggerId;
|
|
}
|
|
}
|
|
// everyone has access to send to this
|
|
}
|
|
else { // other loggers requested
|
|
for (LoggerId = 2; LoggerId < MAXLOGGERS; LoggerId++) {
|
|
if ( InterlockedCompareExchangePointer(
|
|
&ContextTable[LoggerId],
|
|
ContextTable,
|
|
NULL ) == NULL )
|
|
break; // mark the slot as busy by putting in ServiceInfo
|
|
}
|
|
|
|
if (LoggerId >= MAXLOGGERS) { // could not find any more slots
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
#if DBG
|
|
RefCount =
|
|
#endif
|
|
WmipReferenceLogger(LoggerId);
|
|
TraceDebug((1, "WmipStartLogger: %d %d->%d\n", LoggerId,
|
|
RefCount-1, RefCount));
|
|
//
|
|
// first, check to see if caller has access to proper Guids.
|
|
//
|
|
Status = WmipCheckGuidAccess(
|
|
&InstanceGuid,
|
|
DesiredAccess,
|
|
EtwpDefaultTraceSecurityDescriptor
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
RefCount =
|
|
#endif
|
|
WmipDereferenceLogger(LoggerId);
|
|
TraceDebug((1, "WmipStartLogger: Status1=%X %d %d->%d\n",
|
|
Status, LoggerId, RefCount+1, RefCount));
|
|
ContextTable[LoggerId] = NULL;
|
|
return Status;
|
|
}
|
|
|
|
// Next, try and see if we need to get the logfile object first
|
|
//
|
|
FileObject = NULL;
|
|
if (LoggerInfo->LogFileHandle != NULL) {
|
|
OBJECT_HANDLE_INFORMATION handleInformation;
|
|
ACCESS_MASK grantedAccess;
|
|
|
|
Status = ObReferenceObjectByHandle(
|
|
LoggerInfo->LogFileHandle,
|
|
0L,
|
|
IoFileObjectType,
|
|
RequestorMode,
|
|
(PVOID *) &FileObject,
|
|
&handleInformation);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
TraceDebug((1, "WmipStartLogger: Referenced FDO %X %X %d\n",
|
|
FileObject, LoggerInfo->LogFileHandle,
|
|
((POBJECT_HEADER)FileObject)->PointerCount));
|
|
if (RequestorMode != KernelMode) {
|
|
grantedAccess = handleInformation.GrantedAccess;
|
|
if (!SeComputeGrantedAccesses(grantedAccess, FILE_WRITE_DATA)) {
|
|
TraceDebug((1, "WmipStartLogger: Deref FDO %x %d\n",
|
|
FileObject,
|
|
((POBJECT_HEADER)FileObject)->PointerCount));
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
}
|
|
ObDereferenceObject(FileObject);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
RefCount =
|
|
#endif
|
|
WmipDereferenceLogger(LoggerId);
|
|
TraceDebug((1, "WmipStartLogger: Status2=%X %d %d->%d\n",
|
|
Status, LoggerId, RefCount+1, RefCount));
|
|
ContextTable[LoggerId] = NULL;
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
LoggerContext = WmipInitContext();
|
|
if (LoggerContext == NULL) {
|
|
#if DBG
|
|
RefCount =
|
|
#endif
|
|
WmipDereferenceLogger(LoggerId);
|
|
Status = STATUS_NO_MEMORY;
|
|
TraceDebug((1, "WmipStartLogger: Status5=%X %d %d->%d\n",
|
|
Status, LoggerId, RefCount+1, RefCount));
|
|
ContextTable[LoggerId] = NULL;
|
|
return Status;
|
|
}
|
|
#ifndef WMI_MUTEX_FREE
|
|
WmipInitializeMutex(&LoggerContext->LoggerMutex);
|
|
#endif
|
|
|
|
if (LogFileMode & EVENT_TRACE_USE_PAGED_MEMORY) {
|
|
LoggerContext->PoolType = PagedPool;
|
|
LoggerContext->LoggerMode |= EVENT_TRACE_USE_PAGED_MEMORY;
|
|
}
|
|
else {
|
|
LoggerContext->PoolType = NonPagedPool;
|
|
}
|
|
|
|
if (LogFileMode & EVENT_TRACE_KD_FILTER_MODE) {
|
|
LoggerContext->LoggerMode |= EVENT_TRACE_KD_FILTER_MODE;
|
|
LoggerContext->BufferCallback = &KdReportTraceData;
|
|
}
|
|
LoggerContext->InstanceGuid = InstanceGuid;
|
|
// By now, the slot will be allocated properly
|
|
|
|
LoggerContext->MaximumFileSize = LoggerInfo->MaximumFileSize;
|
|
LoggerContext->BuffersWritten = LoggerInfo->BuffersWritten;
|
|
|
|
LoggerContext->LoggerMode |= LoggerInfo->LogFileMode & 0x0000FFFF;
|
|
|
|
// For circular logging with persistent events.
|
|
if (!EnableKernel && LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_CIRCULAR_PERSIST) {
|
|
LoggerContext->RequestFlag |= REQUEST_FLAG_CIRCULAR_PERSIST;
|
|
}
|
|
|
|
// LoggerInfo->Wow is set by the kernel in IOCTL
|
|
LoggerContext->Wow = LoggerInfo->Wow;
|
|
|
|
WmipValidateClockType(LoggerInfo);
|
|
|
|
LoggerContext->UsePerfClock = LoggerInfo->Wnode.ClientContext;
|
|
|
|
if (LoggerInfo->FlushTimer > 0)
|
|
LoggerContext->FlushTimer = LoggerInfo->FlushTimer;
|
|
|
|
if (LoggerInfo->AgeLimit >= 0) { // minimum is 15 minutes
|
|
LoggerContext->BufferAgeLimit.QuadPart
|
|
= max (DEFAULT_AGE_LIMIT, LoggerInfo->AgeLimit)
|
|
* WmiOneSecond.QuadPart * 60;
|
|
}
|
|
else if (LoggerInfo->AgeLimit < 0) {
|
|
LoggerContext->BufferAgeLimit.QuadPart = 0;
|
|
}
|
|
|
|
LoggerContext->LoggerId = LoggerId;
|
|
LoggerContext->EnableFlags = EnableFlags;
|
|
LoggerContext->KernelTraceOn = EnableKernel;
|
|
LoggerContext->MaximumIrql = DEFAULT_MAX_IRQL;
|
|
|
|
if (EnableKernel) {
|
|
//
|
|
// Always reserve space for FileTable to allow file trace
|
|
// to be turn on/off dynamically
|
|
//
|
|
WmipFileTable
|
|
= (PFILE_OBJECT*) WmipExtendBase(
|
|
LoggerContext, MAX_FILE_TABLE_SIZE * sizeof(PVOID));
|
|
|
|
Status = (WmipFileTable == NULL) ? STATUS_NO_MEMORY : STATUS_SUCCESS;
|
|
if (NT_SUCCESS(Status)) {
|
|
if (! RtlCreateUnicodeString(
|
|
&LoggerContext->LoggerName, KERNEL_LOGGER_CAPTION)) {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ExFreePool(LoggerContext); // free the partial context
|
|
#if DBG
|
|
RefCount =
|
|
#endif
|
|
WmipDereferenceLogger(LoggerId);
|
|
TraceDebug((1, "WmipStartLogger: Status6=%X %d %d->%d\n",
|
|
Status, LoggerId, RefCount+1, RefCount));
|
|
ContextTable[LoggerId] = NULL;
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Next, if user provided acceptable default buffer parameters, use them.
|
|
// Otherwise, set them to predetermined default values.
|
|
//
|
|
if (LoggerInfo->BufferSize > 0) {
|
|
if (LoggerInfo->BufferSize > MAX_WMI_BUFFER_SIZE) {
|
|
LoggerInfo->BufferSize = MAX_WMI_BUFFER_SIZE;
|
|
}
|
|
LoggerContext->BufferSize = LoggerInfo->BufferSize * 1024;
|
|
}
|
|
|
|
LoggerInfo->BufferSize = LoggerContext->BufferSize / 1024;
|
|
if (LoggerInfo->MaximumBuffers >= 2) {
|
|
LoggerContext->MaximumBuffers = LoggerInfo->MaximumBuffers;
|
|
}
|
|
|
|
if (LoggerInfo->MinimumBuffers >= 2 &&
|
|
LoggerInfo->MinimumBuffers <= LoggerContext->MaximumBuffers) {
|
|
LoggerContext->MinimumBuffers = LoggerInfo->MinimumBuffers;
|
|
}
|
|
|
|
RtlInitUnicodeString(&FileName, NULL);
|
|
if (LoggerName.Buffer != NULL) {
|
|
if (LoggerContext->KernelTraceOn) {
|
|
RtlFreeUnicodeString(&LoggerName);
|
|
LoggerName.Buffer = NULL;
|
|
}
|
|
else {
|
|
RtlInitUnicodeString(&LoggerContext->LoggerName, LoggerName.Buffer);
|
|
}
|
|
}
|
|
|
|
try {
|
|
if (LoggerInfo->Checksum != NULL) {
|
|
ULONG SizeNeeded = sizeof(WNODE_HEADER)
|
|
+ sizeof(TRACE_LOGFILE_HEADER);
|
|
if (RequestorMode != KernelMode) {
|
|
ProbeForRead(LoggerInfo->Checksum, SizeNeeded, sizeof(UCHAR));
|
|
}
|
|
LoggerContext->LoggerHeader =
|
|
ExAllocatePoolWithTag(PagedPool, SizeNeeded, TRACEPOOLTAG);
|
|
if (LoggerContext->LoggerHeader != NULL) {
|
|
RtlCopyMemory(LoggerContext->LoggerHeader,
|
|
LoggerInfo->Checksum,
|
|
SizeNeeded);
|
|
}
|
|
}
|
|
if (LoggerContext->KernelTraceOn) {
|
|
if (RequestorMode != KernelMode) {
|
|
ProbeForWrite(
|
|
LoggerInfo->LoggerName.Buffer,
|
|
LoggerContext->LoggerName.Length + sizeof(WCHAR),
|
|
sizeof (UCHAR) );
|
|
}
|
|
RtlCopyUnicodeString(
|
|
&LoggerInfo->LoggerName, &LoggerContext->LoggerName);
|
|
}
|
|
if (LoggerInfo->LogFileName.Length > 0) {
|
|
if (RequestorMode != KernelMode) {
|
|
ProbeForRead(
|
|
LoggerInfo->LogFileName.Buffer,
|
|
LoggerInfo->LogFileName.Length,
|
|
sizeof (UCHAR) );
|
|
}
|
|
if (! RtlCreateUnicodeString(
|
|
&FileName,
|
|
LoggerInfo->LogFileName.Buffer) ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set up the Global mask for Perf traces
|
|
//
|
|
if (IsGlobalForKernel || IsKernelRealTimeNoFile) {
|
|
if (EnableFlags & EVENT_TRACE_FLAG_EXTENSION) {
|
|
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));
|
|
|
|
LoggerContext->EnableFlags = LoggerContext->EnableFlagArray[0];
|
|
|
|
} else {
|
|
LoggerContext->EnableFlagArray[0] = EnableFlags;
|
|
}
|
|
|
|
PerfGroupMasks = (PERFINFO_GROUPMASK *) &LoggerContext->EnableFlagArray[0];
|
|
} else {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
} else {
|
|
ASSERT((EnableFlags & EVENT_TRACE_FLAG_EXTENSION) ==0);
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
//
|
|
// The context is partially set up by now, so have to clean up
|
|
//
|
|
if (LoggerContext->LoggerName.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&LoggerContext->LoggerName);
|
|
}
|
|
if (FileName.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&FileName);
|
|
}
|
|
|
|
if (LoggerContext->LoggerHeader != NULL) {
|
|
ExFreePool(LoggerContext->LoggerHeader);
|
|
}
|
|
#if DBG
|
|
RefCount =
|
|
#endif
|
|
WmipDereferenceLogger(LoggerId);
|
|
TraceDebug((1, "WmipStartLogger: Status7=EXCEPTION %d %d->%d\n",
|
|
LoggerId, RefCount+1, RefCount));
|
|
ContextTable[LoggerId] = NULL;
|
|
ExFreePool(LoggerContext); // free the partial context
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
if (LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_NEWFILE) {
|
|
RtlInitUnicodeString(&LoggerContext->LogFilePattern, FileName.Buffer);
|
|
Status = WmipGenerateFileName(
|
|
&LoggerContext->LogFilePattern,
|
|
&LoggerContext->FileCounter,
|
|
&LoggerContext->LogFileName);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
RefCount =
|
|
#endif
|
|
WmipDereferenceLogger(LoggerId);
|
|
TraceDebug((1, "WmipStartLogger: Status8=%X %d %d->%d\n",
|
|
Status, LoggerId, RefCount+1, RefCount));
|
|
ContextTable[LoggerId] = NULL;
|
|
if (LoggerContext->LoggerHeader != NULL) {
|
|
ExFreePool(LoggerContext->LoggerHeader);
|
|
}
|
|
if (LoggerContext->LoggerName.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&LoggerContext->LoggerName);
|
|
}
|
|
if (LoggerContext->LogFileName.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&LoggerContext->LogFileName);
|
|
}
|
|
if (LoggerContext->LogFilePattern.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&LoggerContext->LogFilePattern);
|
|
}
|
|
ExFreePool(LoggerContext);
|
|
return(Status);
|
|
}
|
|
}
|
|
else {
|
|
RtlInitUnicodeString(&LoggerContext->LogFileName, FileName.Buffer);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
// 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)) {
|
|
#if DBG
|
|
RefCount =
|
|
#endif
|
|
WmipDereferenceLogger(LoggerId);
|
|
TraceDebug((1, "WmipStartLogger: Status8=%X %d %d->%d\n",
|
|
Status, LoggerId, RefCount+1, RefCount));
|
|
ContextTable[LoggerId] = NULL;
|
|
if (LoggerContext != NULL) {
|
|
if (LoggerContext->LoggerHeader != NULL) {
|
|
ExFreePool(LoggerContext->LoggerHeader);
|
|
}
|
|
if (LoggerContext->LoggerName.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&LoggerContext->LoggerName);
|
|
}
|
|
if (LoggerContext->LogFileName.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&LoggerContext->LogFileName);
|
|
}
|
|
if (LoggerContext->LogFilePattern.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&LoggerContext->LogFilePattern);
|
|
}
|
|
ExFreePool(LoggerContext);
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// Now, allocate the buffer pool and associated buffers.
|
|
// Note that buffer allocation routine will also set NumberOfBuffers and
|
|
// MaximumBuffers.
|
|
//
|
|
|
|
InitializeSListHead (&LoggerContext->FreeList);
|
|
InitializeSListHead (&LoggerContext->FlushList);
|
|
InitializeSListHead (&LoggerContext->WaitList);
|
|
InitializeSListHead (&LoggerContext->GlobalList);
|
|
|
|
#ifdef NTPERF
|
|
//
|
|
// Check if we are logging into perfmem.
|
|
//
|
|
if (PERFINFO_IS_PERFMEM_ALLOCATED()) {
|
|
if (NT_SUCCESS(PerfInfoStartPerfMemLog())) {
|
|
LoggerContext->MaximumBuffers = PerfQueryBufferSizeBytes()/LoggerContext->BufferSize;
|
|
}
|
|
}
|
|
#endif //NTPERF
|
|
|
|
Status = WmipAllocateTraceBufferPool(LoggerContext);
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (LoggerContext != NULL) {
|
|
if (LoggerContext->LoggerHeader != NULL) {
|
|
ExFreePool(LoggerContext->LoggerHeader);
|
|
}
|
|
if (LoggerContext->LoggerName.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&LoggerContext->LoggerName);
|
|
}
|
|
if (LoggerContext->LogFileName.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&LoggerContext->LogFileName);
|
|
}
|
|
if (LoggerContext->LogFilePattern.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&LoggerContext->LogFilePattern);
|
|
}
|
|
ExFreePool(LoggerContext);
|
|
}
|
|
#if DBG
|
|
RefCount =
|
|
#endif
|
|
WmipDereferenceLogger(LoggerId);
|
|
TraceDebug((1, "WmipStartLogger: Status9=%X %d %d->%d\n",
|
|
Status, LoggerId, RefCount+1, RefCount));
|
|
ContextTable[LoggerId] = NULL;
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// From this point on, LoggerContext is a valid structure
|
|
//
|
|
LoggerInfo->NumberOfBuffers = (ULONG) LoggerContext->NumberOfBuffers;
|
|
LoggerInfo->MaximumBuffers = LoggerContext->MaximumBuffers;
|
|
LoggerInfo->MinimumBuffers = LoggerContext->MinimumBuffers;
|
|
LoggerInfo->FreeBuffers = (ULONG) LoggerContext->BuffersAvailable;
|
|
LoggerInfo->EnableFlags = LoggerContext->EnableFlags;
|
|
LoggerInfo->AgeLimit = (ULONG) (LoggerContext->BufferAgeLimit.QuadPart
|
|
/ WmiOneSecond.QuadPart / 60);
|
|
LoggerInfo->BufferSize = LoggerContext->BufferSize / 1024;
|
|
|
|
WmiSetLoggerId(LoggerId,
|
|
(PTRACE_ENABLE_CONTEXT)&LoggerInfo->Wnode.HistoricalContext);
|
|
|
|
if (LoggerContext->LoggerMode & EVENT_TRACE_USE_LOCAL_SEQUENCE)
|
|
LoggerContext->SequencePtr = (PLONG) &LoggerContext->LocalSequence;
|
|
else if (LoggerContext->LoggerMode & EVENT_TRACE_USE_GLOBAL_SEQUENCE)
|
|
LoggerContext->SequencePtr = (PLONG) &WmipGlobalSequence;
|
|
|
|
// Initialize synchronization event with logger
|
|
KeInitializeEvent(
|
|
&LoggerContext->LoggerEvent,
|
|
NotificationEvent,
|
|
FALSE
|
|
);
|
|
KeInitializeEvent(
|
|
&LoggerContext->FlushEvent,
|
|
NotificationEvent,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Close file handle here so that it can be opened by system thread
|
|
//
|
|
if (LoggerInfo->LogFileHandle != NULL) {
|
|
ZwClose(LoggerInfo->LogFileHandle);
|
|
LoggerInfo->LogFileHandle = NULL;
|
|
}
|
|
|
|
//
|
|
// User Mode call always gets APPEND mode
|
|
//
|
|
LogFileMode = LoggerContext->LoggerMode;
|
|
|
|
if (RequestorMode != KernelMode) {
|
|
LoggerContext->LoggerMode |= EVENT_TRACE_FILE_MODE_APPEND;
|
|
}
|
|
|
|
//
|
|
// Lock down the routines that need to be non-pageable
|
|
//
|
|
KeAcquireGuardedMutex(&WmipTraceGuardedMutex);
|
|
if (++WmipLoggerCount == 1) {
|
|
|
|
ASSERT(EtwpPageLockHandle);
|
|
MmLockPagableSectionByHandle(EtwpPageLockHandle);
|
|
WmipGlobalSequence = 0;
|
|
}
|
|
KeReleaseGuardedMutex(&WmipTraceGuardedMutex);
|
|
|
|
//
|
|
// Start up the logger as a system thread
|
|
//
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = PsCreateSystemThread(
|
|
&ThreadHandle,
|
|
THREAD_ALL_ACCESS,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
WmipLogger,
|
|
LoggerContext );
|
|
|
|
if (NT_SUCCESS(Status)) { // if SystemThread is started
|
|
ZwClose (ThreadHandle);
|
|
|
|
// Wait for Logger to start up properly before proceeding
|
|
//
|
|
KeWaitForSingleObject(
|
|
&LoggerContext->LoggerEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
&TimeOut
|
|
);
|
|
|
|
KeResetEvent(&LoggerContext->LoggerEvent);
|
|
//
|
|
// If the logger is up and running properly, we can now turn on
|
|
// event tracing if kernel tracing is requested
|
|
//
|
|
if (NT_SUCCESS(LoggerContext->LoggerStatus)) {
|
|
LoggerContext->LoggerMode = LogFileMode;
|
|
|
|
LoggerContext->WriteFailureLimit = 100;
|
|
#ifdef NTPERF
|
|
if (EnableKernel) {
|
|
WmiGetCpuClock = &PerfGetCycleCount;
|
|
}
|
|
LoggerContext->GetCpuClock = &PerfGetCycleCount;
|
|
#else
|
|
switch (LoggerContext->UsePerfClock) {
|
|
case EVENT_TRACE_CLOCK_PERFCOUNTER:
|
|
if (EnableKernel) {
|
|
WmiGetCpuClock = &WmipGetPerfCounter;
|
|
}
|
|
LoggerContext->GetCpuClock = &WmipGetPerfCounter;
|
|
break;
|
|
case EVENT_TRACE_CLOCK_SYSTEMTIME:
|
|
default :
|
|
if (EnableKernel) {
|
|
WmiGetCpuClock = &WmipGetSystemTime;
|
|
}
|
|
LoggerContext->GetCpuClock = &WmipGetSystemTime;
|
|
break;
|
|
}
|
|
#endif //NTPERF
|
|
|
|
//
|
|
// At this point, the clock type should be set and we take a
|
|
// reference timesamp, which should be the earliest timestamp
|
|
// for the logger. The order is this way sine SystemTime
|
|
// is typically cheaper to obtain.
|
|
//
|
|
|
|
#ifdef NTPERF
|
|
PerfTimeStamp(LoggerContext->ReferenceTimeStamp);
|
|
#else
|
|
LoggerContext->ReferenceTimeStamp.QuadPart = (*LoggerContext->GetCpuClock)();
|
|
#endif
|
|
KeQuerySystemTime(&LoggerContext->ReferenceSystemTime);
|
|
|
|
//
|
|
// After we release this mutex, any other thread can acquire
|
|
// the valid logger context and call the shutdown path for
|
|
// this logger. Until this, no other thread can call the enable
|
|
// or disable code for this logger.
|
|
//
|
|
WmipAcquireMutex( &LoggerContext->LoggerMutex );
|
|
InterlockedIncrement(&LoggerContext->MutexCount);
|
|
|
|
LoggerInfo->BuffersWritten = LoggerContext->BuffersWritten;
|
|
|
|
WmipLoggerContext[LoggerId] = LoggerContext;
|
|
TraceDebug((1, "WmipStartLogger: Started %X %d\n",
|
|
LoggerContext, LoggerContext->LoggerId));
|
|
if (LoggerContext->KernelTraceOn) {
|
|
EnableFlags = LoggerContext->EnableFlags;
|
|
if (EnableFlags & EVENT_TRACE_FLAG_DISK_FILE_IO)
|
|
EnableFlags |= EVENT_TRACE_FLAG_DISK_IO;
|
|
WmipEnableKernelTrace(EnableFlags);
|
|
}
|
|
|
|
if (IsEqualGUID(&InstanceGuid, &WmiEventLoggerGuid)) {
|
|
WmipEventLogger = LoggerId;
|
|
EnableFlags = EVENT_TRACE_FLAG_PROCESS |
|
|
EVENT_TRACE_FLAG_THREAD |
|
|
EVENT_TRACE_FLAG_IMAGE_LOAD;
|
|
WmipEnableKernelTrace(EnableFlags);
|
|
LoggerContext->EnableFlags = EnableFlags;
|
|
}
|
|
|
|
if (LoggerContext->LoggerThread) {
|
|
LoggerInfo->LoggerThreadId
|
|
= LoggerContext->LoggerThread->Cid.UniqueThread;
|
|
}
|
|
|
|
//
|
|
// Logger is started properly, now turn on perf trace
|
|
//
|
|
if (IsGlobalForKernel) {
|
|
ASSERT(LoggerContext->KernelTraceOn);
|
|
ASSERT(EnableKernel);
|
|
Status = PerfInfoStartLog(PerfGroupMasks,
|
|
PERFINFO_START_LOG_FROM_GLOBAL_LOGGER);
|
|
if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// Turning on tracing failed, needs to clean up.
|
|
// Logger Thread has been created at this point.
|
|
// Just do WmipStopLoggerInstance and let Logger Thread
|
|
// handle all the cleanup work.
|
|
//
|
|
LoggerContext->LoggerStatus = Status;
|
|
WmipStopLoggerInstance(LoggerContext);
|
|
}
|
|
} else if (IsKernelRealTimeNoFile) {
|
|
//
|
|
// We need to protect PerfInfoStartLog from stopping thread.
|
|
//
|
|
LONG PerfLogInTransition;
|
|
|
|
ASSERT(LoggerContext->KernelTraceOn);
|
|
ASSERT(EnableKernel);
|
|
PerfLogInTransition = InterlockedCompareExchange(
|
|
&LoggerContext->PerfLogInTransition,
|
|
PERF_LOG_START_TRANSITION,
|
|
PERF_LOG_NO_TRANSITION);
|
|
|
|
if (PerfLogInTransition != PERF_LOG_NO_TRANSITION) {
|
|
Status = STATUS_ALREADY_DISCONNECTED;
|
|
LoggerContext->LoggerStatus = Status;
|
|
} else {
|
|
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)) {
|
|
//
|
|
// Turning on tracing failed, needs to clean up.
|
|
// Logger Thread has been created at this point.
|
|
// Just do WmipStopLoggerInstance and let Logger Thread
|
|
// handle all the cleanup work.
|
|
//
|
|
LoggerContext->LoggerStatus = Status;
|
|
WmipStopLoggerInstance(LoggerContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
InterlockedDecrement(&LoggerContext->MutexCount);
|
|
WmipReleaseMutex(&LoggerContext->LoggerMutex);
|
|
|
|
// LoggerContext refcount is now >= 1 until it is stopped
|
|
return Status;
|
|
}
|
|
Status = LoggerContext->LoggerStatus;
|
|
}
|
|
}
|
|
TraceDebug((2, "WmipStartLogger: %d %X failed with status=%X ref %d\n",
|
|
LoggerId, LoggerContext, Status, WmipRefCount[LoggerId]));
|
|
//
|
|
// will get here if Status has failed earlier.
|
|
if (LoggerContext != NULL) { // should not be NULL
|
|
// WmipReferenceLogger(LoggerId); // Below will deref twice
|
|
WmipFreeLoggerContext(LoggerContext);
|
|
}
|
|
else {
|
|
WmipDereferenceLogger(LoggerId);
|
|
ContextTable[LoggerId] = NULL;
|
|
}
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
WmipQueryLogger(
|
|
IN OUT PWMI_LOGGER_INFORMATION LoggerInfo,
|
|
IN PWMI_LOGGER_CONTEXT LoggerContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to control the data collection and logger.
|
|
It is called by WmipIoControl in wmi.c, with IOCTL_WMI_QUERY_LOGGER.
|
|
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
|
|
|
|
LoggerContext if this is provided, it assumes it is a valid one
|
|
|
|
Return Value:
|
|
|
|
The status of performing the action requested.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG LoggerId, NoContext;
|
|
ACCESS_MASK DesiredAccess = WMIGUID_QUERY;
|
|
KPROCESSOR_MODE RequestorMode;
|
|
#if DBG
|
|
LONG RefCount;
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
NoContext = (LoggerContext == NULL);
|
|
if (NoContext) {
|
|
|
|
if ((LoggerInfo->Wnode.HistoricalContext == 0XFFFF) || (LoggerInfo->Wnode.HistoricalContext < 1))
|
|
TraceDebug((2, "WmipQueryLogger: %d\n",
|
|
LoggerInfo->Wnode.HistoricalContext));
|
|
#if DBG
|
|
Status = WmipVerifyLoggerInfo(
|
|
LoggerInfo, &LoggerContext, "WmipQueryLogger");
|
|
#else
|
|
Status = WmipVerifyLoggerInfo( LoggerInfo, &LoggerContext );
|
|
#endif
|
|
|
|
if (!NT_SUCCESS(Status) || (LoggerContext == NULL))
|
|
return Status; // cannot find by name nor logger id
|
|
|
|
LoggerInfo->Wnode.Flags = 0;
|
|
LoggerInfo->EnableFlags = 0;
|
|
LoggerId = (ULONG) LoggerContext->LoggerId;
|
|
|
|
if (LoggerContext->KernelTraceOn) {
|
|
DesiredAccess |= TRACELOG_ACCESS_KERNEL_LOGGER;
|
|
}
|
|
|
|
Status = WmipCheckGuidAccess(
|
|
&LoggerContext->InstanceGuid,
|
|
DesiredAccess,
|
|
EtwpDefaultTraceSecurityDescriptor
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
#ifndef WMI_MUTEX_FREE
|
|
InterlockedDecrement(&LoggerContext->MutexCount);
|
|
TraceDebug((1, "WmipQueryLogger: Release mutex1 %d %d\n",
|
|
LoggerId, LoggerContext->MutexCount));
|
|
WmipReleaseMutex(&LoggerContext->LoggerMutex);
|
|
#endif
|
|
#if DBG
|
|
RefCount =
|
|
#endif
|
|
WmipDereferenceLogger(LoggerId);
|
|
TraceDebug((1, "WmipQueryLogger: Status1=%X %d %d->%d\n",
|
|
Status, LoggerId, RefCount+1, RefCount));
|
|
return Status;
|
|
}
|
|
}
|
|
else {
|
|
LoggerId = LoggerContext->LoggerId;
|
|
}
|
|
|
|
if (LoggerContext->KernelTraceOn) {
|
|
LoggerInfo->Wnode.Guid = SystemTraceControlGuid;
|
|
LoggerInfo->EnableFlags = LoggerContext->EnableFlags;
|
|
}
|
|
else
|
|
LoggerInfo->Wnode.Guid = LoggerContext->InstanceGuid;
|
|
|
|
LoggerInfo->LogFileMode = LoggerContext->LoggerMode;
|
|
LoggerInfo->MaximumFileSize = LoggerContext->MaximumFileSize;
|
|
LoggerInfo->FlushTimer = LoggerContext->FlushTimer;
|
|
|
|
LoggerInfo->BufferSize = LoggerContext->BufferSize / 1024;
|
|
LoggerInfo->NumberOfBuffers = (ULONG) LoggerContext->NumberOfBuffers;
|
|
LoggerInfo->MinimumBuffers = LoggerContext->MinimumBuffers;
|
|
LoggerInfo->MaximumBuffers = LoggerContext->MaximumBuffers;
|
|
LoggerInfo->EventsLost = LoggerContext->EventsLost;
|
|
LoggerInfo->FreeBuffers = (ULONG) LoggerContext->BuffersAvailable;
|
|
LoggerInfo->BuffersWritten = LoggerContext->BuffersWritten;
|
|
LoggerInfo->Wow = LoggerContext->Wow;
|
|
LoggerInfo->LogBuffersLost = LoggerContext->LogBuffersLost;
|
|
LoggerInfo->RealTimeBuffersLost = LoggerContext->RealTimeBuffersLost;
|
|
LoggerInfo->AgeLimit = (ULONG)
|
|
(LoggerContext->BufferAgeLimit.QuadPart
|
|
/ WmiOneSecond.QuadPart / 60);
|
|
WmiSetLoggerId(LoggerId,
|
|
(PTRACE_ENABLE_CONTEXT)&LoggerInfo->Wnode.HistoricalContext);
|
|
|
|
if (LoggerContext->LoggerThread) {
|
|
LoggerInfo->LoggerThreadId
|
|
= LoggerContext->LoggerThread->Cid.UniqueThread;
|
|
}
|
|
|
|
LoggerInfo->Wnode.ClientContext = LoggerContext->UsePerfClock;
|
|
|
|
//
|
|
// Return LogFileName and Logger Caption here
|
|
//
|
|
RequestorMode = KeGetPreviousMode();
|
|
try {
|
|
if (LoggerContext->LogFileName.Length > 0 &&
|
|
LoggerInfo->LogFileName.MaximumLength > 0) {
|
|
if (RequestorMode != KernelMode) {
|
|
ProbeForWrite(
|
|
LoggerInfo->LogFileName.Buffer,
|
|
LoggerContext->LogFileName.Length + sizeof(WCHAR),
|
|
sizeof (UCHAR) );
|
|
}
|
|
RtlCopyUnicodeString(
|
|
&LoggerInfo->LogFileName,
|
|
&LoggerContext->LogFileName);
|
|
}
|
|
if (LoggerContext->LoggerName.Length > 0 &&
|
|
LoggerInfo->LoggerName.MaximumLength > 0) {
|
|
if (RequestorMode != KernelMode) {
|
|
ProbeForWrite(
|
|
LoggerInfo->LoggerName.Buffer,
|
|
LoggerContext->LoggerName.Length + sizeof(WCHAR),
|
|
sizeof(UCHAR));
|
|
}
|
|
RtlCopyUnicodeString(
|
|
&LoggerInfo->LoggerName,
|
|
&LoggerContext->LoggerName);
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
if (NoContext) {
|
|
#ifndef WMI_MUTEX_FREE
|
|
InterlockedDecrement(&LoggerContext->MutexCount);
|
|
TraceDebug((1, "WmipQueryLogger: Release mutex3 %d %d\n",
|
|
LoggerId, LoggerContext->MutexCount));
|
|
WmipReleaseMutex(&LoggerContext->LoggerMutex);
|
|
#endif
|
|
#if DBG
|
|
RefCount =
|
|
#endif
|
|
WmipDereferenceLogger(LoggerId);
|
|
TraceDebug((1, "WmipQueryLogger: Status3=EXCEPTION %d %d->%d\n",
|
|
LoggerId, RefCount+1, RefCount));
|
|
}
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
if (NoContext) {
|
|
#ifndef WMI_MUTEX_FREE
|
|
InterlockedDecrement(&LoggerContext->MutexCount);
|
|
TraceDebug((1, "WmipQueryLogger: Release mutex %d %d\n",
|
|
LoggerId, LoggerContext->MutexCount));
|
|
WmipReleaseMutex(&LoggerContext->LoggerMutex);
|
|
#endif
|
|
#if DBG
|
|
RefCount =
|
|
#endif
|
|
WmipDereferenceLogger(LoggerId);
|
|
TraceDebug((1, "WmipQueryLogger: %d %d->%d\n",
|
|
LoggerId, RefCount+1, RefCount));
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
WmipStopLoggerInstance(
|
|
IN PWMI_LOGGER_CONTEXT LoggerContext
|
|
)
|
|
{
|
|
LONG LoggerOn;
|
|
|
|
PAGED_CODE();
|
|
if (LoggerContext == NULL) { // just in case
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
if (LoggerContext->KernelTraceOn) {
|
|
// PerfInfoStopLog should not be executed when perf logging is starting
|
|
// or stopping by other thread. PerfLogInTransition flag in the logger
|
|
// context should only be used here and UpdateTrace and NO WHERE ELSE.
|
|
LONG PerfLogInTransition =
|
|
InterlockedCompareExchange(&LoggerContext->PerfLogInTransition,
|
|
PERF_LOG_STOP_TRANSITION,
|
|
PERF_LOG_NO_TRANSITION);
|
|
if (PerfLogInTransition == PERF_LOG_START_TRANSITION) {
|
|
// This is the logger thread, and it is terminating.
|
|
// UpdateTrace call is enabling perf logging at the moment.
|
|
// Come back later.
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
else if (PerfLogInTransition == PERF_LOG_STOP_TRANSITION) {
|
|
return STATUS_ALREADY_DISCONNECTED;
|
|
}
|
|
//
|
|
// Time to turn off trace in perf tools
|
|
//
|
|
PerfInfoStopLog();
|
|
}
|
|
|
|
//
|
|
// turn off data tracing first
|
|
//
|
|
LoggerOn = InterlockedExchange(&LoggerContext->CollectionOn, FALSE);
|
|
if (LoggerOn == FALSE) {
|
|
// This happens if another stoplogger already in progress
|
|
return STATUS_ALREADY_DISCONNECTED;
|
|
}
|
|
if (LoggerContext->KernelTraceOn) {
|
|
//
|
|
// Turn off everything, just to be on the safe side
|
|
// NOTE: If we start sharing callouts, the argument should be
|
|
// LoggerContext->EnableFlags
|
|
//
|
|
WmipDisableKernelTrace(LoggerContext->EnableFlags);
|
|
}
|
|
if (LoggerContext->LoggerId == WmipEventLogger) {
|
|
WmipDisableKernelTrace(EVENT_TRACE_FLAG_PROCESS |
|
|
EVENT_TRACE_FLAG_THREAD |
|
|
EVENT_TRACE_FLAG_IMAGE_LOAD);
|
|
WmipEventLogger = 0xFFFFFFFF;
|
|
}
|
|
|
|
//
|
|
// Mark the table entry as in-transition
|
|
// From here on, the stop operation will not fail
|
|
//
|
|
WmipLoggerContext[LoggerContext->LoggerId] = (PWMI_LOGGER_CONTEXT)
|
|
&WmipLoggerContext[0];
|
|
|
|
WmipNotifyLogger(LoggerContext);
|
|
|
|
WmipSendNotification(LoggerContext, STATUS_THREAD_IS_TERMINATING, 0);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
WmipVerifyLoggerInfo(
|
|
IN PWMI_LOGGER_INFORMATION LoggerInfo,
|
|
#if DBG
|
|
OUT PWMI_LOGGER_CONTEXT *pLoggerContext,
|
|
IN LPSTR Caller
|
|
#else
|
|
OUT PWMI_LOGGER_CONTEXT *pLoggerContext
|
|
#endif
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SEVERITY_ERROR;
|
|
ULONG LoggerId;
|
|
UNICODE_STRING LoggerName;
|
|
KPROCESSOR_MODE RequestorMode;
|
|
PWMI_LOGGER_CONTEXT LoggerContext, CurrentContext;
|
|
LONG MutexCount = 0;
|
|
#if DBG
|
|
LONG RefCount;
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
*pLoggerContext = NULL;
|
|
|
|
if (LoggerInfo == NULL)
|
|
return STATUS_SEVERITY_ERROR;
|
|
|
|
//
|
|
// try and check for bogus parameter
|
|
// if the size is at least what we want, we have to assume it's valid
|
|
//
|
|
|
|
if (LoggerInfo->Wnode.BufferSize < sizeof(WMI_LOGGER_INFORMATION))
|
|
return STATUS_INVALID_BUFFER_SIZE;
|
|
|
|
if (! (LoggerInfo->Wnode.Flags & WNODE_FLAG_TRACED_GUID) )
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
RtlInitUnicodeString(&LoggerName, NULL);
|
|
|
|
RequestorMode = KeGetPreviousMode();
|
|
try {
|
|
if (LoggerInfo->LoggerName.Length > 0) {
|
|
if (RequestorMode != KernelMode) {
|
|
ProbeForRead(
|
|
LoggerInfo->LoggerName.Buffer,
|
|
LoggerInfo->LoggerName.Length,
|
|
sizeof (UCHAR) );
|
|
}
|
|
RtlCreateUnicodeString(
|
|
&LoggerName,
|
|
LoggerInfo->LoggerName.Buffer);
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
if (LoggerName.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&LoggerName);
|
|
}
|
|
return GetExceptionCode();
|
|
}
|
|
Status = STATUS_SUCCESS;
|
|
if (IsEqualGUID(&LoggerInfo->Wnode.Guid, &SystemTraceControlGuid)) {
|
|
LoggerId = WmipKernelLogger;
|
|
}
|
|
else if (LoggerName.Length > 0) { // Logger Name is passed
|
|
Status = WmipLookupLoggerIdByName(&LoggerName, &LoggerId);
|
|
}
|
|
else {
|
|
LoggerId = WmiGetLoggerId(LoggerInfo->Wnode.HistoricalContext);
|
|
if (LoggerId == KERNEL_LOGGER_ID) {
|
|
LoggerId = WmipKernelLogger;
|
|
}
|
|
else if (LoggerId < 1 || LoggerId >= MAXLOGGERS) {
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
}
|
|
if (LoggerName.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&LoggerName);
|
|
}
|
|
if (!NT_SUCCESS(Status)) { // cannot find by name nor logger id
|
|
return Status;
|
|
}
|
|
|
|
#if DBG
|
|
RefCount =
|
|
#endif
|
|
WmipReferenceLogger(LoggerId);
|
|
TraceDebug((2, "WmipVerifyLoggerInfo(%s): %d %d->%d\n",
|
|
Caller, LoggerId, RefCount-1, RefCount));
|
|
|
|
LoggerContext = WmipGetLoggerContext( LoggerId );
|
|
if (!WmipIsValidLogger(LoggerContext)) {
|
|
#if DBG
|
|
RefCount =
|
|
#endif
|
|
WmipDereferenceLogger(LoggerId);
|
|
TraceDebug((2, "WmipVerifyLoggerInfo(%s): Status=%X %d %d->%d\n",
|
|
Caller, STATUS_WMI_INSTANCE_NOT_FOUND,
|
|
LoggerId, RefCount+1, RefCount));
|
|
return STATUS_WMI_INSTANCE_NOT_FOUND;
|
|
}
|
|
|
|
#ifndef WMI_MUTEX_FREE
|
|
InterlockedIncrement(&LoggerContext->MutexCount);
|
|
TraceDebug((1, "WmipVerifyLoggerInfo: Acquiring mutex... %d %d\n",
|
|
LoggerId, LoggerContext->MutexCount));
|
|
WmipAcquireMutex (&LoggerContext->LoggerMutex);
|
|
TraceDebug((1, "WmipVerifyLoggerInfo: Acquired mutex %d %d %X\n",
|
|
LoggerId, LoggerContext->MutexCount, LoggerContext));
|
|
#endif
|
|
|
|
// Need to check for validity of LoggerContext in mutex
|
|
CurrentContext = WmipGetLoggerContext( LoggerId );
|
|
if (!WmipIsValidLogger(CurrentContext) ||
|
|
!LoggerContext->CollectionOn ) {
|
|
#ifndef WMI_MUTEX_FREE
|
|
TraceDebug((1, "WmipVerifyLoggerInfo: Released mutex %d %d\n",
|
|
LoggerId, LoggerContext->MutexCount-1));
|
|
WmipReleaseMutex(&LoggerContext->LoggerMutex);
|
|
MutexCount = InterlockedDecrement(&LoggerContext->MutexCount);
|
|
#endif
|
|
#if DBG
|
|
RefCount =
|
|
#endif
|
|
WmipDereferenceLogger(LoggerId);
|
|
TraceDebug((2, "WmipVerifyLoggerInfo(%s): Status2=%X %d %d->%d\n",
|
|
Caller, STATUS_WMI_INSTANCE_NOT_FOUND,
|
|
LoggerId, RefCount+1, RefCount));
|
|
|
|
return STATUS_WMI_INSTANCE_NOT_FOUND;
|
|
}
|
|
*pLoggerContext = LoggerContext;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
PVOID
|
|
WmipExtendBase(
|
|
IN PWMI_LOGGER_CONTEXT Base,
|
|
IN ULONG Size
|
|
)
|
|
{
|
|
//
|
|
// This private routine only return space from the Base by extending its
|
|
// offset. It does not actually try and allocate memory from the system
|
|
//
|
|
// It rounds the size to a ULONGLONG alignment and expects EndPageMarker
|
|
// to already be aligned.
|
|
//
|
|
PVOID Space = NULL;
|
|
ULONG SpaceLeft;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(((ULONGLONG) Base->EndPageMarker % sizeof(ULONGLONG)) == 0);
|
|
|
|
//
|
|
// Round up to pointer boundary
|
|
//
|
|
#ifdef _WIN64
|
|
Size = ALIGN_TO_POWER2(Size, 16);
|
|
#else
|
|
Size = ALIGN_TO_POWER2(Size, DEFAULT_TRACE_ALIGNMENT);
|
|
#endif
|
|
|
|
SpaceLeft = CONTEXT_SIZE - (ULONG) (Base->EndPageMarker - (PUCHAR)Base);
|
|
|
|
if ( SpaceLeft > Size ) {
|
|
Space = Base->EndPageMarker;
|
|
Base->EndPageMarker += Size;
|
|
}
|
|
|
|
return Space;
|
|
}
|
|
|
|
VOID
|
|
WmipFreeLoggerContext(
|
|
IN PWMI_LOGGER_CONTEXT LoggerContext
|
|
)
|
|
{
|
|
ULONG LoggerId;
|
|
LONG RefCount;
|
|
LARGE_INTEGER Timeout = {(ULONG)(-50 * 1000 * 10), -1}; // 50 ms
|
|
NTSTATUS Status = STATUS_TIMEOUT;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (LoggerContext == NULL)
|
|
return; // should not happen
|
|
|
|
if (LoggerContext->LoggerHeader != NULL) {
|
|
ExFreePool(LoggerContext->LoggerHeader);
|
|
}
|
|
|
|
LoggerId = LoggerContext->LoggerId;
|
|
|
|
//
|
|
// The RefCount must be at least 2 at this point.
|
|
// One was set by WmipStartLogger() in the beginning, and the
|
|
// second must be done normally by WmiStopTrace() or anybody who
|
|
// needs to call this routine to free the logger context
|
|
//
|
|
// RefCount = WmipDereferenceLogger(LoggerId);
|
|
|
|
KeResetEvent(&LoggerContext->LoggerEvent);
|
|
RefCount = WmipRefCount[LoggerId];
|
|
WmipAssert(RefCount >= 1);
|
|
TraceDebug((1, "WmipFreeLoggerContext: %d %d->%d\n",
|
|
LoggerId, RefCount+1, RefCount));
|
|
|
|
while (RefCount > 1) {
|
|
Status = KeWaitForSingleObject(
|
|
&LoggerContext->LoggerEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
&Timeout);
|
|
KeResetEvent(&LoggerContext->LoggerEvent);
|
|
KeSetEvent(&LoggerContext->FlushEvent, 0, FALSE); // Just to be sure
|
|
|
|
#ifndef WMI_MUTEX_FREE
|
|
if (LoggerContext->MutexCount >= 1) {
|
|
KeResetEvent(&LoggerContext->LoggerEvent);
|
|
Status = STATUS_TIMEOUT;
|
|
continue;
|
|
}
|
|
#endif
|
|
if (WmipRefCount[LoggerId] <= 1)
|
|
break;
|
|
RefCount = WmipRefCount[LoggerId];
|
|
}
|
|
|
|
KeAcquireGuardedMutex(&WmipTraceGuardedMutex);
|
|
if (--WmipLoggerCount == 0) {
|
|
if (EtwpPageLockHandle) {
|
|
MmUnlockPagableImageSection(EtwpPageLockHandle);
|
|
}
|
|
#if DBG
|
|
else {
|
|
ASSERT(EtwpPageLockHandle);
|
|
}
|
|
#endif
|
|
}
|
|
KeReleaseGuardedMutex(&WmipTraceGuardedMutex);
|
|
|
|
WmipFreeTraceBufferPool(LoggerContext);
|
|
|
|
if (LoggerContext->LoggerName.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&LoggerContext->LoggerName);
|
|
}
|
|
if (LoggerContext->LogFileName.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&LoggerContext->LogFileName);
|
|
}
|
|
if (LoggerContext->LogFilePattern.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&LoggerContext->LogFilePattern);
|
|
}
|
|
if (LoggerContext->NewLogFileName.Buffer != NULL) {
|
|
RtlFreeUnicodeString(&LoggerContext->NewLogFileName);
|
|
}
|
|
#if DBG
|
|
RefCount =
|
|
#endif
|
|
//
|
|
// Finally, decrement the refcount incremented by WmipStartLogger()
|
|
//
|
|
WmipDereferenceLogger(LoggerId);
|
|
|
|
#if DBG
|
|
TraceDebug((2, "WmipFreeLoggerContext: Freeing pool %X %d %d->%d\n",
|
|
LoggerContext, LoggerId, RefCount+1, RefCount));
|
|
if (LoggerContext->CollectionOn) {
|
|
TraceDebug((1,
|
|
"WmipFreeLoggerContext: %X %d still active\n", LoggerContext,
|
|
LoggerId));
|
|
}
|
|
#ifndef WMI_MUTEX_FREE
|
|
if (LoggerContext->MutexCount >= 1) {
|
|
TraceDebug((0, "****ERROR**** Mutex count is %d for %d\n", LoggerId,
|
|
LoggerContext->MutexCount));
|
|
}
|
|
#endif // WMI_MUTEX_FREE
|
|
#endif // DBG
|
|
ExFreePool(LoggerContext);
|
|
WmipLoggerContext[LoggerId] = NULL;
|
|
}
|
|
|
|
|
|
PWMI_LOGGER_CONTEXT
|
|
WmipInitContext(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to initialize the context of LoggerContext
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returned Value:
|
|
|
|
Status of STATUS_SUCCESS if the allocation was successful
|
|
|
|
--*/
|
|
|
|
{
|
|
PWMI_LOGGER_CONTEXT LoggerContext;
|
|
|
|
PAGED_CODE();
|
|
|
|
LoggerContext = (PWMI_LOGGER_CONTEXT)
|
|
ExAllocatePoolWithTag(NonPagedPool,
|
|
CONTEXT_SIZE, TRACEPOOLTAG);
|
|
|
|
// One page is reserved to store the buffer pool pointers plus anything
|
|
// else that we need. Should experiment a little more to reduce it further
|
|
|
|
if (LoggerContext == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
RtlZeroMemory(LoggerContext, CONTEXT_SIZE);
|
|
|
|
LoggerContext->EndPageMarker =
|
|
(PUCHAR) LoggerContext +
|
|
ALIGN_TO_POWER2(sizeof(WMI_LOGGER_CONTEXT), DEFAULT_TRACE_ALIGNMENT);
|
|
|
|
LoggerContext->BufferSize = PAGE_SIZE;
|
|
LoggerContext->MinimumBuffers = (ULONG)KeNumberProcessors + DEFAULT_BUFFERS;
|
|
// 20 additional buffers for MaximumBuffers
|
|
LoggerContext->MaximumBuffers
|
|
= LoggerContext->MinimumBuffers + DEFAULT_BUFFERS + 20;
|
|
|
|
KeQuerySystemTime(&LoggerContext->StartTime);
|
|
|
|
KeInitializeSemaphore( &LoggerContext->LoggerSemaphore,
|
|
0,
|
|
SEMAPHORE_LIMIT );
|
|
|
|
KeInitializeSpinLock(&LoggerContext->BufferSpinLock);
|
|
|
|
return LoggerContext;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
WmipAllocateTraceBufferPool(
|
|
IN PWMI_LOGGER_CONTEXT LoggerContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine is used to set up the circular trace buffers
|
|
|
|
Arguments:
|
|
|
|
LoggerContext Context of the logger to own the buffers.
|
|
|
|
Returned Value:
|
|
|
|
STATUS_SUCCESS if the initialization is successful
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG NumberProcessors, SysMax_Buffers, SysMin_Buffers;
|
|
LONG i;
|
|
PWMI_BUFFER_HEADER Buffer;
|
|
ULONG AllocatedBuffers, NumberOfBuffers;
|
|
SIZE_T WmiMaximumPoolInBytes;
|
|
|
|
PAGED_CODE();
|
|
//
|
|
// Allocate the pointers the each buffer here by sharing the same page
|
|
// with LoggerContext context pointer
|
|
//
|
|
NumberProcessors = (ULONG) KeNumberProcessors;
|
|
|
|
// This does not keep track of the amount already used by other sessions
|
|
if (LoggerContext->LoggerMode & EVENT_TRACE_USE_PAGED_MEMORY) {
|
|
WmiMaximumPoolInBytes = MmSizeOfPagedPoolInBytes;
|
|
}
|
|
else {
|
|
WmiMaximumPoolInBytes = MmMaximumNonPagedPoolInBytes;
|
|
}
|
|
|
|
// Compute System limits for min and max
|
|
|
|
// This is the absolute maximum that ANYONE can use
|
|
SysMax_Buffers = (ULONG) (WmiMaximumPoolInBytes
|
|
/ TRACE_MAXIMUM_NP_POOL_USAGE
|
|
/ LoggerContext->BufferSize);
|
|
|
|
// This is the absolute minimum that ANYONE MUST have
|
|
SysMin_Buffers = NumberProcessors + DEFAULT_BUFFERS;
|
|
|
|
// Sanity check to ensure that we have at least the minimum available
|
|
if (SysMin_Buffers > SysMax_Buffers) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
|
|
// Cover the case if the caller did not specify any values
|
|
LoggerContext->MaximumBuffers = max(LoggerContext->MaximumBuffers,
|
|
NumberProcessors + DEFAULT_BUFFERS +
|
|
DEFAULT_MAX_BUFFERS);
|
|
|
|
LoggerContext->MinimumBuffers = max(LoggerContext->MinimumBuffers,
|
|
SysMin_Buffers);
|
|
|
|
|
|
// Ensure each parameter is in range of SysMin and SysMax
|
|
|
|
LoggerContext->MaximumBuffers = max (LoggerContext->MaximumBuffers,
|
|
SysMin_Buffers);
|
|
LoggerContext->MaximumBuffers = min (LoggerContext->MaximumBuffers,
|
|
SysMax_Buffers);
|
|
|
|
LoggerContext->MinimumBuffers = max (LoggerContext->MinimumBuffers,
|
|
SysMin_Buffers);
|
|
LoggerContext->MinimumBuffers = min (LoggerContext->MinimumBuffers,
|
|
SysMax_Buffers);
|
|
|
|
// In case the MaximumBuffers and MinimumBuffers got reversed pick the
|
|
// larger value
|
|
|
|
if (LoggerContext->MinimumBuffers > LoggerContext->MaximumBuffers) {
|
|
LoggerContext->MaximumBuffers = LoggerContext->MinimumBuffers;
|
|
}
|
|
|
|
// NOTE: We do not return anything if we reset MaximumBuffers or MinimumBuffers
|
|
// provided by the caller.
|
|
|
|
LoggerContext->NumberOfBuffers = (LONG) LoggerContext->MinimumBuffers;
|
|
LoggerContext->BuffersAvailable = LoggerContext->NumberOfBuffers;
|
|
|
|
#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
|
|
|
|
//
|
|
// Allocate the buffers now
|
|
//
|
|
//
|
|
// Now determine the initial number of buffers
|
|
//
|
|
NumberOfBuffers = LoggerContext->NumberOfBuffers;
|
|
LoggerContext->NumberOfBuffers = 0;
|
|
LoggerContext->BuffersAvailable = 0;
|
|
|
|
AllocatedBuffers = WmipAllocateFreeBuffers(LoggerContext,
|
|
NumberOfBuffers);
|
|
|
|
if (AllocatedBuffers < NumberOfBuffers) {
|
|
//
|
|
// No enough buffer is allocated.
|
|
//
|
|
WmipFreeTraceBufferPool(LoggerContext);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Allocate Per Processor Buffer pointers
|
|
//
|
|
|
|
LoggerContext->ProcessorBuffers
|
|
= (PWMI_BUFFER_HEADER *)
|
|
WmipExtendBase(LoggerContext,
|
|
sizeof(PWMI_BUFFER_HEADER)*NumberProcessors);
|
|
|
|
|
|
if (LoggerContext->ProcessorBuffers == NULL) {
|
|
WmipFreeTraceBufferPool(LoggerContext);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// NOTE: We already know that we have allocated > number of processors
|
|
// buffers
|
|
//
|
|
for (i=0; i<(LONG)NumberProcessors; i++) {
|
|
Buffer = (PWMI_BUFFER_HEADER) WmipGetFreeBuffer(LoggerContext);
|
|
LoggerContext->ProcessorBuffers[i] = Buffer;
|
|
Buffer->ClientContext.ProcessorNumber = (UCHAR)i;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
WmipFreeTraceBufferPool(
|
|
IN PWMI_LOGGER_CONTEXT LoggerContext
|
|
)
|
|
{
|
|
ULONG i;
|
|
PSLIST_ENTRY Entry;
|
|
PWMI_BUFFER_HEADER* ProcessorBuffers;
|
|
PWMI_BUFFER_HEADER Buffer;
|
|
|
|
PAGED_CODE();
|
|
|
|
TraceDebug((2, "Free Buffer Pool: %2d, Free: %d, InUse: %d, Dirty: %d, Total: %d\n",
|
|
LoggerContext->LoggerId,
|
|
LoggerContext->BuffersAvailable,
|
|
LoggerContext->BuffersInUse,
|
|
LoggerContext->BuffersDirty,
|
|
LoggerContext->NumberOfBuffers));
|
|
|
|
while (Entry = InterlockedPopEntrySList(&LoggerContext->FreeList)) {
|
|
|
|
Buffer = CONTAINING_RECORD(Entry,
|
|
WMI_BUFFER_HEADER,
|
|
SlistEntry);
|
|
|
|
InterlockedDecrement(&LoggerContext->NumberOfBuffers);
|
|
InterlockedDecrement(&LoggerContext->BuffersAvailable);
|
|
|
|
TraceDebug((2, "WmipFreeTraceBufferPool (Free): %2d, %p, Free: %d, InUse: %d, Dirty: %d, Total: %d\n",
|
|
LoggerContext->LoggerId,
|
|
Buffer,
|
|
LoggerContext->BuffersAvailable,
|
|
LoggerContext->BuffersInUse,
|
|
LoggerContext->BuffersDirty,
|
|
LoggerContext->NumberOfBuffers));
|
|
|
|
WMI_FREE_TRACE_BUFFER(Buffer);
|
|
}
|
|
|
|
while (Entry = InterlockedPopEntrySList(&LoggerContext->FlushList)) {
|
|
|
|
Buffer = CONTAINING_RECORD(Entry,
|
|
WMI_BUFFER_HEADER,
|
|
SlistEntry);
|
|
|
|
InterlockedDecrement(&LoggerContext->NumberOfBuffers);
|
|
InterlockedDecrement(&LoggerContext->BuffersDirty);
|
|
|
|
TraceDebug((2, "WmipFreeTraceBufferPool (Flush): %2d, %p, Free: %d, InUse: %d, Dirty: %d, Total: %d\n",
|
|
LoggerContext->LoggerId,
|
|
Buffer,
|
|
LoggerContext->BuffersAvailable,
|
|
LoggerContext->BuffersInUse,
|
|
LoggerContext->BuffersDirty,
|
|
LoggerContext->NumberOfBuffers));
|
|
|
|
WMI_FREE_TRACE_BUFFER(Buffer);
|
|
}
|
|
|
|
while (Entry = InterlockedPopEntrySList(&LoggerContext->WaitList)) {
|
|
|
|
Buffer = CONTAINING_RECORD(Entry,
|
|
WMI_BUFFER_HEADER,
|
|
SlistEntry);
|
|
|
|
InterlockedDecrement(&LoggerContext->NumberOfBuffers);
|
|
InterlockedDecrement(&LoggerContext->BuffersDirty);
|
|
|
|
TraceDebug((2, "WmipFreeTraceBufferPool (Wait): %2d, %p, Free: %d, InUse: %d, Dirty: %d, Total: %d\n",
|
|
LoggerContext->LoggerId,
|
|
Buffer,
|
|
LoggerContext->BuffersAvailable,
|
|
LoggerContext->BuffersInUse,
|
|
LoggerContext->BuffersDirty,
|
|
LoggerContext->NumberOfBuffers));
|
|
|
|
WMI_FREE_TRACE_BUFFER(Buffer);
|
|
}
|
|
|
|
ProcessorBuffers = LoggerContext->ProcessorBuffers;
|
|
if (ProcessorBuffers != NULL) {
|
|
for (i=0; i<(ULONG)KeNumberProcessors; i++) {
|
|
Buffer = InterlockedExchangePointer(&ProcessorBuffers[i], NULL);
|
|
if (Buffer) {
|
|
InterlockedDecrement(&LoggerContext->NumberOfBuffers);
|
|
InterlockedDecrement(&LoggerContext->BuffersInUse);
|
|
|
|
TraceDebug((2, "WmipFreeTraceBufferPool (CPU %2d): %2d, %p, Free: %d, InUse: %d, Dirty: %d, Total: %d\n",
|
|
i,
|
|
LoggerContext->LoggerId,
|
|
Buffer,
|
|
LoggerContext->BuffersAvailable,
|
|
LoggerContext->BuffersInUse,
|
|
LoggerContext->BuffersDirty,
|
|
LoggerContext->NumberOfBuffers));
|
|
|
|
WMI_FREE_TRACE_BUFFER(Buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT(LoggerContext->BuffersAvailable == 0);
|
|
ASSERT(LoggerContext->BuffersInUse == 0);
|
|
ASSERT(LoggerContext->BuffersDirty == 0);
|
|
ASSERT(LoggerContext->NumberOfBuffers == 0);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
WmipLookupLoggerIdByName(
|
|
IN PUNICODE_STRING Name,
|
|
OUT PULONG LoggerId
|
|
)
|
|
{
|
|
ULONG i;
|
|
PWMI_LOGGER_CONTEXT *ContextTable;
|
|
|
|
PAGED_CODE();
|
|
if (Name == NULL) {
|
|
*LoggerId = (ULONG) -1;
|
|
return STATUS_WMI_INSTANCE_NOT_FOUND;
|
|
}
|
|
ContextTable = (PWMI_LOGGER_CONTEXT *) &WmipLoggerContext[0];
|
|
for (i=0; i<MAXLOGGERS; i++) {
|
|
if (ContextTable[i] == NULL ||
|
|
ContextTable[i] == (PWMI_LOGGER_CONTEXT) ContextTable)
|
|
continue;
|
|
if (RtlEqualUnicodeString(&ContextTable[i]->LoggerName, Name, TRUE) ) {
|
|
*LoggerId = i;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
*LoggerId = (ULONG) -1;
|
|
return STATUS_WMI_INSTANCE_NOT_FOUND;
|
|
}
|
|
|
|
NTSTATUS
|
|
WmipShutdown(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
//
|
|
// Shutdown all loggers cleanly. If a logger is in transition, it may
|
|
// not be stopped properly.
|
|
//
|
|
{
|
|
ULONG LoggerCount;
|
|
USHORT i;
|
|
PWMI_LOGGER_CONTEXT LoggerContext;
|
|
WMI_LOGGER_INFORMATION LoggerInfo;
|
|
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
UNREFERENCED_PARAMETER(Irp);
|
|
|
|
PAGED_CODE();
|
|
|
|
TraceDebug((2, "WmipShutdown called\n"));
|
|
if (WmipLoggerCount > 0) {
|
|
RtlZeroMemory(&LoggerInfo, sizeof(LoggerInfo));
|
|
LoggerInfo.Wnode.BufferSize = sizeof(LoggerInfo);
|
|
LoggerInfo.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
|
|
|
|
LoggerCount = 0;
|
|
for (i=0; i<MAXLOGGERS; i++) {
|
|
LoggerContext = WmipLoggerContext[i];
|
|
if ((LoggerContext != NULL) &&
|
|
(LoggerContext != (PWMI_LOGGER_CONTEXT)&WmipLoggerContext[0])) {
|
|
WmiSetLoggerId(i, &LoggerInfo.Wnode.HistoricalContext);
|
|
LoggerInfo.Wnode.Guid = LoggerContext->InstanceGuid;
|
|
WmiStopTrace(&LoggerInfo);
|
|
if (++LoggerCount == WmipLoggerCount)
|
|
break;
|
|
}
|
|
#if DBG
|
|
else if (LoggerContext
|
|
== (PWMI_LOGGER_CONTEXT)&WmipLoggerContext[0]) {
|
|
TraceDebug((4, "WmipShutdown: Logger %d in transition\n", i));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
WmipFlushLogger(
|
|
IN OUT PWMI_LOGGER_CONTEXT LoggerContext,
|
|
IN ULONG Wait
|
|
)
|
|
{
|
|
LARGE_INTEGER TimeOut = {(ULONG)(-20 * 1000 * 1000 * 10), -1};
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
KeResetEvent(&LoggerContext->FlushEvent);
|
|
|
|
LoggerContext->RequestFlag |= REQUEST_FLAG_FLUSH_BUFFERS;
|
|
Status = WmipNotifyLogger(LoggerContext);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
if (Wait) {
|
|
Status = KeWaitForSingleObject(
|
|
&LoggerContext->FlushEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
& TimeOut
|
|
);
|
|
#if DBG
|
|
if (Status == STATUS_TIMEOUT) {
|
|
TraceDebug((1, "WmiFlushLogger: Wait status=%X\n",Status));
|
|
}
|
|
#endif
|
|
KeResetEvent(&LoggerContext->FlushEvent);
|
|
Status = LoggerContext->LoggerStatus;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
WmipNotifyLogger(
|
|
IN PWMI_LOGGER_CONTEXT LoggerContext
|
|
)
|
|
// Routine can be called at DISPATCH_LEVEL
|
|
{
|
|
LONG SemCount = KeReadStateSemaphore(&LoggerContext->LoggerSemaphore);
|
|
if (SemCount >= SEMAPHORE_LIMIT/2) {
|
|
return STATUS_SEMAPHORE_LIMIT_EXCEEDED;
|
|
}
|
|
{
|
|
KeReleaseSemaphore(&LoggerContext->LoggerSemaphore, 0, 1, FALSE);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Note: Sep. 6th, 2001
|
|
We do not need the following two functions after removing GuidMaps from the kernel.
|
|
However, we feel that these two may provide useful code samples in case we need to
|
|
do RunDown. Hence we're keeping the codes in comments.
|
|
|
|
PVOID
|
|
WmipGetTraceBuffer(
|
|
IN PWMI_LOGGER_CONTEXT LoggerContext,
|
|
IN HANDLE LogFileHandle,
|
|
IN PWMI_BUFFER_HEADER Buffer,
|
|
IN ULONG GroupType,
|
|
IN ULONG RequiredSize,
|
|
OUT PULONG GuidMapBuffers
|
|
)
|
|
{
|
|
PSYSTEM_TRACE_HEADER Header;
|
|
NTSTATUS Status;
|
|
ULONG BytesUsed;
|
|
PETHREAD Thread;
|
|
|
|
PAGED_CODE();
|
|
|
|
RequiredSize += sizeof (SYSTEM_TRACE_HEADER); // add in header
|
|
|
|
RequiredSize = (ULONG) ALIGN_TO_POWER2(RequiredSize, WmiTraceAlignment);
|
|
|
|
if (RequiredSize > LoggerContext->BufferSize - sizeof(WMI_BUFFER_HEADER)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (RequiredSize > (LoggerContext->BufferSize - Buffer->Offset)) {
|
|
IO_STATUS_BLOCK IoStatus;
|
|
|
|
if (Buffer->Offset < LoggerContext->BufferSize) {
|
|
RtlFillMemory(
|
|
(char *) Buffer + Buffer->Offset,
|
|
LoggerContext->BufferSize - Buffer->Offset,
|
|
0xFF);
|
|
}
|
|
|
|
Status = ZwWriteFile(
|
|
LogFileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatus,
|
|
Buffer,
|
|
LoggerContext->BufferSize,
|
|
&LoggerContext->ByteOffset,
|
|
NULL);
|
|
Buffer->Offset = sizeof(WMI_BUFFER_HEADER);
|
|
LoggerContext->ByteOffset.QuadPart += LoggerContext->BufferSize;
|
|
if (!NT_SUCCESS(Status)) {
|
|
return NULL;
|
|
}
|
|
*GuidMapBuffers++;
|
|
}
|
|
|
|
Header = (PSYSTEM_TRACE_HEADER) ((char*)Buffer + Buffer->Offset);
|
|
Header->Header = (GroupType << 16) + RequiredSize;
|
|
Header->Marker = SYSTEM_TRACE_MARKER;
|
|
|
|
|
|
Thread = PsGetCurrentThread();
|
|
|
|
Header->SystemTime.QuadPart = (*LoggerContext->GetCpuClock)();
|
|
|
|
|
|
Header->ThreadId = HandleToUlong(Thread->Cid.UniqueThread);
|
|
Header->ProcessId = HandleToUlong(Thread->Cid.UniqueProcess);
|
|
Header->KernelTime = Thread->Tcb.KernelTime;
|
|
Header->UserTime = Thread->Tcb.UserTime;
|
|
Header->Packet.Size = (USHORT) RequiredSize;
|
|
|
|
|
|
Buffer->Offset += RequiredSize;
|
|
// If there is room, throw in a end of buffer marker.
|
|
|
|
BytesUsed = Buffer->Offset;
|
|
if ( BytesUsed <= (LoggerContext->BufferSize-sizeof(ULONG)) ) {
|
|
*((long*)((char*)Buffer+Buffer->Offset)) = -1;
|
|
}
|
|
return (PVOID) ( (char*) Header + sizeof(SYSTEM_TRACE_HEADER) );
|
|
}
|
|
|
|
|
|
ULONG
|
|
WmipDumpGuidMaps(
|
|
IN PWMI_LOGGER_CONTEXT LoggerContext,
|
|
IN PLIST_ENTRY TraceGMHeadPtr
|
|
)
|
|
{
|
|
PWMI_BUFFER_HEADER Buffer;
|
|
HANDLE LogFileHandle = NULL;
|
|
PWCHAR LogFileName = NULL;
|
|
NTSTATUS Status;
|
|
ULONG BufferSize;
|
|
ULONG GuidMapBuffers = 0;
|
|
PGUIDMAPENTRY GuidMap;
|
|
PLIST_ENTRY GuidMapList;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
if ( (LoggerContext == NULL) || (TraceGMHeadPtr == NULL) )
|
|
return 0;
|
|
|
|
|
|
//
|
|
// If this a realtime logger only, then simply free the GuidMaps.
|
|
//
|
|
|
|
if ( (LoggerContext->LoggerMode & EVENT_TRACE_REAL_TIME_MODE) &&
|
|
((LoggerContext->LogFileName.Buffer == NULL) ||
|
|
(LoggerContext->LogFileName.Length == 0)) ){
|
|
|
|
GuidMapList = TraceGMHeadPtr->Flink;
|
|
while (GuidMapList != TraceGMHeadPtr)
|
|
{
|
|
GuidMap = CONTAINING_RECORD(GuidMapList,
|
|
GUIDMAPENTRY,
|
|
Entry);
|
|
|
|
GuidMapList = GuidMapList->Flink;
|
|
|
|
RemoveEntryList(&GuidMap->Entry);
|
|
WmipFree(GuidMap);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
BufferSize = LoggerContext->BufferSize;
|
|
|
|
if ( BufferSize == 0)
|
|
return 0;
|
|
|
|
Buffer = ExAllocatePoolWithTag(PagedPool,
|
|
BufferSize, TRACEPOOLTAG);
|
|
if (Buffer == NULL) {
|
|
|
|
//
|
|
// No buffer available.
|
|
//
|
|
return 0;
|
|
}
|
|
|
|
RtlZeroMemory(Buffer, BufferSize);
|
|
|
|
Buffer->CurrentOffset = sizeof(WMI_BUFFER_HEADER);
|
|
Buffer->Offset = sizeof(WMI_BUFFER_HEADER);
|
|
Buffer->Wnode.BufferSize = BufferSize;
|
|
Buffer->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
|
|
Buffer->ClientContext.Alignment = (UCHAR)WmiTraceAlignment;
|
|
Buffer->Wnode.Guid = LoggerContext->InstanceGuid;
|
|
|
|
KeQuerySystemTime(&Buffer->TimeStamp);
|
|
|
|
Status = WmipCreateNtFileName( LoggerContext->LogFileName.Buffer,
|
|
&LogFileName);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ExFreePool(Buffer);
|
|
return 0;
|
|
}
|
|
|
|
Status = WmipCreateDirectoryFile (
|
|
LogFileName,
|
|
FALSE,
|
|
&LogFileHandle,
|
|
TRUE );
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
PULONG AuxInfo;
|
|
if ((LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_PREALLOCATE) &&
|
|
(LoggerContext->MaximumFileSize > (((LONGLONG) LoggerContext->BuffersWritten * (LONGLONG) LoggerContext->BufferSize) / (1024 * 1024)))) {
|
|
LoggerContext->ByteOffset.QuadPart = ((LONGLONG) LoggerContext->BufferSize) *
|
|
((LONGLONG) LoggerContext->BuffersWritten);
|
|
}
|
|
else {
|
|
FILE_STANDARD_INFORMATION FileSize;
|
|
|
|
Status = ZwQueryInformationFile(
|
|
LogFileHandle,
|
|
&IoStatus,
|
|
&FileSize,
|
|
sizeof (FILE_STANDARD_INFORMATION),
|
|
FileStandardInformation
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ZwClose(LogFileHandle);
|
|
ExFreePool(LogFileName);
|
|
ExFreePool(Buffer);
|
|
return 0;
|
|
}
|
|
|
|
LoggerContext->ByteOffset = FileSize.EndOfFile;
|
|
}
|
|
//
|
|
// Do the RunDown of GuidMaps
|
|
//
|
|
|
|
GuidMapList = TraceGMHeadPtr->Flink;
|
|
while (GuidMapList != TraceGMHeadPtr)
|
|
{
|
|
GuidMap = CONTAINING_RECORD(GuidMapList,
|
|
GUIDMAPENTRY,
|
|
Entry);
|
|
|
|
GuidMapList = GuidMapList->Flink;
|
|
|
|
RemoveEntryList(&GuidMap->Entry);
|
|
|
|
AuxInfo = (PULONG) WmipGetTraceBuffer(LoggerContext,
|
|
LogFileHandle,
|
|
Buffer,
|
|
EVENT_TRACE_GROUP_HEADER + EVENT_TRACE_TYPE_GUIDMAP,
|
|
sizeof(TRACEGUIDMAP),
|
|
&GuidMapBuffers
|
|
);
|
|
|
|
if (AuxInfo != NULL) {
|
|
RtlCopyMemory(AuxInfo, &GuidMap->GuidMap, sizeof(TRACEGUIDMAP) );
|
|
}
|
|
|
|
WmipFree(GuidMap);
|
|
}
|
|
|
|
//
|
|
// Flush the last buffer if needed
|
|
//
|
|
|
|
if (Buffer->Offset > sizeof(WMI_BUFFER_HEADER) ) {
|
|
Status = ZwWriteFile(
|
|
LogFileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatus,
|
|
Buffer,
|
|
LoggerContext->BufferSize,
|
|
&LoggerContext->ByteOffset,
|
|
NULL);
|
|
LoggerContext->ByteOffset.QuadPart += LoggerContext->BufferSize;
|
|
GuidMapBuffers++;
|
|
|
|
}
|
|
|
|
ZwClose(LogFileHandle);
|
|
}
|
|
|
|
ExFreePool(LogFileName);
|
|
ExFreePool(Buffer);
|
|
|
|
return GuidMapBuffers;
|
|
}
|
|
*/
|
|
|
|
NTSTATUS
|
|
WmipNtDllLoggerInfo(
|
|
IN OUT PWMINTDLLLOGGERINFO Buffer
|
|
)
|
|
{
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
KPROCESSOR_MODE RequestorMode;
|
|
PBGUIDENTRY GuidEntry;
|
|
ULONG SizeNeeded;
|
|
GUID Guid;
|
|
ACCESS_MASK DesiredAccess = TRACELOG_GUID_ENABLE;
|
|
|
|
PAGED_CODE();
|
|
|
|
RequestorMode = KeGetPreviousMode();
|
|
|
|
SizeNeeded = sizeof(WMI_LOGGER_INFORMATION);
|
|
|
|
__try {
|
|
|
|
if (RequestorMode != KernelMode){
|
|
ProbeForRead(Buffer->LoggerInfo, SizeNeeded, sizeof(ULONGLONG));
|
|
}
|
|
|
|
RtlCopyMemory(&Guid, &Buffer->LoggerInfo->Wnode.Guid, sizeof(GUID));
|
|
|
|
if(!IsEqualGUID(&Guid, &NtdllTraceGuid)){
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
|
|
SizeNeeded = Buffer->LoggerInfo->Wnode.BufferSize;
|
|
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
WmipEnterTLCritSection();
|
|
WmipEnterSMCritSection();
|
|
|
|
GuidEntry = WmipFindGEByGuid(&Guid, FALSE);
|
|
|
|
if(Buffer->IsGet){
|
|
|
|
if( GuidEntry ){
|
|
|
|
if(GuidEntry->LoggerInfo){
|
|
|
|
SizeNeeded = GuidEntry->LoggerInfo->Wnode.BufferSize;
|
|
|
|
__try {
|
|
|
|
if (RequestorMode != KernelMode){
|
|
ProbeForWrite(Buffer->LoggerInfo, SizeNeeded, sizeof(ULONGLONG));
|
|
}
|
|
|
|
RtlCopyMemory(Buffer->LoggerInfo,GuidEntry->LoggerInfo,SizeNeeded);
|
|
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
WmipUnreferenceGE(GuidEntry);
|
|
WmipLeaveSMCritSection();
|
|
WmipLeaveTLCritSection();
|
|
return GetExceptionCode();
|
|
}
|
|
}
|
|
|
|
WmipUnreferenceGE(GuidEntry);
|
|
|
|
} else {
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// This must be a control operation.
|
|
// Check to see if heap/critsec controller has access
|
|
// to proper Guids.
|
|
//
|
|
Status = WmipCheckGuidAccess(
|
|
&Guid,
|
|
DesiredAccess,
|
|
EtwpDefaultTraceSecurityDescriptor
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
if( GuidEntry ){
|
|
WmipUnreferenceGE(GuidEntry);
|
|
}
|
|
WmipLeaveSMCritSection();
|
|
WmipLeaveTLCritSection();
|
|
return Status;
|
|
}
|
|
|
|
if(SizeNeeded){
|
|
|
|
if(GuidEntry == NULL){
|
|
|
|
GuidEntry = WmipAllocGuidEntry();
|
|
|
|
if (GuidEntry){
|
|
|
|
//
|
|
// Initialize the guid entry and keep the ref count
|
|
// from creation. When tracelog enables we take a ref
|
|
// count and when it disables we release it
|
|
//
|
|
GuidEntry->Guid = Guid;
|
|
GuidEntry->EventRefCount = 1;
|
|
GuidEntry->Flags |= GE_NOTIFICATION_TRACE_FLAG;
|
|
InsertHeadList(WmipGEHeadPtr, &GuidEntry->MainGEList);
|
|
|
|
//
|
|
// Take Extra Refcount so that we release it at stoplogger call
|
|
//
|
|
|
|
WmipReferenceGE(GuidEntry);
|
|
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
if(NT_SUCCESS(Status)){
|
|
|
|
if(GuidEntry->LoggerInfo) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
} else {
|
|
|
|
GuidEntry->LoggerInfo = WmipAlloc(SizeNeeded);
|
|
|
|
if(GuidEntry->LoggerInfo){
|
|
|
|
WMITRACEENABLEDISABLEINFO TraceEnableInfo;
|
|
PTRACE_ENABLE_CONTEXT pContext;
|
|
|
|
__try {
|
|
|
|
pContext = (PTRACE_ENABLE_CONTEXT)&Buffer->LoggerInfo->Wnode.HistoricalContext;
|
|
|
|
pContext->InternalFlag |= EVENT_TRACE_INTERNAL_FLAG_PRIVATE;
|
|
pContext->LoggerId = 1;
|
|
|
|
RtlCopyMemory(GuidEntry->LoggerInfo,Buffer->LoggerInfo,SizeNeeded);
|
|
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
WmipUnreferenceGE(GuidEntry);
|
|
WmipLeaveSMCritSection();
|
|
WmipLeaveTLCritSection();
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
TraceEnableInfo.Guid = GuidEntry->Guid;
|
|
TraceEnableInfo.Enable = TRUE;
|
|
TraceEnableInfo.LoggerContext = 0;
|
|
Status = WmipEnableDisableTrace(IOCTL_WMI_ENABLE_DISABLE_TRACELOG, &TraceEnableInfo);
|
|
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
WmipUnreferenceGE(GuidEntry);
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// This is stop logger call.
|
|
//
|
|
|
|
if(GuidEntry){
|
|
|
|
WMITRACEENABLEDISABLEINFO TraceEnableInfo;
|
|
|
|
if(GuidEntry->LoggerInfo) {
|
|
|
|
__try{
|
|
|
|
if (RequestorMode != KernelMode){
|
|
ProbeForWrite(Buffer->LoggerInfo, sizeof(WMI_LOGGER_INFORMATION), sizeof(ULONGLONG));
|
|
}
|
|
|
|
Buffer->LoggerInfo->BufferSize = GuidEntry->LoggerInfo->BufferSize;
|
|
Buffer->LoggerInfo->MinimumBuffers = GuidEntry->LoggerInfo->MinimumBuffers;
|
|
Buffer->LoggerInfo->MaximumBuffers = GuidEntry->LoggerInfo->MaximumBuffers;
|
|
|
|
WmipFree(GuidEntry->LoggerInfo);
|
|
GuidEntry->LoggerInfo = NULL;
|
|
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
WmipUnreferenceGE(GuidEntry);
|
|
WmipLeaveSMCritSection();
|
|
WmipLeaveTLCritSection();
|
|
return GetExceptionCode();
|
|
}
|
|
}
|
|
|
|
TraceEnableInfo.Guid = GuidEntry->Guid;
|
|
TraceEnableInfo.Enable = FALSE;
|
|
TraceEnableInfo.LoggerContext = 0;
|
|
|
|
//
|
|
// The Extra Refcount taken at logger start is released by calling
|
|
// Disable trace.
|
|
//
|
|
|
|
Status = WmipEnableDisableTrace(IOCTL_WMI_ENABLE_DISABLE_TRACELOG, &TraceEnableInfo);
|
|
WmipUnreferenceGE(GuidEntry);
|
|
}
|
|
}
|
|
}
|
|
|
|
WmipLeaveSMCritSection();
|
|
WmipLeaveTLCritSection();
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
WmipValidateClockType(
|
|
IN OUT PWMI_LOGGER_INFORMATION LoggerInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to validate the requested clock type in the
|
|
LoggerInfo. If the requested type can not be handled, we will override
|
|
to a type that this system will support.
|
|
|
|
This routine assumes that LoggerInfo pointer is a valid one.
|
|
|
|
Arguments:
|
|
|
|
LoggerInfo - a pointer to the structure for the logger's control
|
|
and status information
|
|
|
|
Returned Value:
|
|
|
|
Status of STATUS_SUCCESS
|
|
|
|
--*/
|
|
{
|
|
#ifdef NTPERF
|
|
//
|
|
// For private kernel, use EVENT_TRACE_CLOCK_CPUCYCLE no matter
|
|
// what the user sets
|
|
// This mechanism need to considered again
|
|
//
|
|
LoggerInfo->Wnode.ClientContext = EVENT_TRACE_CLOCK_CPUCYCLE;
|
|
#else
|
|
//
|
|
// For retail kernel, if not EVENT_TRACE_CLOCK_SYSTEMTIME,
|
|
// force it to be EVENT_TRACE_CLOCK_PERFCOUNTER.
|
|
//
|
|
if (LoggerInfo->Wnode.ClientContext != EVENT_TRACE_CLOCK_SYSTEMTIME) {
|
|
LoggerInfo->Wnode.ClientContext = EVENT_TRACE_CLOCK_PERFCOUNTER;
|
|
}
|
|
#endif //NTPERF
|
|
|
|
}
|
|
|