/*++ Copyright (c) 1992 Microsoft Corporation Module Name: query.c Abstract: This module contains the RtlQueryProcessInformation function Author: Steve Wood (stevewo) 01-Apr-1994 Revision History: --*/ #include "ldrp.h" #include #include #include #include #define AdjustPointer( t, p, d ) (p); if ((p) != NULL) (p) = (t)((ULONG_PTR)(p) + (d)) // // Define the offset from the real control data to the copy thats mapped into target processes. // We need to copies so the target process can't corrupt the information. // #define CONTROL_OFFSET (0x10000) #define CONTROL_TO_FAKE(Buffer) ((PRTL_DEBUG_INFORMATION)((PUCHAR)(Buffer) + CONTROL_OFFSET)) #define FAKE_TO_CONTROL(Buffer) ((PRTL_DEBUG_INFORMATION)((PUCHAR)(Buffer) - CONTROL_OFFSET)) NTSYSAPI NTSTATUS NTAPI RtlpQueryProcessDebugInformationRemote( IN OUT PRTL_DEBUG_INFORMATION Buffer ) /*++ Routine Description: This is the target routine for a remote query. It runds in the context of an injected thread. If event pairs are used then this code loops repeatedly as an optimization. Arguments: Buffer - Query buffer to fill out with the query results Return Value: NTSTATUS - Status of call --*/ { NTSTATUS Status, Status1; ULONG i; ULONG_PTR Delta; PRTL_PROCESS_HEAPS Heaps; PRTL_HEAP_INFORMATION HeapInfo; HANDLE EventPairTarget; EventPairTarget = Buffer->EventPairTarget; if (EventPairTarget != NULL) { Status = NtWaitLowEventPair (EventPairTarget); } else { Status = STATUS_SUCCESS; } while (NT_SUCCESS (Status)) { Status = RtlQueryProcessDebugInformation (NtCurrentTeb()->ClientId.UniqueProcess, Buffer->Flags, Buffer); if (NT_SUCCESS (Status)) { Delta = Buffer->ViewBaseDelta; if (Delta) { // // Need to relocate buffer pointers back to client addresses // AdjustPointer (PRTL_PROCESS_MODULES, Buffer->Modules, Delta); AdjustPointer (PRTL_PROCESS_BACKTRACES, Buffer->BackTraces, Delta); Heaps = AdjustPointer (PRTL_PROCESS_HEAPS, Buffer->Heaps, Delta); if (Heaps != NULL) { for (i=0; iNumberOfHeaps; i++) { HeapInfo = &Heaps->Heaps[ i ]; AdjustPointer (PRTL_HEAP_TAG, HeapInfo->Tags, Delta); AdjustPointer (PRTL_HEAP_ENTRY, HeapInfo->Entries, Delta); } } AdjustPointer (PRTL_PROCESS_LOCKS, Buffer->Locks, Delta); } } // // If we were supposed to be a one shot then exit now. // if (EventPairTarget == NULL) { // // If no event pair handle, then exit loop and terminate // break; } Status = NtSetHighWaitLowEventPair (EventPairTarget); // // The client side will clear this variable to signal we should exit // if (Buffer->EventPairTarget == NULL) { // // If no event pair handle, then exit loop and terminate // break; } } // // All done with buffer, remove from our address space // then terminate ourselves so client wakes up. // Buffer->ViewBaseTarget = NULL; Status1 = NtUnmapViewOfSection (NtCurrentProcess(), Buffer); ASSERT (NT_SUCCESS (Status1)); RtlExitUserThread (Status); // // NEVER REACHED. // } NTSTATUS RtlpChangeQueryDebugBufferTarget( IN PRTL_DEBUG_INFORMATION Buffer, IN HANDLE TargetProcessId, OUT PHANDLE ReturnedTargetProcessHandle OPTIONAL ) /*++ Routine Description: This routine changes the target process being queried. If the current target is set then we cleanup any state assocuated with this query. If we are using event pairs then we causes the cached thread to exit the target. Arguments: Buffer - Query buffer to be assigned and deassigned from the processes. TargetProcessId - New target to be queueried. If NULL then just clear current context ReturnedProcessHandle - OPTIONAL, If present receives the process handle of the new target if there is one. Return Value: NTSTATUS - Status of call --*/ { NTSTATUS Status, Status1; CLIENT_ID OldTargetClientId, NewTargetClientId; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE OldTargetProcess, NewTargetProcess, NewHandle; PRTL_DEBUG_INFORMATION TargetBuffer; LARGE_INTEGER SectionOffset; HANDLE EventPairTarget = NULL; TargetBuffer = CONTROL_TO_FAKE (Buffer); if (Buffer->EventPairClient != NULL && Buffer->TargetProcessId == TargetProcessId) { return STATUS_SUCCESS; } if (Buffer->TargetThreadHandle != NULL) { EventPairTarget = Buffer->EventPairTarget; TargetBuffer->EventPairTarget = NULL; Buffer->EventPairTarget = NULL; if (Buffer->EventPairClient != NULL) { Status = NtSetLowEventPair (Buffer->EventPairClient); ASSERT (NT_SUCCESS (Status)); } Status = NtWaitForSingleObject (Buffer->TargetThreadHandle, TRUE, NULL); Status = NtClose (Buffer->TargetThreadHandle); Buffer->TargetThreadHandle = NULL; ASSERT (NT_SUCCESS (Status)); Status = NtClose (Buffer->TargetProcessHandle); Buffer->TargetProcessHandle = NULL; ASSERT (NT_SUCCESS (Status)); } InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL ); if (Buffer->TargetProcessId != NULL) { OldTargetClientId.UniqueProcess = Buffer->TargetProcessId; OldTargetClientId.UniqueThread = 0; Status = NtOpenProcess (&OldTargetProcess, PROCESS_ALL_ACCESS, &ObjectAttributes, &OldTargetClientId); if (!NT_SUCCESS (Status)) { return Status; } } else { OldTargetProcess = NtCurrentProcess (); } if (ARGUMENT_PRESENT( TargetProcessId )) { NewTargetClientId.UniqueProcess = TargetProcessId; NewTargetClientId.UniqueThread = 0; Status = NtOpenProcess( &NewTargetProcess, PROCESS_ALL_ACCESS, &ObjectAttributes, &NewTargetClientId ); if (!NT_SUCCESS( Status )) { if (EventPairTarget != NULL) { Status = NtDuplicateObject (OldTargetProcess, EventPairTarget, NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE); // // The target process could have closed the handle to cause us problems. // We ignore this error as the target is only damaging itself. // } if (OldTargetProcess != NtCurrentProcess()) { Status1 = NtClose (OldTargetProcess); ASSERT (NT_SUCCESS (Status1)); } return Status; } } else { NewTargetProcess = NULL; } NewHandle = NULL; if (Buffer->EventPairClient) { if (EventPairTarget != NULL) { Status = NtDuplicateObject (OldTargetProcess, EventPairTarget, NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE); // // The target process could have closed the handle to cause us problems. // We ignore this error as the target is only damaging itself. // } if (NewTargetProcess != NULL) { Status = NtDuplicateObject (NtCurrentProcess (), Buffer->EventPairClient, NewTargetProcess, &NewHandle, 0, 0, DUPLICATE_SAME_ACCESS); if (!NT_SUCCESS (Status)) { if (OldTargetProcess != NtCurrentProcess()) { Status1 = NtClose (OldTargetProcess); ASSERT (NT_SUCCESS (Status1)); } NtClose (NewTargetProcess); return Status; } } } if (OldTargetProcess != NtCurrentProcess()) { if (TargetBuffer->ViewBaseTarget != NULL) { Status1 = NtUnmapViewOfSection (OldTargetProcess, TargetBuffer->ViewBaseTarget); TargetBuffer->ViewBaseTarget = NULL; } Status1 = NtClose (OldTargetProcess); ASSERT (NT_SUCCESS (Status1)); } else { Buffer->ViewBaseTarget = Buffer->ViewBaseClient; } SectionOffset.QuadPart = CONTROL_OFFSET; if (NewTargetProcess != NULL) { Status = NtMapViewOfSection( Buffer->SectionHandleClient, NewTargetProcess, &Buffer->ViewBaseTarget, 0, 0, &SectionOffset, &Buffer->ViewSize, ViewUnmap, 0, PAGE_READWRITE ); if (Status == STATUS_CONFLICTING_ADDRESSES) { Buffer->ViewBaseTarget = NULL; Status = NtMapViewOfSection( Buffer->SectionHandleClient, NewTargetProcess, &Buffer->ViewBaseTarget, 0, 0, &SectionOffset, &Buffer->ViewSize, ViewUnmap, 0, PAGE_READWRITE ); } if (!NT_SUCCESS( Status )) { if (NewHandle != NULL) { NtDuplicateObject (NewTargetProcess, &NewHandle, NULL, NULL, 0, 0, DUPLICATE_CLOSE_SOURCE); } NtClose( NewTargetProcess ); return Status; } if (ARGUMENT_PRESENT( ReturnedTargetProcessHandle )) { *ReturnedTargetProcessHandle = NewTargetProcess; } else { Status = NtClose (NewTargetProcess); ASSERT (NT_SUCCESS (Status)); } } Buffer->EventPairTarget = NewHandle; Buffer->ViewBaseDelta = (ULONG_PTR)Buffer->ViewBaseClient - (ULONG_PTR)Buffer->ViewBaseTarget; Buffer->TargetProcessId = TargetProcessId; *TargetBuffer = *Buffer; return STATUS_SUCCESS; } PVOID RtlpCommitQueryDebugInfo( IN PRTL_DEBUG_INFORMATION Buffer, IN ULONG Size ) /*++ Routine Description: This routine commits a range of memory in the buffer and returns its address. Arguments: Buffer - Query buffer to have space allocated to. If there is not enough commited space then we expand. Size - Size of data to return Return Value: PVOID - Pointer to the commited space. --*/ { NTSTATUS Status; PVOID Result; PVOID CommitBase; SIZE_T CommitSize; SIZE_T NeededSize; if (Size > (MAXULONG - sizeof(PVOID) + 1)) { return NULL; } Size = (Size + sizeof (PVOID) - 1) & ~(sizeof (PVOID) - 1); NeededSize = Buffer->OffsetFree + Size; if (NeededSize > Buffer->CommitSize) { if (NeededSize >= Buffer->ViewSize) { return NULL; } CommitBase = (PCHAR)Buffer + Buffer->CommitSize; CommitSize = NeededSize - Buffer->CommitSize; Status = NtAllocateVirtualMemory( NtCurrentProcess(), &CommitBase, 0, &CommitSize, MEM_COMMIT, PAGE_READWRITE ); if (!NT_SUCCESS( Status )) { return NULL; } Buffer->CommitSize += CommitSize; } Result = (PCHAR)Buffer + Buffer->OffsetFree; Buffer->OffsetFree = NeededSize; return Result; } VOID RtlpDeCommitQueryDebugInfo( IN PRTL_DEBUG_INFORMATION Buffer, IN PVOID p, IN ULONG Size ) /*++ Routine Description: This routine returns a range of previously commited data to the buffer. Arguments: Buffer - Query buffer to have space returned to. Size - Size of data to return Return Value: None. --*/ { if (Size > (MAXULONG - sizeof(PVOID) + 1)) { return; } Size = (Size + sizeof(PVOID) - 1) & ~(sizeof (PVOID) - 1); if (p == (PVOID)(Buffer->OffsetFree - Size)) { Buffer->OffsetFree -= Size; } } NTSYSAPI PRTL_DEBUG_INFORMATION NTAPI RtlCreateQueryDebugBuffer( IN ULONG MaximumCommit OPTIONAL, IN BOOLEAN UseEventPair ) /*++ Routine Description: Creates a new query buffer to allow a remote of local query to be done. Arguments: MaximumCommit - Largest query buffer size allowed UseEventPair - If TRUE the codes caches a single thread in the target to make repeated queries fast. Return Value: PRTL_DEBUG_INFORMATION - NULL on failure, non-NULL otherwise. --*/ { NTSTATUS Status; HANDLE Section; PRTL_DEBUG_INFORMATION Buffer, ReturnBuffer; LARGE_INTEGER MaximumSize; ULONG_PTR ViewSize, CommitSize; if (!ARGUMENT_PRESENT( (PVOID)(ULONG_PTR)MaximumCommit )) { // Sundown Note: ULONG zero-extended. MaximumCommit = 4 * 1024 * 1024; } ViewSize = MaximumCommit + CONTROL_OFFSET; MaximumSize.QuadPart = ViewSize; Status = NtCreateSection( &Section, SECTION_ALL_ACCESS, NULL, &MaximumSize, PAGE_READWRITE, SEC_RESERVE, NULL ); if (!NT_SUCCESS( Status )) { return NULL; } Buffer = NULL; Status = NtMapViewOfSection( Section, NtCurrentProcess(), &Buffer, 0, 0, NULL, &ViewSize, ViewUnmap, 0, PAGE_READWRITE ); if (!NT_SUCCESS (Status)) { NtClose (Section); return NULL; } CommitSize = 1 + CONTROL_OFFSET; Status = NtAllocateVirtualMemory( NtCurrentProcess(), &Buffer, 0, &CommitSize, MEM_COMMIT, PAGE_READWRITE ); if (!NT_SUCCESS( Status )) { NtUnmapViewOfSection( NtCurrentProcess(), Buffer ); NtClose( Section ); return NULL; } if (UseEventPair) { Status = NtCreateEventPair( &Buffer->EventPairClient, EVENT_PAIR_ALL_ACCESS, NULL ); if (!NT_SUCCESS( Status )) { NtFreeVirtualMemory( NtCurrentProcess(), &Buffer, &CommitSize, MEM_RELEASE ); NtUnmapViewOfSection( NtCurrentProcess(), Buffer ); NtClose( Section ); return NULL; } } ReturnBuffer = CONTROL_TO_FAKE (Buffer); Buffer->SectionHandleClient = Section; Buffer->ViewBaseClient = ReturnBuffer; Buffer->OffsetFree = sizeof (RTL_DEBUG_INFORMATION); Buffer->CommitSize = CommitSize - CONTROL_OFFSET; Buffer->ViewSize = ViewSize - CONTROL_OFFSET; *ReturnBuffer = *Buffer; return ReturnBuffer; } NTSYSAPI NTSTATUS NTAPI RtlDestroyQueryDebugBuffer( IN PRTL_DEBUG_INFORMATION Buffer ) /*++ Routine Description: Destroys a previously created buffer that was returned by RtlCreateQueryDebugBuffer Arguments: Buffer - Buffer pointer obtained from RtlCreateQueryDebugBuffer Return Value: NTSTATUS - Status of operation. --*/ { NTSTATUS Status; PRTL_DEBUG_INFORMATION RealBuffer; RealBuffer = FAKE_TO_CONTROL (Buffer); RtlpChangeQueryDebugBufferTarget (RealBuffer, NULL, NULL); if (RealBuffer->EventPairClient != NULL) { Status = NtClose (RealBuffer->EventPairClient); ASSERT (NT_SUCCESS (Status)); } Status = NtClose (RealBuffer->SectionHandleClient); ASSERT (NT_SUCCESS (Status)); Status = NtUnmapViewOfSection (NtCurrentProcess(), RealBuffer); ASSERT (NT_SUCCESS (Status)); return STATUS_SUCCESS; } NTSYSAPI NTSTATUS NTAPI RtlQueryProcessDebugInformation( IN HANDLE UniqueProcessId, IN ULONG Flags, IN OUT PRTL_DEBUG_INFORMATION Buffer ) /*++ Routine Description: Queries the current or a remote process for the specified debug information Arguments: UniqueProcessId - ProcessId of process to query Flags - Flags mask describing what to query Buffer - Buffer pointer obtained from RtlCreateQueryDebugBuffer Return Value: NTSTATUS - Status of operation. --*/ { NTSTATUS Status = STATUS_SUCCESS; HANDLE ProcessHandle, ThreadHandle; THREAD_BASIC_INFORMATION BasicInformation; PRTL_DEBUG_INFORMATION RealBuffer; HANDLE hNiProcess = NULL; Buffer->Flags = Flags; if (Buffer->OffsetFree != 0) { RtlZeroMemory( (Buffer+1), Buffer->OffsetFree - (SIZE_T)sizeof(*Buffer) ); } Buffer->OffsetFree = sizeof( *Buffer ); // // Get process handle for noninvasive query if required // if ( (NtCurrentTeb()->ClientId.UniqueProcess != UniqueProcessId) && (Flags & RTL_QUERY_PROCESS_NONINVASIVE) && (Flags & ( RTL_QUERY_PROCESS_MODULES | RTL_QUERY_PROCESS_MODULES32 ) ) && !(Flags & ~( RTL_QUERY_PROCESS_MODULES | RTL_QUERY_PROCESS_MODULES32 | RTL_QUERY_PROCESS_NONINVASIVE ) ) ) { OBJECT_ATTRIBUTES ObjectAttributes; CLIENT_ID NiProcessId; InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL ); NiProcessId.UniqueProcess = UniqueProcessId; NiProcessId.UniqueThread = 0; if (!NT_SUCCESS( NtOpenProcess( &hNiProcess, PROCESS_ALL_ACCESS, &ObjectAttributes, &NiProcessId ) ) ) { hNiProcess = NULL; } } if ( (NtCurrentTeb()->ClientId.UniqueProcess != UniqueProcessId) && !hNiProcess) { RealBuffer = FAKE_TO_CONTROL (Buffer); RealBuffer->Flags = Flags; RealBuffer->OffsetFree = sizeof (*Buffer); // // Perform remote query // ProcessHandle = NULL; Status = RtlpChangeQueryDebugBufferTarget (RealBuffer, UniqueProcessId, &ProcessHandle); if (!NT_SUCCESS( Status )) { return Status; } if (ProcessHandle == NULL) { waitForDump: Status = NtSetLowWaitHighEventPair( RealBuffer->EventPairClient ); } else { // // don't let the debugger see this remote thread ! // This is a very ugly but effective way to prevent // the debugger deadlocking with the target process when calling // this function. // Status = RtlCreateUserThread( ProcessHandle, NULL, TRUE, 0, 0, 0, RtlpQueryProcessDebugInformationRemote, RealBuffer->ViewBaseTarget, &ThreadHandle, NULL ); if (NT_SUCCESS( Status )) { Status = NtSetInformationThread( ThreadHandle, ThreadHideFromDebugger, NULL, 0 ); if ( !NT_SUCCESS(Status) ) { NtTerminateThread(ThreadHandle,Status); NtClose(ThreadHandle); NtClose(ProcessHandle); return Status; } NtResumeThread(ThreadHandle,NULL); if (RealBuffer->EventPairClient != NULL) { RealBuffer->TargetThreadHandle = ThreadHandle; RealBuffer->TargetProcessHandle = ProcessHandle; goto waitForDump; } Status = NtWaitForSingleObject( ThreadHandle, TRUE, NULL ); if (NT_SUCCESS( Status )) { Status = NtQueryInformationThread( ThreadHandle, ThreadBasicInformation, &BasicInformation, sizeof( BasicInformation ), NULL ); if (NT_SUCCESS( Status )) { Status = BasicInformation.ExitStatus; } if (NT_SUCCESS (Status) && (Flags&(RTL_QUERY_PROCESS_MODULES|RTL_QUERY_PROCESS_MODULES32)) != 0 && Buffer->Modules == NULL) { Status = STATUS_PROCESS_IS_TERMINATING; } } NtClose( ThreadHandle ); } NtClose( ProcessHandle ); } } else { if (Flags & (RTL_QUERY_PROCESS_MODULES | RTL_QUERY_PROCESS_MODULES32)) { Status = RtlQueryProcessModuleInformation( hNiProcess, Flags, Buffer ); if (Status != STATUS_SUCCESS) { goto closeNiProcessAndBreak; } } if (Flags & RTL_QUERY_PROCESS_BACKTRACES) { Status = RtlQueryProcessBackTraceInformation( Buffer ); if (Status != STATUS_SUCCESS) { goto closeNiProcessAndBreak; } } if (Flags & RTL_QUERY_PROCESS_LOCKS) { Status = RtlQueryProcessLockInformation( Buffer ); if (Status != STATUS_SUCCESS) { goto closeNiProcessAndBreak; } } if (Flags & (RTL_QUERY_PROCESS_HEAP_SUMMARY | RTL_QUERY_PROCESS_HEAP_TAGS | RTL_QUERY_PROCESS_HEAP_ENTRIES ) ) { Status = RtlQueryProcessHeapInformation( Buffer ); if (Status != STATUS_SUCCESS) { goto closeNiProcessAndBreak; } } closeNiProcessAndBreak: if ( hNiProcess ) { NtClose( hNiProcess ); } } return Status; } NTSTATUS LdrQueryProcessModuleInformationEx( IN HANDLE hProcess OPTIONAL, IN ULONG_PTR Flags OPTIONAL, OUT PRTL_PROCESS_MODULES ModuleInformation, IN ULONG ModuleInformationLength, OUT PULONG ReturnLength OPTIONAL ); NTSTATUS NTAPI RtlQueryProcessModuleInformation ( IN HANDLE hProcess OPTIONAL, IN ULONG Flags, IN OUT PRTL_DEBUG_INFORMATION Buffer ) /*++ Routine Description: Queries the current or a remote process for loaded module information Arguments: hProcess - Handle to the process being queuried Flags - Flags mask describing what to query Buffer - Buffer pointer obtained from RtlCreateQueryDebugBuffer Return Value: NTSTATUS - Status of operation. --*/ { NTSTATUS Status; ULONG RequiredLength, BufferSize; PRTL_PROCESS_MODULES Modules; ULONG LdrFlags = (Flags & RTL_QUERY_PROCESS_MODULES32) != 0; Status = LdrQueryProcessModuleInformationEx( hProcess, LdrFlags, NULL, 0, &BufferSize ); if (Status == STATUS_INFO_LENGTH_MISMATCH) { Modules = RtlpCommitQueryDebugInfo( Buffer, BufferSize ); if (Modules != NULL) { RtlZeroMemory( Modules, BufferSize ); Status = LdrQueryProcessModuleInformationEx( hProcess, LdrFlags, Modules, BufferSize, &RequiredLength ); if (NT_SUCCESS( Status )) { Buffer->Modules = Modules; return STATUS_SUCCESS; } RtlpDeCommitQueryDebugInfo( Buffer, Modules, BufferSize ); } else { Status = STATUS_NO_MEMORY; } } return Status; } NTSTATUS RtlQueryProcessBackTraceInformation( IN OUT PRTL_DEBUG_INFORMATION Buffer ) /*++ Routine Description: Queries the current process for back trace information Arguments: Buffer - Buffer pointer obtained from RtlCreateQueryDebugBuffer Return Value: NTSTATUS - Status of operation. --*/ { #if i386 NTSTATUS Status; OUT PRTL_PROCESS_BACKTRACES BackTraces; PRTL_PROCESS_BACKTRACE_INFORMATION BackTraceInfo; PSTACK_TRACE_DATABASE DataBase; PRTL_STACK_TRACE_ENTRY p, *pp; ULONG n; DataBase = RtlpAcquireStackTraceDataBase(); if (DataBase == NULL) { return STATUS_SUCCESS; } BackTraces = RtlpCommitQueryDebugInfo( Buffer, FIELD_OFFSET( RTL_PROCESS_BACKTRACES, BackTraces ) ); if (BackTraces == NULL) { return STATUS_NO_MEMORY; } DataBase->DumpInProgress = TRUE; RtlpReleaseStackTraceDataBase(); Status = STATUS_ACCESS_VIOLATION; try { BackTraces->CommittedMemory = (ULONG)DataBase->CurrentUpperCommitLimit - (ULONG)DataBase->CommitBase; BackTraces->ReservedMemory = (ULONG)DataBase->EntryIndexArray - (ULONG)DataBase->CommitBase; BackTraces->NumberOfBackTraceLookups = DataBase->NumberOfEntriesLookedUp; BackTraces->NumberOfBackTraces = DataBase->NumberOfEntriesAdded; BackTraceInfo = RtlpCommitQueryDebugInfo( Buffer, (sizeof( *BackTraceInfo ) * BackTraces->NumberOfBackTraces) ); if (BackTraceInfo == NULL) { Status = STATUS_NO_MEMORY; RtlpDeCommitQueryDebugInfo( Buffer, BackTraces, FIELD_OFFSET( RTL_PROCESS_BACKTRACES, BackTraces ) ); } else { Status = STATUS_SUCCESS; n = DataBase->NumberOfEntriesAdded; pp = DataBase->EntryIndexArray; while (n--) { p = *--pp; BackTraceInfo->SymbolicBackTrace = NULL; BackTraceInfo->TraceCount = p->TraceCount; BackTraceInfo->Index = p->Index; BackTraceInfo->Depth = p->Depth; RtlMoveMemory( BackTraceInfo->BackTrace, p->BackTrace, p->Depth * sizeof( PVOID ) ); BackTraceInfo++; } } } finally { DataBase->DumpInProgress = FALSE; } if (NT_SUCCESS( Status )) { Buffer->BackTraces = BackTraces; } return Status; #else UNREFERENCED_PARAMETER (Buffer); return STATUS_SUCCESS; #endif // i386 } NTSTATUS RtlpQueryProcessEnumHeapsRoutine( PVOID HeapHandle, PVOID Parameter ) { PRTL_DEBUG_INFORMATION Buffer = (PRTL_DEBUG_INFORMATION)Parameter; PRTL_PROCESS_HEAPS Heaps = Buffer->Heaps; PHEAP Heap = (PHEAP)HeapHandle; PRTL_HEAP_INFORMATION HeapInfo; PHEAP_SEGMENT Segment; UCHAR SegmentIndex; // // NOTICE-2002/03/24-ELi // This function assumes that HeapInfo is allocated immediately following // the Buffer->Heaps allocation. Therefore, HeapInfo is not leaked. // HeapInfo = RtlpCommitQueryDebugInfo( Buffer, sizeof( *HeapInfo ) ); if (HeapInfo == NULL) { return STATUS_NO_MEMORY; } RtlZeroMemory( HeapInfo, sizeof( *HeapInfo ) ); HeapInfo->BaseAddress = Heap; HeapInfo->Flags = Heap->Flags; HeapInfo->EntryOverhead = sizeof( HEAP_ENTRY ); HeapInfo->CreatorBackTraceIndex = Heap->AllocatorBackTraceIndex; SegmentIndex = HEAP_MAXIMUM_SEGMENTS; while (SegmentIndex--) { Segment = Heap->Segments[ SegmentIndex ]; if (Segment) { HeapInfo->BytesCommitted += (Segment->NumberOfPages - Segment->NumberOfUnCommittedPages ) * PAGE_SIZE; } } HeapInfo->BytesAllocated = HeapInfo->BytesCommitted - (Heap->TotalFreeSize << HEAP_GRANULARITY_SHIFT); Heaps->NumberOfHeaps += 1; return STATUS_SUCCESS; } NTSYSAPI NTSTATUS NTAPI RtlQueryProcessHeapInformation( IN OUT PRTL_DEBUG_INFORMATION Buffer ) { NTSTATUS Status; PHEAP Heap; BOOLEAN LockAcquired; PRTL_PROCESS_HEAPS Heaps; PRTL_HEAP_INFORMATION HeapInfo; UCHAR SegmentIndex; ULONG i, n, TagIndex; PHEAP_SEGMENT Segment; PRTL_HEAP_TAG Tags; PHEAP_PSEUDO_TAG_ENTRY PseudoTags; PRTL_HEAP_ENTRY Entries; PHEAP_ENTRY CurrentBlock; PHEAP_ENTRY_EXTRA ExtraStuff; PLIST_ENTRY Head, Next; PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock; ULONG Size; PHEAP_UNCOMMMTTED_RANGE UnCommittedRange; Heaps = RtlpCommitQueryDebugInfo( Buffer, FIELD_OFFSET( RTL_PROCESS_HEAPS, Heaps ) ); if (Heaps == NULL) { return STATUS_NO_MEMORY; } Heaps->NumberOfHeaps = 0; Buffer->Heaps = Heaps; Status = RtlEnumProcessHeaps( RtlpQueryProcessEnumHeapsRoutine, Buffer ); if (NT_SUCCESS( Status )) { if (Buffer->Flags & RTL_QUERY_PROCESS_HEAP_TAGS) { Heap = RtlpGlobalTagHeap; if (Heap->TagEntries != NULL) { HeapInfo = RtlpCommitQueryDebugInfo( Buffer, sizeof( *HeapInfo ) ); if (HeapInfo == NULL) { return STATUS_NO_MEMORY; } RtlZeroMemory( HeapInfo, sizeof( *HeapInfo ) ); HeapInfo->BaseAddress = Heap; HeapInfo->Flags = Heap->Flags; HeapInfo->EntryOverhead = sizeof( HEAP_ENTRY ); Heaps->NumberOfHeaps += 1; } for (i=0; iNumberOfHeaps; i++) { HeapInfo = &Heaps->Heaps[ i ]; if (Buffer->SpecificHeap == NULL || Buffer->SpecificHeap == HeapInfo->BaseAddress ) { Heap = HeapInfo->BaseAddress; HeapInfo->NumberOfTags = Heap->NextAvailableTagIndex; n = HeapInfo->NumberOfTags * sizeof( RTL_HEAP_TAG ); if (Heap->PseudoTagEntries != NULL) { HeapInfo->NumberOfTags += HEAP_MAXIMUM_FREELISTS + 1; n += (HEAP_MAXIMUM_FREELISTS + 1) * sizeof( RTL_HEAP_TAG ); } Tags = RtlpCommitQueryDebugInfo( Buffer, n ); if (Tags == NULL) { Status = STATUS_NO_MEMORY; break; } RtlZeroMemory( Tags, n ); HeapInfo->Tags = Tags; if ((PseudoTags = Heap->PseudoTagEntries) != NULL) { HeapInfo->NumberOfPseudoTags = HEAP_NUMBER_OF_PSEUDO_TAG; HeapInfo->PseudoTagGranularity = HEAP_GRANULARITY; for (TagIndex=0; TagIndex<=HEAP_MAXIMUM_FREELISTS; TagIndex++) { Tags->NumberOfAllocations = PseudoTags->Allocs; Tags->NumberOfFrees = PseudoTags->Frees; Tags->BytesAllocated = PseudoTags->Size << HEAP_GRANULARITY_SHIFT; Tags->TagIndex = (USHORT)(TagIndex | HEAP_PSEUDO_TAG_FLAG); if (TagIndex == 0) { swprintf( Tags->TagName, L"Objects>%4u", HEAP_MAXIMUM_FREELISTS << HEAP_GRANULARITY_SHIFT ); } else if (TagIndex < HEAP_MAXIMUM_FREELISTS) { swprintf( Tags->TagName, L"Objects=%4u", TagIndex << HEAP_GRANULARITY_SHIFT ); } else { swprintf( Tags->TagName, L"VirtualAlloc" ); } Tags += 1; PseudoTags += 1; } } RtlMoveMemory( Tags, Heap->TagEntries, Heap->NextAvailableTagIndex * sizeof( RTL_HEAP_TAG ) ); for (TagIndex=0; TagIndexNextAvailableTagIndex; TagIndex++) { Tags->BytesAllocated <<= HEAP_GRANULARITY_SHIFT; Tags += 1; } } } } } else { Buffer->Heaps = NULL; } if (NT_SUCCESS( Status )) { if (Buffer->Flags & RTL_QUERY_PROCESS_HEAP_ENTRIES) { for (i=0; iNumberOfHeaps; i++) { HeapInfo = &Heaps->Heaps[ i ]; Heap = HeapInfo->BaseAddress; if (Buffer->SpecificHeap == NULL || Buffer->SpecificHeap == Heap ) { if (!(Heap->Flags & HEAP_NO_SERIALIZE)) { RtlEnterCriticalSection( (PRTL_CRITICAL_SECTION)Heap->LockVariable ); LockAcquired = TRUE; } else { LockAcquired = FALSE; } try { for (SegmentIndex=0; SegmentIndexSegments[ SegmentIndex ]; if (!Segment) { continue; } Entries = RtlpCommitQueryDebugInfo( Buffer, sizeof( *Entries ) ); if (Entries == NULL) { Status = STATUS_NO_MEMORY; leave; } else if (HeapInfo->Entries == NULL) { HeapInfo->Entries = Entries; } RtlZeroMemory( Entries, sizeof( *Entries ) ); Entries->Flags = RTL_HEAP_SEGMENT; Entries->AllocatorBackTraceIndex = Segment->AllocatorBackTraceIndex; Entries->Size = Segment->NumberOfPages * PAGE_SIZE; Entries->u.s2.CommittedSize = (Segment->NumberOfPages - Segment->NumberOfUnCommittedPages ) * PAGE_SIZE; Entries->u.s2.FirstBlock = Segment->FirstEntry; HeapInfo->NumberOfEntries++; UnCommittedRange = Segment->UnCommittedRanges; CurrentBlock = Segment->FirstEntry; while (CurrentBlock < Segment->LastValidEntry) { Entries = RtlpCommitQueryDebugInfo( Buffer, sizeof( *Entries ) ); if (Entries == NULL) { Status = STATUS_NO_MEMORY; leave; } RtlZeroMemory( Entries, sizeof( *Entries ) ); Size = CurrentBlock->Size << HEAP_GRANULARITY_SHIFT; Entries->Size = Size; HeapInfo->NumberOfEntries++; if (CurrentBlock->Flags & HEAP_ENTRY_BUSY) { if (CurrentBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) { ExtraStuff = (PHEAP_ENTRY_EXTRA)(CurrentBlock + CurrentBlock->Size - 1); #if i386 Entries->AllocatorBackTraceIndex = ExtraStuff->AllocatorBackTraceIndex; #endif // i386 Entries->Flags |= RTL_HEAP_SETTABLE_VALUE; Entries->u.s1.Settable = ExtraStuff->Settable; Entries->u.s1.Tag = ExtraStuff->TagIndex; } else { Entries->u.s1.Tag = CurrentBlock->SmallTagIndex; } Entries->Flags |= RTL_HEAP_BUSY | (CurrentBlock->Flags & HEAP_ENTRY_SETTABLE_FLAGS); } else if (CurrentBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) { PHEAP_FREE_ENTRY_EXTRA FreeExtra; FreeExtra = (PHEAP_FREE_ENTRY_EXTRA)(CurrentBlock + CurrentBlock->Size) - 1; Entries->u.s1.Tag = FreeExtra->TagIndex; Entries->AllocatorBackTraceIndex = FreeExtra->FreeBackTraceIndex; } if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) { CurrentBlock += CurrentBlock->Size; if (UnCommittedRange == NULL) { CurrentBlock = Segment->LastValidEntry; } else { Entries = RtlpCommitQueryDebugInfo( Buffer, sizeof( *Entries ) ); if (Entries == NULL) { Status = STATUS_NO_MEMORY; leave; } RtlZeroMemory( Entries, sizeof( *Entries ) ); Entries->Flags = RTL_HEAP_UNCOMMITTED_RANGE; Entries->Size = UnCommittedRange->Size; HeapInfo->NumberOfEntries++; CurrentBlock = (PHEAP_ENTRY) ((PCHAR)UnCommittedRange->Address + UnCommittedRange->Size); UnCommittedRange = UnCommittedRange->Next; } } else { CurrentBlock += CurrentBlock->Size; } } } Head = &Heap->VirtualAllocdBlocks; Next = Head->Flink; while (Head != Next) { VirtualAllocBlock = CONTAINING_RECORD( Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry ); CurrentBlock = &VirtualAllocBlock->BusyBlock; Entries = RtlpCommitQueryDebugInfo( Buffer, sizeof( *Entries ) ); if (Entries == NULL) { Status = STATUS_NO_MEMORY; leave; } else if (HeapInfo->Entries == NULL) { HeapInfo->Entries = Entries; } RtlZeroMemory( Entries, sizeof( *Entries ) ); Entries->Flags = RTL_HEAP_SEGMENT; Entries->Size = VirtualAllocBlock->ReserveSize; Entries->u.s2.CommittedSize = VirtualAllocBlock->CommitSize; Entries->u.s2.FirstBlock = CurrentBlock; HeapInfo->NumberOfEntries++; Entries = RtlpCommitQueryDebugInfo( Buffer, sizeof( *Entries ) ); if (Entries == NULL) { Status = STATUS_NO_MEMORY; leave; } RtlZeroMemory( Entries, sizeof( *Entries ) ); Entries->Size = VirtualAllocBlock->CommitSize; Entries->Flags = RTL_HEAP_BUSY | (CurrentBlock->Flags & HEAP_ENTRY_SETTABLE_FLAGS); #if i386 Entries->AllocatorBackTraceIndex = VirtualAllocBlock->ExtraStuff.AllocatorBackTraceIndex; #endif // i386 Entries->Flags |= RTL_HEAP_SETTABLE_VALUE; Entries->u.s1.Settable = VirtualAllocBlock->ExtraStuff.Settable; Entries->u.s1.Tag = VirtualAllocBlock->ExtraStuff.TagIndex; HeapInfo->NumberOfEntries++; Next = Next->Flink; } } finally { // // Unlock the heap // if (LockAcquired) { RtlLeaveCriticalSection( (PRTL_CRITICAL_SECTION)Heap->LockVariable ); } } } if (!NT_SUCCESS( Status )) { break; } } } } return Status; } NTSYSAPI NTSTATUS NTAPI RtlQueryProcessLockInformation( IN OUT PRTL_DEBUG_INFORMATION Buffer ) /*++ Routine Description: Queries the current process for ciritcal section information Arguments: Buffer - Buffer pointer obtained from RtlCreateQueryDebugBuffer Return Value: NTSTATUS - Status of operation. --*/ { NTSTATUS Status; PLIST_ENTRY Head, Next; PRTL_PROCESS_LOCKS Locks; PRTL_PROCESS_LOCK_INFORMATION LockInfo; PRTL_CRITICAL_SECTION CriticalSection; PRTL_CRITICAL_SECTION_DEBUG DebugInfo; PRTL_RESOURCE Resource; PRTL_RESOURCE_DEBUG ResourceDebugInfo; Locks = RtlpCommitQueryDebugInfo( Buffer, FIELD_OFFSET( RTL_PROCESS_LOCKS, Locks ) ); if (Locks == NULL) { return STATUS_NO_MEMORY; } Locks->NumberOfLocks = 0; Head = &RtlCriticalSectionList; RtlEnterCriticalSection( &RtlCriticalSectionLock ); Next = Head->Flink; Status = STATUS_SUCCESS; while (Next != Head) { DebugInfo = CONTAINING_RECORD( Next, RTL_CRITICAL_SECTION_DEBUG, ProcessLocksList ); LockInfo = RtlpCommitQueryDebugInfo( Buffer, sizeof( RTL_PROCESS_LOCK_INFORMATION ) ); if (LockInfo == NULL) { Status = STATUS_NO_MEMORY; break; } CriticalSection = DebugInfo->CriticalSection; try { LockInfo->Address = CriticalSection; LockInfo->Type = DebugInfo->Type; LockInfo->CreatorBackTraceIndex = DebugInfo->CreatorBackTraceIndex; if (LockInfo->Type == RTL_CRITSECT_TYPE) { LockInfo->OwningThread = CriticalSection->OwningThread; LockInfo->LockCount = CriticalSection->LockCount; LockInfo->RecursionCount = CriticalSection->RecursionCount; LockInfo->ContentionCount = DebugInfo->ContentionCount; LockInfo->EntryCount = DebugInfo->EntryCount; LockInfo->NumberOfWaitingShared = 0; LockInfo->NumberOfWaitingExclusive = 0; } else { Resource = (PRTL_RESOURCE)CriticalSection; ResourceDebugInfo = Resource->DebugInfo; LockInfo->ContentionCount = ResourceDebugInfo->ContentionCount; LockInfo->OwningThread = Resource->ExclusiveOwnerThread; LockInfo->LockCount = Resource->NumberOfActive; LockInfo->NumberOfWaitingShared = Resource->NumberOfWaitingShared; LockInfo->NumberOfWaitingExclusive = Resource->NumberOfWaitingExclusive; LockInfo->EntryCount = 0; LockInfo->RecursionCount = 0; } Locks->NumberOfLocks++; } except (EXCEPTION_EXECUTE_HANDLER) { DbgPrint("NTDLL: Lost critical section %08lX\n", CriticalSection); RtlpDeCommitQueryDebugInfo( Buffer, LockInfo, sizeof( RTL_PROCESS_LOCK_INFORMATION ) ); } if (Next == Next->Flink) { // // Bail if list is circular // Status = STATUS_INTERNAL_ERROR; break; } else { Next = Next->Flink; } } RtlLeaveCriticalSection( &RtlCriticalSectionLock ); if (NT_SUCCESS( Status )) { Buffer->Locks = Locks; } else { RtlpDeCommitQueryDebugInfo( Buffer, Locks, FIELD_OFFSET( RTL_PROCESS_LOCKS, Locks ) ); } return Status; }