1149 lines
40 KiB
C
1149 lines
40 KiB
C
/*++
|
|
|
|
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 <ntos.h>
|
|
#include "ldrp.h"
|
|
#include <stktrace.h>
|
|
#include <heap.h>
|
|
#include <stdio.h>
|
|
|
|
#define AdjustPointer( t, p, d ) (p); if ((p) != NULL) (p) = (t)((ULONG_PTR)(p) + (d))
|
|
|
|
NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlpQueryProcessDebugInformationRemote(
|
|
IN OUT PRTL_DEBUG_INFORMATION Buffer
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG i;
|
|
ULONG_PTR Delta;
|
|
PRTL_PROCESS_HEAPS Heaps;
|
|
PRTL_HEAP_INFORMATION HeapInfo;
|
|
|
|
if (Buffer->EventPairTarget != NULL) {
|
|
Status = NtWaitLowEventPair( Buffer->EventPairTarget );
|
|
}
|
|
else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
while (NT_SUCCESS( Status )) {
|
|
Status = RtlQueryProcessDebugInformation( NtCurrentTeb()->ClientId.UniqueProcess,
|
|
Buffer->Flags,
|
|
Buffer
|
|
);
|
|
if (NT_SUCCESS( Status )) {
|
|
if (Delta = Buffer->ViewBaseDelta) {
|
|
//
|
|
// 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; i<Heaps->NumberOfHeaps; 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 (Buffer->EventPairTarget == NULL) {
|
|
//
|
|
// If no event pair handle, then exit loop and terminate
|
|
//
|
|
break;
|
|
}
|
|
|
|
Status = NtSetHighWaitLowEventPair( Buffer->EventPairTarget );
|
|
if (Buffer->EventPairTarget == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// All done with buffer, remove from our address space
|
|
// then terminate ourselves so client wakes up.
|
|
//
|
|
|
|
NtUnmapViewOfSection( NtCurrentProcess(), Buffer );
|
|
RtlExitUserThread (Status);
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlpChangeQueryDebugBufferTarget(
|
|
IN PRTL_DEBUG_INFORMATION Buffer,
|
|
IN HANDLE TargetProcessId,
|
|
OUT PHANDLE ReturnedTargetProcessHandle
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
CLIENT_ID OldTargetClientId, NewTargetClientId;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE OldTargetProcess, NewTargetProcess, NewHandle;
|
|
PHANDLE pOldHandle;
|
|
ULONG DuplicateHandleFlags;
|
|
|
|
if (Buffer->EventPairClient != NULL &&
|
|
Buffer->TargetProcessId == TargetProcessId
|
|
) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
DuplicateHandleFlags = DUPLICATE_CLOSE_SOURCE |
|
|
DUPLICATE_SAME_ACCESS |
|
|
DUPLICATE_SAME_ATTRIBUTES;
|
|
|
|
if (Buffer->EventPairClient != NULL) {
|
|
pOldHandle = &Buffer->EventPairTarget;
|
|
}
|
|
else {
|
|
pOldHandle = 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();
|
|
DuplicateHandleFlags &= ~DUPLICATE_CLOSE_SOURCE;
|
|
if (pOldHandle != NULL) {
|
|
pOldHandle = &Buffer->EventPairClient;
|
|
}
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT( TargetProcessId )) {
|
|
NewTargetClientId.UniqueProcess = TargetProcessId;
|
|
NewTargetClientId.UniqueThread = 0;
|
|
Status = NtOpenProcess( &NewTargetProcess,
|
|
PROCESS_ALL_ACCESS,
|
|
&ObjectAttributes,
|
|
&NewTargetClientId
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
if (OldTargetProcess != NtCurrentProcess()) {
|
|
NtClose( OldTargetProcess );
|
|
}
|
|
return Status;
|
|
}
|
|
}
|
|
else {
|
|
NewTargetProcess = NULL;
|
|
}
|
|
|
|
NewHandle = NULL;
|
|
if (pOldHandle != NULL) {
|
|
Status = NtDuplicateObject( OldTargetProcess,
|
|
*pOldHandle,
|
|
NewTargetProcess,
|
|
&NewHandle,
|
|
0,
|
|
0,
|
|
DuplicateHandleFlags
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
if (OldTargetProcess != NtCurrentProcess()) {
|
|
NtClose( OldTargetProcess );
|
|
}
|
|
if (NewTargetProcess != NULL) {
|
|
NtClose( NewTargetProcess );
|
|
}
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
if (OldTargetProcess != NtCurrentProcess()) {
|
|
NtUnmapViewOfSection( OldTargetProcess, Buffer->ViewBaseTarget );
|
|
}
|
|
else {
|
|
Buffer->ViewBaseTarget = Buffer->ViewBaseClient;
|
|
}
|
|
|
|
if (NewTargetProcess != NULL) {
|
|
Status = NtMapViewOfSection( Buffer->SectionHandleClient,
|
|
NewTargetProcess,
|
|
&Buffer->ViewBaseTarget,
|
|
0,
|
|
0,
|
|
NULL,
|
|
&Buffer->ViewSize,
|
|
ViewUnmap,
|
|
0,
|
|
PAGE_READWRITE
|
|
);
|
|
if (Status == STATUS_CONFLICTING_ADDRESSES) {
|
|
Buffer->ViewBaseTarget = NULL;
|
|
Status = NtMapViewOfSection( Buffer->SectionHandleClient,
|
|
NewTargetProcess,
|
|
&Buffer->ViewBaseTarget,
|
|
0,
|
|
0,
|
|
NULL,
|
|
&Buffer->ViewSize,
|
|
ViewUnmap,
|
|
0,
|
|
PAGE_READWRITE
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
if (NewHandle != NULL) {
|
|
NtDuplicateObject( NewTargetProcess,
|
|
&NewHandle,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
0,
|
|
DUPLICATE_CLOSE_SOURCE
|
|
);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT( ReturnedTargetProcessHandle )) {
|
|
*ReturnedTargetProcessHandle = NewTargetProcess;
|
|
}
|
|
else {
|
|
NtClose( NewTargetProcess );
|
|
}
|
|
}
|
|
|
|
Buffer->EventPairTarget = NewHandle;
|
|
Buffer->ViewBaseDelta = (ULONG_PTR)Buffer->ViewBaseClient - (ULONG_PTR)Buffer->ViewBaseTarget;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
PVOID
|
|
RtlpCommitQueryDebugInfo(
|
|
IN PRTL_DEBUG_INFORMATION Buffer,
|
|
IN ULONG Size
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID Result;
|
|
PVOID CommitBase;
|
|
SIZE_T CommitSize;
|
|
SIZE_T NeededSize;
|
|
|
|
Size = (Size + 3) & ~3;
|
|
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
|
|
)
|
|
{
|
|
Size = (Size + 3) & ~3;
|
|
if (p == (PVOID)(Buffer->OffsetFree - Size)) {
|
|
Buffer->OffsetFree -= Size;
|
|
}
|
|
}
|
|
|
|
NTSYSAPI
|
|
PRTL_DEBUG_INFORMATION
|
|
NTAPI
|
|
RtlCreateQueryDebugBuffer(
|
|
IN ULONG MaximumCommit OPTIONAL,
|
|
IN BOOLEAN UseEventPair
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE Section;
|
|
PRTL_DEBUG_INFORMATION Buffer;
|
|
LARGE_INTEGER MaximumSize;
|
|
ULONG_PTR ViewSize, CommitSize;
|
|
|
|
if (!ARGUMENT_PRESENT( (PVOID)(ULONG_PTR)MaximumCommit )) { // Sundown Note: ULONG zero-extended.
|
|
MaximumCommit = 4 * 1024 * 1024;
|
|
}
|
|
MaximumSize.QuadPart = MaximumCommit;
|
|
Status = NtCreateSection( &Section,
|
|
SECTION_ALL_ACCESS,
|
|
NULL,
|
|
&MaximumSize,
|
|
PAGE_READWRITE,
|
|
SEC_RESERVE,
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
return NULL;
|
|
}
|
|
|
|
Buffer = NULL;
|
|
ViewSize = MaximumCommit;
|
|
Status = NtMapViewOfSection( Section,
|
|
NtCurrentProcess(),
|
|
&Buffer,
|
|
0,
|
|
0,
|
|
NULL,
|
|
&ViewSize,
|
|
ViewUnmap,
|
|
0,
|
|
PAGE_READWRITE
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
NtClose( Section );
|
|
return NULL;
|
|
}
|
|
|
|
CommitSize = 1;
|
|
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 )) {
|
|
NtUnmapViewOfSection( NtCurrentProcess(), Buffer );
|
|
NtClose( Section );
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
Buffer->SectionHandleClient = Section;
|
|
Buffer->ViewBaseClient = Buffer;
|
|
Buffer->OffsetFree = 0;
|
|
Buffer->CommitSize = CommitSize;
|
|
Buffer->ViewSize = ViewSize;
|
|
return Buffer;
|
|
}
|
|
|
|
|
|
NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlDestroyQueryDebugBuffer(
|
|
IN PRTL_DEBUG_INFORMATION Buffer
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE ProcessHandle, ThreadHandle;
|
|
THREAD_BASIC_INFORMATION BasicInformation;
|
|
SIZE_T BigSize;
|
|
|
|
RtlpChangeQueryDebugBufferTarget( Buffer, NULL, NULL );
|
|
|
|
Status = STATUS_SUCCESS;
|
|
if (Buffer->TargetThreadHandle != NULL) {
|
|
|
|
Buffer->EventPairTarget = NULL;
|
|
NtSetLowEventPair( Buffer->EventPairClient );
|
|
NtClose( Buffer->EventPairClient );
|
|
Status = NtWaitForSingleObject( Buffer->TargetThreadHandle,
|
|
TRUE,
|
|
NULL
|
|
);
|
|
if (NT_SUCCESS( Status )) {
|
|
Status = NtQueryInformationThread( Buffer->TargetThreadHandle,
|
|
ThreadBasicInformation,
|
|
&BasicInformation,
|
|
sizeof( BasicInformation ),
|
|
NULL
|
|
);
|
|
if (NT_SUCCESS( Status )) {
|
|
Status = BasicInformation.ExitStatus;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
NtClose( Buffer->SectionHandleClient );
|
|
NtUnmapViewOfSection( NtCurrentProcess(), Buffer );
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlQueryProcessDebugInformation(
|
|
IN HANDLE UniqueProcessId,
|
|
IN ULONG Flags,
|
|
IN OUT PRTL_DEBUG_INFORMATION Buffer
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
HANDLE ProcessHandle, ThreadHandle;
|
|
THREAD_BASIC_INFORMATION BasicInformation;
|
|
|
|
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
|
|
) {
|
|
//
|
|
// Perform remote query
|
|
//
|
|
ProcessHandle = NULL;
|
|
Status = RtlpChangeQueryDebugBufferTarget( Buffer, UniqueProcessId, &ProcessHandle );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
if (ProcessHandle == NULL) {
|
|
waitForDump:
|
|
Status = NtSetLowWaitHighEventPair( Buffer->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,
|
|
Buffer->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 (Buffer->EventPairClient != NULL) {
|
|
Buffer->TargetThreadHandle = ThreadHandle;
|
|
Buffer->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
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG RequiredLength;
|
|
PRTL_PROCESS_MODULES Modules;
|
|
ULONG LdrFlags = (Flags & RTL_QUERY_PROCESS_MODULES32) != 0;
|
|
|
|
Status = LdrQueryProcessModuleInformationEx( hProcess,
|
|
LdrFlags,
|
|
NULL,
|
|
0,
|
|
&RequiredLength
|
|
);
|
|
if (Status == STATUS_INFO_LENGTH_MISMATCH) {
|
|
Modules = RtlpCommitQueryDebugInfo( Buffer, RequiredLength );
|
|
if (Modules != NULL) {
|
|
Status = LdrQueryProcessModuleInformationEx( hProcess,
|
|
LdrFlags,
|
|
Modules,
|
|
RequiredLength,
|
|
&RequiredLength
|
|
);
|
|
if (NT_SUCCESS( Status )) {
|
|
Buffer->Modules = Modules;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
NTSTATUS
|
|
RtlQueryProcessBackTraceInformation(
|
|
IN OUT PRTL_DEBUG_INFORMATION Buffer
|
|
)
|
|
{
|
|
#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();
|
|
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;
|
|
}
|
|
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
|
|
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;
|
|
|
|
HeapInfo = RtlpCommitQueryDebugInfo( Buffer, sizeof( *HeapInfo ) );
|
|
if (HeapInfo == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
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;
|
|
ULONG NumberOfHeaps;
|
|
PVOID *ProcessHeaps;
|
|
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;
|
|
PRTL_HEAP_ENTRY p;
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
HeapInfo->BaseAddress = Heap;
|
|
HeapInfo->Flags = Heap->Flags;
|
|
HeapInfo->EntryOverhead = sizeof( HEAP_ENTRY );
|
|
Heaps->NumberOfHeaps += 1;
|
|
}
|
|
|
|
for (i=0; i<Heaps->NumberOfHeaps; 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;
|
|
}
|
|
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",
|
|
TagIndex << HEAP_GRANULARITY_SHIFT
|
|
);
|
|
}
|
|
|
|
Tags += 1;
|
|
PseudoTags += 1;
|
|
}
|
|
}
|
|
|
|
RtlMoveMemory( Tags,
|
|
Heap->TagEntries,
|
|
Heap->NextAvailableTagIndex * sizeof( RTL_HEAP_TAG )
|
|
);
|
|
for (TagIndex=0; TagIndex<Heap->NextAvailableTagIndex; 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; i<Heaps->NumberOfHeaps; 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; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
|
|
Segment = Heap->Segments[ SegmentIndex ];
|
|
if (!Segment) {
|
|
continue;
|
|
}
|
|
|
|
Entries = RtlpCommitQueryDebugInfo( Buffer, sizeof( *Entries ) );
|
|
if (Entries == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
break;
|
|
}
|
|
else
|
|
if (HeapInfo->Entries == NULL) {
|
|
HeapInfo->Entries = 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;
|
|
break;
|
|
}
|
|
|
|
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;
|
|
break;
|
|
}
|
|
|
|
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;
|
|
break;
|
|
}
|
|
else
|
|
if (HeapInfo->Entries == NULL) {
|
|
HeapInfo->Entries = 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;
|
|
break;
|
|
}
|
|
|
|
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
|
|
)
|
|
{
|
|
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;
|
|
}
|
|
|
|
RtlEnterCriticalSection( &RtlCriticalSectionLock );
|
|
Head = &RtlCriticalSectionList;
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
|
|
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
|
|
//
|
|
break;
|
|
}
|
|
else {
|
|
Next = Next->Flink;
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &RtlCriticalSectionLock );
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
Buffer->Locks = Locks;
|
|
}
|
|
|
|
return Status;
|
|
}
|