/*++ 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 #include #include #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; iLoggerName, 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; iInstanceGuid; 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 }