/*++ 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 #include #include "wmikmp.h" #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 ULONG WmipKernelLogger = KERNEL_LOGGER; ULONG WmipEventLogger = 0XFFFFFFFF; FAST_MUTEX WmipTraceFastMutex; #ifdef ALLOC_DATA_PRAGMA #pragma data_seg("PAGEDATA") #endif ULONG WmipLoggerCount = 0; HANDLE WmipPageLockHandle = NULL; #ifdef ALLOC_DATA_PRAGMA #pragma data_seg() #endif extern SIZE_T MmMaximumNonPagedPoolInBytes; #pragma warning( default: 4035 ) #pragma warning( default: 4127 ) 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) #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}; LARGE_INTEGER OneSecond = {(ULONG)(-1 * 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; PERFINFO_GROUPMASK *PerfGroupMasks=NULL; BOOLEAN IsGlobalForKernel = 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)) return STATUS_INVALID_PARAMETER; } RequestorMode = KeGetPreviousMode(); if (LoggerInfo->LoggerName.Length > 0) { LoggerName.Buffer = NULL; try { if (RequestorMode != KernelMode) { ProbeForRead( LoggerInfo->LoggerName.Buffer, LoggerInfo->LoggerName.Length, sizeof (UCHAR) ); } if (! RtlCreateUnicodeString( &LoggerName, LoggerInfo->LoggerName.Buffer) ) { Status = 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? // RtlZeroMemory(&InstanceGuid, sizeof(GUID)); if (IsEqualGUID(&LoggerInfo->Wnode.Guid, &InstanceGuid)) { InstanceGuid = EventTraceGuid; } else { InstanceGuid = LoggerInfo->Wnode.Guid; } ContextTable = (PWMI_LOGGER_CONTEXT *) &WmipLoggerContext[0]; EnableKernel = IsEqualGUID(&InstanceGuid, &SystemTraceControlGuid); if (EnableKernel) { // // 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 EventTraceGuid // Status = WmipCheckGuidAccess( (LPGUID) &EventTraceGuid, DesiredAccess ); if (NT_SUCCESS(Status) && (!IsEqualGUID(&InstanceGuid, &EventTraceGuid))) { // // next, check to see if more access required // Status = WmipCheckGuidAccess( (LPGUID) &InstanceGuid, DesiredAccess ); } 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)) { ObDereferenceObject( FileObject ); 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; if (LoggerContext->LoggerMode & EVENT_TRACE_FILE_MODE_CIRCULAR) { LoggerContext->RequestFlag |= REQUEST_FLAG_CIRCULAR_PERSIST; } WmipValidateClockType(LoggerInfo); LoggerContext->UsePerfClock = LoggerInfo->Wnode.ClientContext; #ifdef WMI_NON_BLOCKING if (LoggerInfo->FlushTimer > 0) LoggerContext->FlushTimer = LoggerInfo->FlushTimer; #else if (LoggerInfo->FlushTimer > 0) LoggerContext->FlushTimer.QuadPart = LoggerInfo->FlushTimer * OneSecond.QuadPart; #endif //WMI_NON_BLOCKING if (LoggerInfo->AgeLimit >= 0) { // minimum is 15 minutes LoggerContext->BufferAgeLimit.QuadPart = max (DEFAULT_AGE_LIMIT, LoggerInfo->AgeLimit) * OneSecond.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) { 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) { LoggerContext->LogFilePattern = FileName; Status = WmipGenerateFileName( &LoggerContext->LogFilePattern, &LoggerContext->FileCounter, &LoggerContext->LogFileName); } else { LoggerContext->LogFileName = FileName; } 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); } ExFreePool(LoggerContext); } return(Status); } // // Now, allocate the buffer pool and associated buffers. // Note that buffer allocation routine will also set NumberOfBuffers and // MaximumBuffers. // #ifdef WMI_NON_BLOCKING KeInitializeSpinLock (&WmiSlistLock); InitializeSListHead (&LoggerContext->FreeList); InitializeSListHead (&LoggerContext->FlushList); InitializeSListHead (&LoggerContext->GlobalList); #else InitializeListHead(&LoggerContext->FreeList); InitializeListHead(&LoggerContext->FlushList); #endif //WMI_NON_BLOCKING #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); } 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 / OneSecond.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; } // // 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)) { ULONG i; ULONG NumberProcessors = (ULONG) KeNumberProcessors; PLIST_ENTRY pHead, pNext; PWMI_BUFFER_HEADER pBuffer; LoggerContext->LoggerMode = LogFileMode; LoggerContext->WriteFailureLimit = 100; #ifdef NTPERF if (EnableKernel) { WmiGetCpuClock = &PerfGetCycleCount; } LoggerContext->GetCpuClock = &PerfGetCycleCount; #else switch (LoggerContext->UsePerfClock) { case EVENT_TRACE_CLOCK_SYSTEMTIME: if (EnableKernel) { WmiGetCpuClock = &WmipGetSystemTime; } LoggerContext->GetCpuClock = &WmipGetSystemTime; break; case EVENT_TRACE_CLOCK_PERFCOUNTER: default : if (EnableKernel) { WmiGetCpuClock = &WmipGetPerfCounter; } LoggerContext->GetCpuClock = &WmipGetPerfCounter; break; } #endif //NTPERF #ifndef WMI_NON_BLOCKING pHead = &LoggerContext->FreeList; pNext = pHead->Flink; while (pNext != pHead) { pBuffer = (PWMI_BUFFER_HEADER) CONTAINING_RECORD(pNext, WMI_BUFFER_HEADER, Entry); pBuffer->UsePerfClock = LoggerContext->UsePerfClock; pNext = pNext->Flink; } #endif //WMI_NON_BLOCKING // // 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. // PerfTimeStamp(LoggerContext->ReferenceTimeStamp); KeQuerySystemTime(&LoggerContext->ReferenceSystemTime); // // Lock down the routines that need to be non-pageable // ExAcquireFastMutex(&WmipTraceFastMutex); if (++WmipLoggerCount == 1) { ASSERT(WmipPageLockHandle); MmLockPagableSectionByHandle(WmipPageLockHandle); WmipGlobalSequence = 0; } ExReleaseFastMutex(&WmipTraceFastMutex); // // 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) { Status = PerfInfoStartLog(PerfGroupMasks, PERFINFO_START_LOG_FROM_GLOBAL_LOGGER); } 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; LARGE_INTEGER OneSecond = {(ULONG)(-1 * 1000 * 1000 * 10), -1}; 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( (LPGUID) &EventTraceGuid, DesiredAccess ); 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; } if (!IsEqualGUID(&LoggerContext->InstanceGuid, &EventTraceGuid)) { Status = WmipCheckGuidAccess( (LPGUID) &LoggerContext->InstanceGuid, DesiredAccess ); if (!NT_SUCCESS(Status)) { #ifndef WMI_MUTEX_FREE InterlockedDecrement(&LoggerContext->MutexCount); TraceDebug((1, "WmipQueryLogger: Release mutex2 %d %d\n", LoggerId, LoggerContext->MutexCount)); WmipReleaseMutex(&LoggerContext->LoggerMutex); #endif #if DBG RefCount = #endif WmipDereferenceLogger(LoggerId); TraceDebug((1, "WmipQueryLogger: Status2=%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; #ifdef WMI_NON_BLOCKING LoggerInfo->FlushTimer = LoggerContext->FlushTimer; #else LoggerInfo->FlushTimer = (ULONG) (LoggerContext->FlushTimer.QuadPart / OneSecond.QuadPart); #endif //WMI_NON_BLOCKING 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->LogBuffersLost = LoggerContext->LogBuffersLost; LoggerInfo->RealTimeBuffersLost = LoggerContext->RealTimeBuffersLost; LoggerInfo->AgeLimit = (ULONG) (LoggerContext->BufferAgeLimit.QuadPart / OneSecond.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) { #if DBG RefCount = #endif WmipDereferenceLogger(LoggerId); TraceDebug((1, "WmipQueryLogger: %d %d->%d\n", LoggerId, RefCount+1, RefCount)); #ifndef WMI_MUTEX_FREE InterlockedDecrement(&LoggerContext->MutexCount); TraceDebug((1, "WmipQueryLogger: Release mutex %d %d\n", LoggerId, LoggerContext->MutexCount)); WmipReleaseMutex(&LoggerContext->LoggerMutex); #endif } 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); if (LoggerId < 1) 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); if (LoggerId < 1) 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 // Size = ALIGN_TO_POWER2(Size, DEFAULT_TRACE_ALIGNMENT); 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; ULONG i; LONG RefCount; LARGE_INTEGER Timeout = {(ULONG)(-50 * 1000 * 10), -1}; // 50 ms NTSTATUS Status = STATUS_TIMEOUT; LONG count = 0; 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 (Status == STATUS_TIMEOUT) { count ++; 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; /* // For temporary Debugging only if (WmipRefCount[LoggerId] == RefCount) { if (count > 495) { TraceDebug((0, "WmipFreeLoggerContext: %d Waiting for %d ref %d\n", count, LoggerId, RefCount)); } if (count >= 500) { // temporarily only to catch synch problems TraceDebug((0, "WmipFreeLoggerContext: Resetting %d...\n", LoggerId)); break; } Status = STATUS_TIMEOUT; // try again } #if DBG else if (count > 495) { TraceDebug((0, "WmipFreeLoggerContext: %d Logger %d ref %d\n", count, LoggerId, WmipRefCount[LoggerId])); } #endif */ RefCount = WmipRefCount[LoggerId]; } ExAcquireFastMutex(&WmipTraceFastMutex); if (--WmipLoggerCount == 0) { if (WmipPageLockHandle) { MmUnlockPagableImageSection(WmipPageLockHandle); } #if DBG else { ASSERT(WmipPageLockHandle); } #endif } ExReleaseFastMutex(&WmipTraceFastMutex); WmipFreeTraceBufferPool(LoggerContext); if (LoggerContext->LoggerName.Buffer != NULL) { RtlFreeUnicodeString(&LoggerContext->LoggerName); } if (LoggerContext->LogFileName.Buffer != NULL) { RtlFreeUnicodeString(&LoggerContext->LogFileName); } 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)); // DbgBreakPoint(); } #ifndef WMI_MUTEX_FREE if (LoggerContext->MutexCount >= 1) { TraceDebug((0, "****ERROR**** Mutex count is %d for %d\n", LoggerId, LoggerContext->MutexCount)); // DbgBreakPoint(); } #endif // if (WmipRefCount[LoggerId] >= 1) { // TraceDebug((1, "****ERROR**** Ref count for %d is %d\n", LoggerId, // WmipRefCount[LoggerId])); // DbgBreakPoint(); // } #endif 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; ULONG Min_Buffers; 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 BufferSize, NumberProcessors, Max_Buffers; LONG i; PWMI_BUFFER_HEADER Buffer; #ifdef WMI_NON_BLOCKING ULONG AllocatedBuffers, NumberOfBuffers; #endif //WMI_NON_BLOCKING PAGED_CODE(); // // Allocate the pointers the each buffer here by sharing the same page // with LoggerContext context pointer // NumberProcessors = (ULONG) KeNumberProcessors; Max_Buffers = (ULONG) (MmMaximumNonPagedPoolInBytes / TRACE_MAXIMUM_NP_POOL_USAGE / LoggerContext->BufferSize); if (LoggerContext->MaximumBuffers > Max_Buffers) { LoggerContext->MaximumBuffers = Max_Buffers; } else if (LoggerContext->MaximumBuffers < Max_Buffers) { Max_Buffers = max(LoggerContext->MaximumBuffers, NumberProcessors + DEFAULT_BUFFERS + 20); } LoggerContext->MinimumBuffers = max(LoggerContext->MinimumBuffers, NumberProcessors + DEFAULT_BUFFERS); LoggerContext->NumberOfBuffers = (LONG) LoggerContext->MinimumBuffers; if (LoggerContext->NumberOfBuffers > (LONG) Max_Buffers) { LoggerContext->NumberOfBuffers = (LONG) Max_Buffers; LoggerContext->MinimumBuffers = Max_Buffers; } LoggerContext->MaximumBuffers = Max_Buffers; 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 // #ifdef WMI_NON_BLOCKING // // 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 = (SLIST_HEADER *) WmipExtendBase(LoggerContext, sizeof(SLIST_HEADER)*NumberProcessors); #else BufferSize = LoggerContext->BufferSize; for (i=0; iNumberOfBuffers; i++) { Buffer = (PWMI_BUFFER_HEADER) ExAllocatePoolWithTag(LoggerContext->PoolType, BufferSize, TRACEPOOLTAG); if (Buffer == NULL) { // need to free previously allocated buffers WmipFreeTraceBufferPool(LoggerContext); return STATUS_NO_MEMORY; } // // Initialize newly created buffer // RtlZeroMemory(Buffer, sizeof(WMI_BUFFER_HEADER)); Buffer->CurrentOffset = sizeof(WMI_BUFFER_HEADER); KeQuerySystemTime(&Buffer->TimeStamp); InsertTailList( &LoggerContext->FreeList, &Buffer->Entry); TraceDebug((2, "WmipAllocateTraceBuffer: %d Allocated %X Entry %X\n", LoggerContext->LoggerId, Buffer, Buffer->Entry)); } // // Allocate Per Processor Buffer pointers // LoggerContext->ProcessorBuffers = (PWMI_BUFFER_HEADER *) WmipExtendBase(LoggerContext, sizeof(PWMI_BUFFER_HEADER)*NumberProcessors); #endif //WMI_NON_BLOCKING 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++) { #ifdef WMI_NON_BLOCKING InitializeSListHead (&LoggerContext->ProcessorBuffers[i]); Buffer = (PWMI_BUFFER_HEADER) WmipGetFreeBuffer(LoggerContext); InterlockedPushEntrySList (&LoggerContext->ProcessorBuffers[i], (PSINGLE_LIST_ENTRY) &Buffer->SlistEntry); Buffer->ClientContext.ProcessorNumber = (UCHAR)i; #else Buffer = (PWMI_BUFFER_HEADER) WmipGetFreeBuffer(LoggerContext); LoggerContext->ProcessorBuffers[i] = Buffer; Buffer->ClientContext.ProcessorNumber = (UCHAR)i; #endif //WMI_NON_BLOCKING } return STATUS_SUCCESS; } NTSTATUS WmipFreeTraceBufferPool( IN PWMI_LOGGER_CONTEXT LoggerContext ) { ULONG i; #ifdef WMI_NON_BLOCKING PSINGLE_LIST_ENTRY Entry; SLIST_HEADER* ProcessorBuffers; PWMI_BUFFER_HEADER Buffer; #else PWMI_BUFFER_HEADER* Buffers; PLIST_ENTRY Entry; #endif //WMI_NON_BLOCKING PAGED_CODE(); #ifdef WMI_NON_BLOCKING 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)); #ifdef NTPERF if (!PERFINFO_IS_LOGGING_TO_PERFMEM()) { #endif //NTPERF ExFreePool(Buffer); #ifdef NTPERF } #endif //NTPERF } 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)); #ifdef NTPERF if (!PERFINFO_IS_LOGGING_TO_PERFMEM()) { #endif //NTPERF ExFreePool(Buffer); #ifdef NTPERF } #endif //NTPERF } ProcessorBuffers = LoggerContext->ProcessorBuffers; if (ProcessorBuffers != NULL) { for (i=0; i<(ULONG)KeNumberProcessors; i++) { while (Entry = InterlockedPopEntrySList(&ProcessorBuffers[i])) { Buffer = CONTAINING_RECORD(Entry, WMI_BUFFER_HEADER, SlistEntry); 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)); #ifdef NTPERF if (!PERFINFO_IS_LOGGING_TO_PERFMEM()) { #endif //NTPERF ExFreePool(Buffer); #ifdef NTPERF } #endif //NTPERF } } } ASSERT(LoggerContext->BuffersAvailable == 0); ASSERT(LoggerContext->BuffersInUse == 0); ASSERT(LoggerContext->BuffersDirty == 0); ASSERT(LoggerContext->NumberOfBuffers == 0); #else while (Entry = ExInterlockedRemoveHeadList( &LoggerContext->FreeList, &LoggerContext->BufferSpinLock)) { PWMI_BUFFER_HEADER Buffer; Buffer = CONTAINING_RECORD(Entry, WMI_BUFFER_HEADER, Entry); TraceDebug((2, "WmipFreeTraceBufferPool: Freeing %d %X Entry %X\n", LoggerContext->LoggerId, Buffer, Entry)); if (Buffer != NULL) { ExFreePool(Buffer); } } for (i=0; i<(ULONG)LoggerContext->NumberOfBuffers; i++) { PLIST_ENTRY Entry; if (IsListEmpty(&LoggerContext->FlushList)) break; Entry = RemoveTailList( &LoggerContext->FlushList ); InsertTailList( &LoggerContext->FreeList, Entry ); TraceDebug((1, "WmipFreeTraceBufferPool: Move entry %X from flush to free\n", Entry)); } Buffers = LoggerContext->ProcessorBuffers; if (Buffers != NULL) { for (i=0; i<(ULONG)KeNumberProcessors; i++) { if (Buffers[i] != NULL) { TraceDebug((1, "WmipFreeTraceBufferPool: Freeing buffer %X for CPU%d\n", Buffers[i], i)); ExFreePool(Buffers[i]); Buffers[i] = NULL; } } } #endif //WMI_NON_BLOCKING 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(); // // To Protect against an earlier caller timing out // and resetting the event before it was set by the // logger thread. // 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_UNSUCCESSFUL; } { KeReleaseSemaphore(&LoggerContext->LoggerSemaphore, 0, 1, FALSE); return STATUS_SUCCESS; } } 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; 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 { 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 = NULL; GuidEntry->LoggerInfo = WmipAlloc(SizeNeeded); if(GuidEntry->LoggerInfo){ WMITRACEENABLEDISABLEINFO TraceEnableInfo; __try { RtlCopyMemory(GuidEntry->LoggerInfo,Buffer->LoggerInfo,SizeNeeded); } __except(EXCEPTION_EXECUTE_HANDLER) { WmipUnreferenceGE(GuidEntry); WmipLeaveSMCritSection(); WmipLeaveTLCritSection(); return GetExceptionCode(); } TraceEnableInfo.Guid = GuidEntry->Guid; TraceEnableInfo.Enable = TRUE; 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; // // 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 }