windows-nt/Source/XPSP1/NT/base/tools/resmon/dh.c

3161 lines
93 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <memory.h>
#include <ntos.h>
#include <nturtl.h>
#include <windows.h>
#include <dbghelp.h>
//
// Include umdh stuff
//
#define _PART_OF_DH_ 1
#include "..\umdh\database.c"
#include "..\umdh\miscellaneous.c"
#include "..\umdh\symbols.c"
#include "..\umdh\umdh.c"
#include "..\umdh\dhcmp.c"
#include "..\umdh\heapwalk.c"
#define MAXDWORD 0xffffffff //this is the max value for a DWORD
//
// the amount of memory to increase the size
// of the buffer for NtQuerySystemInformation at each step
//
#define BUFFER_SIZE_STEP 65536
//
// Globals
//
BOOL fVerbose;
BOOL fDumpModules;
BOOL fDumpBackTraces;
BOOL fIgnoreBackTraces;
BOOL fDumpHeapSummaries;
BOOL fDumpHeapTags;
BOOL fDumpHeapEntries;
BOOL fDumpHeapHogs;
BOOL fDumpLocks;
BOOL fDumpSystemObjects;
BOOL fDumpSystemProcesses;
BOOL fDumpKernelModeInformation;
ULONG BufferSize ;
BOOL fRepetitive; // Are we in repetitive mode
DWORD dwTimeInterval; // what is the repetitive time interval
DWORD dwCurrentIteration; // how many iterations have we done in repetitive mode
CHAR SavedFileName[ MAX_PATH ]; // what would the file name be if we didnt iterate
HANDLE hCtrlCEvent; // The ctrl-c event - only for repetitive mode
ULONG_PTR ProcessId; // -1=win32.sys, 0= kernel, +n= Process ID
HANDLE OutputFile;
CHAR DumpLine[512];
CHAR OutputFileName[ MAX_PATH ];
//
// Prototypes
//
// (this is local even though it looks like it should be in ntos\rtl)
PRTL_DEBUG_INFORMATION
RtlQuerySystemDebugInformation(
ULONG Flags
);
BOOLEAN
ComputeSymbolicBackTraces(
PRTL_PROCESS_BACKTRACES BackTraces1
);
BOOLEAN
LoadSymbolsForModules(
PRTL_PROCESS_MODULES Modules
);
VOID
DumpModules(
PRTL_PROCESS_MODULES Modules
);
VOID
DumpBackTraces( VOID );
VOID
DumpHeaps(
PRTL_PROCESS_HEAPS Heaps,
BOOL fDumpSummary,
BOOL fDumpHogs,
BOOL fDumpTags,
BOOL fDumpEntries
);
VOID
DumpLocks(
PRTL_PROCESS_LOCKS Locks
);
VOID
DumpSystemProcesses( VOID );
VOID
DumpObjects( VOID );
VOID
DumpHandles( VOID );
ULONG
GetDhSymbolicNameForAddress(
IN HANDLE UniqueProcess,
IN ULONG_PTR Address,
OUT LPSTR Name,
IN ULONG MaxNameLength
);
////////////////////////////////////////////////////////////////////////////////////////////
//
// CtrlCHandler
//
// Function:
//
// This function is made the control-c handleris the -r option is used. This
// allows a final snap to be taken when you are done without waiting for the next
// iteration of the loop.
BOOL
CtrlCHandler(DWORD nCtrlType)
{
if (nCtrlType == CTRL_C_EVENT) {
if (hCtrlCEvent) {
SetEvent(hCtrlCEvent);
return TRUE;
}
}
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// AdjustFileName
//
// Function:
//
// Adds the iteration number to the OutputFileName and increments the value
// if fRepetitive has been set. Otherwise it returns
//
VOID
AdjustFileName(VOID)
{
CHAR *pPeriod = NULL;
if ((!fRepetitive)||(!strcmp(SavedFileName, "(stdout)"))||(dwCurrentIteration <= 0))
return;
pPeriod = strrchr(SavedFileName, '.');
if (pPeriod) {
pPeriod[0] = '\0';
sprintf(OutputFileName, "%s_%u.%s", SavedFileName, dwCurrentIteration, (pPeriod+1));
pPeriod[0] = '.';
}
else
sprintf(OutputFileName, "%s_%u.dmp", SavedFileName, dwCurrentIteration, pPeriod);
dwCurrentIteration++;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// DumpOutputString
//
// Function:
//
// Writes 'DumpLine' to 'OutputFile' converting newlines to End-Of-Line sequences.
// Closes OutputFile if there are any errors.
//
VOID
DumpOutputString( VOID )
{
ULONG d;
PCHAR s, s1;
if (OutputFile == NULL) {
return;
}
s = DumpLine;
while (*s) {
s1 = s;
while (*s1 && *s1 != '\n') {
s1 += 1;
}
if (s1 != s && !WriteFile( OutputFile, s, (ULONG)(s1 - s), &d, NULL )) {
CloseHandle( OutputFile );
OutputFile = NULL;
return;
}
if (*s1 == '\n') {
s1 += 1;
if (!WriteFile( OutputFile, "\r\n", 2, &d, NULL )) {
CloseHandle( OutputFile );
OutputFile = NULL;
return;
}
}
s = s1;
}
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Usage
//
// Function: prints usage info to stderr and exits.
//
VOID
Usage( VOID )
{
fputs( "Usage: DH [-p n | -p -1 | -p 0 [-k] [-o]] [-l] [-m] [-s] [-g] [-h] [-t] [r n][-f fileName]\n"
"where: -p n - displays information about process with ClientId of n in DH_n.dmp\n"
" -p -1 - displays information about Win32 Subsystem process in DH_WIN32.DMP.\n"
" -l - displays information about locks.\n"
" -m - displays information about module table.\n"
" -s - displays summary information about heaps.\n"
" -g - displays information about memory hogs.\n"
" -h - displays information about heap entries for each heap.\n"
" -t - displays information about heap tags for each heap.\n"
" -b - displays information about stack back trace database.\n"
" -i - ignore information about stack back trace database.\n"
" -p 0 - displays information about kernel memory and objects in DH_SYS.DMP.\n"
" -o - displays information about object handles (only valid with -p 0).\n"
" -k - displays information about processes and threads (only valid with -p 0).\n"
" -f fileName - specifies the name of the file to write the dump to.\n"
" -# n - sets buffer size to n Meg\n"
" -- specifies the dump output should be written to stdout.\n"
" -r n - generates an log every n minutes with _# appended to filename\n"
" -umdh umdh_options (use -umdh ? for help) \n"
"\n"
" Default flags for -p n are -s -g\n"
" Default flags for -p 0 are -m -s -g -t -k -o\n"
, stderr);
exit( 1 );
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// InitializeSymbolPathEnvVar
//
//
// Function: Sets _NT_SYMBOLS_PATH to point to where the symbols should be.
//
VOID
InitializeSymbolPathEnvVar( VOID )
{
ULONG n;
CHAR Buffer[ MAX_PATH ];
n = GetEnvironmentVariable( "_NT_SYMBOL_PATH", Buffer, sizeof( Buffer ) );
if (n == 0) {
n = GetEnvironmentVariable( "SystemRoot", Buffer, sizeof( Buffer ) );
if (n != 0) {
strcat( Buffer, "\\Symbols" );
SetEnvironmentVariable( "_NT_SYMBOL_PATH", Buffer );
fprintf( stderr, "DH: Default _NT_SYMBOL_PATH to %s\n", Buffer );
}
}
return;
}
////////////////////////////////////////////////////////////////////////////////////////////
PRTL_PROCESS_MODULES Modules;
PRTL_PROCESS_BACKTRACES BackTraces;
PUCHAR SymbolicInfoBase;
PUCHAR SymbolicInfoCurrent;
PUCHAR SymbolicInfoCommitNext;
typedef struct _PROCESS_INFO {
LIST_ENTRY Entry;
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
PSYSTEM_THREAD_INFORMATION ThreadInfo[ 1 ];
} PROCESS_INFO, *PPROCESS_INFO;
LIST_ENTRY ProcessListHead;
PSYSTEM_OBJECTTYPE_INFORMATION ObjectInformation;
PSYSTEM_HANDLE_INFORMATION_EX HandleInformation;
PSYSTEM_PROCESS_INFORMATION ProcessInformation;
#define MAX_TYPE_NAMES 128
PUNICODE_STRING *TypeNames;
UNICODE_STRING UnknownTypeIndex;
////////////////////////////////////////////////////////////////////////////////////////////
//
// main
//
////////////////////////////////////////////////////////////////////////////////////////////
int __cdecl
main(
int argc,
CHAR *argv[],
CHAR *envp[]
)
{
CHAR FileNameBuffer[ 32 ];
CHAR *FilePart;
CHAR *s;
NTSTATUS Status;
PRTL_DEBUG_INFORMATION p;
ULONG QueryDebugProcessFlags;
ULONG HeapNumber;
PRTL_HEAP_INFORMATION HeapInfo;
BOOLEAN WasEnabled;
BOOL bSta;
DWORD dwEventState = WAIT_TIMEOUT;
SYSTEMTIME st;
DWORD CompNameLength = MAX_COMPUTERNAME_LENGTH + 1;
CHAR CompName[MAX_COMPUTERNAME_LENGTH + 1];
//
// Before anything else check if we need to dispatch the command line
// to the umdh parser.
//
if (argc >= 2 && _stricmp (argv[1], "-umdh") == 0) {
UmdhMain (argc - 1, argv + 1);
}
//
// Boost our priority in case a service is higher than us.
//
//EnablePrivilege( SE_INC_BASE_PRIORITY_NAME );
bSta= SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS );
if( !bSta ) printf("SetPriorityClass failed: %d\n",GetLastError());
bSta= SetThreadPriority( GetCurrentProcess(), THREAD_PRIORITY_HIGHEST );;
if( !bSta ) printf("SetThreadPriority failed: %d\n",GetLastError());
InitializeSymbolPathEnvVar();
ProcessId = 0xFFFFFFFF;
OutputFile = NULL;
OutputFileName[ 0 ] = '\0';
while (--argc) {
s = *++argv;
if (*s == '/' || *s == '-') {
while (*++s) {
switch (tolower(*s)) {
case 'v':
case 'V':
fVerbose = TRUE;
break;
case 'i':
case 'I':
fIgnoreBackTraces = TRUE;
break;
case 'b':
case 'B':
fDumpBackTraces = TRUE;
break;
case 'g':
case 'G':
fDumpHeapHogs = TRUE;
break;
case 'h':
case 'H':
fDumpHeapEntries = TRUE;
break;
case 't':
case 'T':
fDumpHeapTags = TRUE;
break;
case 'l':
case 'L':
fDumpLocks = TRUE;
break;
case 'm':
case 'M':
fDumpModules = TRUE;
break;
case 'o':
case 'O':
fDumpSystemObjects = TRUE;
break;
case 'k':
case 'K':
fDumpSystemProcesses = TRUE;
break;
case 's':
case 'S':
fDumpHeapSummaries = TRUE;
break;
case 'p':
case 'P':
if (--argc) {
ProcessId = atoi( *++argv );
if (ProcessId == 0) {
fDumpKernelModeInformation = TRUE;
}
}
else {
Usage();
}
break;
case 'r':
case 'R':
if (--argc) {
dwTimeInterval = atoi( *++argv );
if (dwTimeInterval) {
fRepetitive = TRUE;
dwCurrentIteration = 1;
if (dwTimeInterval > (MAXDWORD/60000))
dwTimeInterval = (MAXDWORD/60000);
hCtrlCEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlCHandler, TRUE);
}
}
else {
Usage();
}
break;
case '-':
OutputFile = GetStdHandle( STD_OUTPUT_HANDLE );
break;
case 'f':
case 'F':
if (--argc) {
strcpy( OutputFileName, *++argv );
}
else {
Usage();
}
break;
case '#':
if (--argc)
{
BufferSize = atoi( *++argv ) * 1024 * 1024 ;
}
else
{
Usage();
}
break;
default:
Usage();
}
}
}
else {
Usage();
}
}
if (!fDumpModules && !fDumpHeapSummaries &&
!fDumpHeapTags && !fDumpHeapHogs && !fDumpLocks
) {
if (fDumpKernelModeInformation) {
if (!fDumpSystemObjects &&
!fDumpSystemProcesses
) {
fDumpModules = TRUE;
fDumpHeapSummaries = TRUE;
fDumpHeapTags = TRUE;
fDumpHeapHogs = TRUE;
fDumpSystemObjects = TRUE;
fDumpSystemProcesses = TRUE;
}
}
else {
fDumpHeapSummaries = TRUE;
fDumpHeapHogs = TRUE;
}
}
if ((fDumpSystemObjects || fDumpSystemProcesses) && !fDumpKernelModeInformation) {
Usage();
}
if (OutputFile == NULL) {
if (OutputFileName[ 0 ] == '\0') {
if ( ProcessId == -1 ) {
sprintf( FileNameBuffer, "DH_win32.dmp" );
}
else if ( ProcessId == 0 ) {
sprintf( FileNameBuffer, "DH_sys.dmp" );
}
else {
sprintf( FileNameBuffer, "DH_%u.dmp", (USHORT)ProcessId );
}
GetFullPathName( FileNameBuffer,
sizeof( OutputFileName ),
OutputFileName,
&FilePart
);
}
}
else {
strcpy( OutputFileName, "(stdout)" );
}
if (fRepetitive) {
strcpy(SavedFileName, OutputFileName);
AdjustFileName();
}
Status= RtlAdjustPrivilege( SE_DEBUG_PRIVILEGE,
TRUE, FALSE, &WasEnabled );
if( !NT_SUCCESS(Status) ) {
fprintf(stderr,"RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE) failed: %08x\n",Status);
}
//
// Get the real process id for the windows sub-system
//
if (ProcessId == -1) {
HANDLE Process;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING UnicodeString;
PROCESS_BASIC_INFORMATION BasicInfo;
RtlInitUnicodeString( &UnicodeString, L"\\WindowsSS" );
InitializeObjectAttributes( &ObjectAttributes,
&UnicodeString,
0,
NULL,
NULL
);
Status = NtOpenProcess( &Process,
PROCESS_ALL_ACCESS,
&ObjectAttributes,
NULL
);
if (NT_SUCCESS(Status)) {
Status = NtQueryInformationProcess( Process,
ProcessBasicInformation,
(PVOID)&BasicInfo,
sizeof(BasicInfo),
NULL
);
NtClose( Process );
}
if (!NT_SUCCESS(Status)) {
fprintf( stderr,"Unable to access Win32 server process - %08x", Status );
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
fprintf( stderr,"\nUse GFLAGS.EXE to ""Enable debugging of Win32 Subsystem"" and reboot.\n" );
}
exit( 1 );
}
ProcessId = BasicInfo.UniqueProcessId;
}
//
// Compute QueryDebugProcessFlags
//
QueryDebugProcessFlags = 0;
if (fDumpModules) {
QueryDebugProcessFlags |= RTL_QUERY_PROCESS_MODULES;
}
if (fDumpBackTraces || fDumpHeapHogs) {
QueryDebugProcessFlags |= RTL_QUERY_PROCESS_BACKTRACES | RTL_QUERY_PROCESS_MODULES;
}
if (fDumpHeapSummaries) {
QueryDebugProcessFlags |= RTL_QUERY_PROCESS_HEAP_SUMMARY;
}
if (fDumpHeapTags) {
QueryDebugProcessFlags |= RTL_QUERY_PROCESS_HEAP_TAGS;
}
if (fDumpHeapEntries || fDumpHeapHogs) {
QueryDebugProcessFlags |= RTL_QUERY_PROCESS_HEAP_ENTRIES;
}
if (fDumpLocks) {
QueryDebugProcessFlags |= RTL_QUERY_PROCESS_LOCKS;
}
// Starting the main loop that does most of the work. This will only
// execute once unless fRepetitive is set
do {
//
// Open the output file
//
fprintf( stderr, "DH: Writing dump output to %s", OutputFileName );
if (OutputFile == NULL) {
OutputFile = CreateFile( OutputFileName,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
0,
NULL
);
if ( OutputFile == INVALID_HANDLE_VALUE ) {
fprintf( stderr, " - unable to open, error == %u\n", GetLastError() );
exit( 1 );
}
}
fprintf( stderr, "\n" );
//Output a Timestamp to the first line of the file
GetLocalTime(&st);
GetComputerName(CompName, &CompNameLength);
sprintf( DumpLine, "DH: Logtime %02u/%02u/%4u-%02u:%02u - Machine=%s - PID=%u\n", st.wMonth,
st.wDay, st.wYear, (st.wHour <= 12) ? st.wHour : (st.wHour - 12), st.wMinute,
CompName, ProcessId);
DumpOutputString();
if (fDumpKernelModeInformation) {
p = RtlQuerySystemDebugInformation( QueryDebugProcessFlags );
if (p == NULL) {
fprintf( stderr, "DH: Unable to query kernel mode information.\n" );
exit( 1 );
}
Status = STATUS_SUCCESS;
}
else {
p = RtlCreateQueryDebugBuffer( BufferSize, FALSE );
printf("RtlCreateQueryDebugBuffer returns: %p\n",p);
Status = RtlQueryProcessDebugInformation( (HANDLE)ProcessId,
QueryDebugProcessFlags,
p
);
if (NT_SUCCESS( Status )) {
printf("RtpQueryProcessDebugInformation\n");
printf(" ProcessId: %d ProcessFlags: %08x Status %08x\n",
ProcessId, QueryDebugProcessFlags, Status );
if ((fDumpBackTraces || fDumpHeapHogs) && p->BackTraces == NULL) {
printf("p->BackTraces: %p\n",p->BackTraces);
fputs( "DH: Unable to query stack back trace information\n"
" Be sure target process was launched with the\n"
" 'Create user mode stack trace DB' enabled\n"
" Use the GFLAGS.EXE application to do this.\n"
, stderr);
}
if (fDumpHeapTags) {
HeapInfo = &p->Heaps->Heaps[ 0 ];
for (HeapNumber = 0; HeapNumber < p->Heaps->NumberOfHeaps; HeapNumber++) {
if (HeapInfo->Tags != NULL && HeapInfo->NumberOfTags != 0) {
break;
}
}
if (HeapNumber == p->Heaps->NumberOfHeaps) {
fputs( "DH: Unable to query heap tag information\n"
" Be sure target process was launched with the\n"
" 'Enable heap tagging' option enabled.\n"
" Use the GFLAGS.EXE application to do this.\n"
, stderr);
}
}
}
else {
fprintf(stderr,"RtlQueryProcessDebugInformation failed: %08x\n",Status);
}
}
if (NT_SUCCESS( Status )) {
if (!fIgnoreBackTraces &&
p->Modules != NULL &&
LoadSymbolsForModules( p->Modules ) &&
p->BackTraces != NULL
) {
ComputeSymbolicBackTraces( p->BackTraces );
}
if (fDumpModules) {
DumpModules( p->Modules );
}
if (!fIgnoreBackTraces && fDumpBackTraces) {
DumpBackTraces();
}
if (p->Heaps) {
DumpHeaps( p->Heaps, fDumpHeapSummaries, fDumpHeapHogs, fDumpHeapTags, fDumpHeapEntries );
}
if (fDumpLocks) {
DumpLocks( p->Locks );
}
if (fDumpSystemObjects) {
DumpObjects();
DumpHandles();
}
if (fDumpSystemProcesses) {
DumpSystemProcesses();
}
}
else {
fprintf( stderr, "Failed to query process, %x\n", Status );
}
RtlDestroyQueryDebugBuffer( p );
// Are we in repetitive mode
if (fRepetitive) {
if (hCtrlCEvent)
dwEventState = WaitForSingleObject(hCtrlCEvent,0);
if (dwEventState == WAIT_OBJECT_0)
fRepetitive = FALSE;
else {
// Lets let the user know we are not hung
GetLocalTime(&st);
printf("Starting at %u:%02u - Sleeping for %u Minute(s)\n",
(st.wHour <= 12) ? st.wHour : (st.wHour - 12), st.wMinute, dwTimeInterval);
// lets sleep for our time interval unless signaled with a ctrl-c
if (hCtrlCEvent)
dwEventState = WaitForSingleObject(hCtrlCEvent,(dwTimeInterval * 60000));
else
Sleep(dwTimeInterval * 60000);
// Don't want to close our handle to stdout
if (strcmp(SavedFileName, "(stdout)")){
CloseHandle( OutputFile );
OutputFile = NULL;
}
// Set up for the next iteration.
AdjustFileName();
//Adjust the pointers to this nasty global memory blob
VirtualFree(SymbolicInfoBase, 4096*4096, MEM_DECOMMIT);
SymbolicInfoCurrent = SymbolicInfoBase;
SymbolicInfoCommitNext = SymbolicInfoBase;
}
}
} while (fRepetitive); //do loop
CloseHandle( OutputFile );
VirtualFree(SymbolicInfoBase, 0, MEM_RELEASE);
if (hCtrlCEvent)
CloseHandle(hCtrlCEvent);
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////
BOOL
SymbolCallbackFunction(
HANDLE hProcess,
ULONG ActionCode,
#ifdef _WIN64
ULONG_PTR CallbackData,
ULONG_PTR UserContext
#else
PVOID CallbackData,
PVOID UserContext
#endif
)
{
PIMAGEHLP_DEFERRED_SYMBOL_LOAD idsl;
idsl = (PIMAGEHLP_DEFERRED_SYMBOL_LOAD) CallbackData;
switch( ActionCode ) {
case CBA_DEFERRED_SYMBOL_LOAD_START:
_strlwr( idsl->FileName );
fprintf( stderr, "Loading symbols for 0x%08x %16s - ",
idsl->BaseOfImage,
idsl->FileName
);
fflush( stderr );
return TRUE;
case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
fprintf( stderr, "*** Error: could not load symbols\n", idsl->FileName );
fflush( stderr );
return TRUE;
case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
fprintf( stderr, "done\n" );
fflush( stderr );
return TRUE;
case CBA_SYMBOLS_UNLOADED:
fprintf( stderr, "Symbols unloaded for 0x%08x %s\n",
idsl->BaseOfImage,
idsl->FileName
);
fflush( stderr );
return TRUE;
default:
return FALSE;
}
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////////////////
#define MAX_SYMNAME_SIZE 1024
#define SYM_BUFFER_SIZE (sizeof(IMAGEHLP_SYMBOL)+MAX_SYMNAME_SIZE)
CHAR symBuffer[SYM_BUFFER_SIZE];
PIMAGEHLP_SYMBOL sym;
PIMAGEHLP_SYMBOL sym = (PIMAGEHLP_SYMBOL) symBuffer;
BOOLEAN
LoadSymbolsForModules(
PRTL_PROCESS_MODULES Modules1
)
{
PRTL_PROCESS_MODULE_INFORMATION ModuleInfo;
ULONG ModuleNumber;
PVOID MaxUserModeAddress;
SymSetOptions( SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_NO_CPP );
sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
sym->MaxNameLength = MAX_SYMNAME_SIZE;
SymInitialize( (HANDLE)ProcessId, NULL, FALSE );
SymRegisterCallback( (HANDLE)ProcessId, SymbolCallbackFunction, 0 );
if (!NT_SUCCESS(NtQuerySystemInformation(SystemRangeStartInformation,
&MaxUserModeAddress,
sizeof(MaxUserModeAddress),
NULL))) {
// assume usermode is the low half of the address space
MaxUserModeAddress = (PVOID)MAXLONG_PTR;
}
Modules = Modules1;
ModuleInfo = &Modules->Modules[ 0 ];
for (ModuleNumber=0; ModuleNumber<Modules->NumberOfModules; ModuleNumber++) {
if (!fDumpKernelModeInformation || ModuleInfo->ImageBase >= MaxUserModeAddress) {
SymLoadModule( (HANDLE)ProcessId,
NULL,
ModuleInfo->FullPathName,
NULL,
(ULONG_PTR)ModuleInfo->ImageBase,
ModuleInfo->ImageSize
);
}
ModuleInfo += 1;
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////
static CHAR DllNameBuffer[ MAX_PATH ];
PCHAR
FindDllHandleName(
PVOID DllHandle
)
{
PRTL_PROCESS_MODULE_INFORMATION ModuleInfo;
LPSTR DllName;
ULONG ModuleNumber;
ModuleInfo = &Modules->Modules[ 0 ];
for (ModuleNumber=0; ModuleNumber<Modules->NumberOfModules; ModuleNumber++) {
if (ModuleInfo->ImageBase == DllHandle) {
strcpy( DllNameBuffer, &ModuleInfo->FullPathName[ ModuleInfo->OffsetToFileName ] );
if ((DllName = strchr( DllNameBuffer, '.' )) != NULL) {
*DllName = '\0';
}
return DllNameBuffer;
}
ModuleInfo += 1;
}
return "UNKNOWN";
}
////////////////////////////////////////////////////////////////////////////////////////////
PUCHAR
SaveSymbolicBackTrace(
IN ULONG Depth,
IN PVOID BackTrace[]
)
{
NTSTATUS Status;
ULONG i, FileNameLength, SymbolOffset;
PCHAR s, SymbolicBackTrace;
if (Depth == 0) {
return NULL;
}
if (SymbolicInfoBase == NULL) {
SymbolicInfoBase = (PUCHAR)VirtualAlloc( NULL,
4096 * 4096,
MEM_RESERVE,
PAGE_READWRITE
);
if (SymbolicInfoBase == NULL) {
fprintf(stderr,"DH: VirtualAlloc(4096*4096...) failed: GetLastError()= %d\n",GetLastError());
return NULL;
}
SymbolicInfoCurrent = SymbolicInfoBase;
SymbolicInfoCommitNext = SymbolicInfoBase;
}
i = 4096;
if ((SymbolicInfoCurrent + i - 1) > SymbolicInfoCommitNext) {
if (!VirtualAlloc( SymbolicInfoCommitNext,
i,
MEM_COMMIT,
PAGE_READWRITE
)
) {
fprintf( stderr, "DH: Exceeded 16MB of space for symbolic stack back traces.\n" );
fprintf( stderr, "DH: virtualalloc(%p,%d...)\n",SymbolicInfoCommitNext,i);
return NULL;
}
SymbolicInfoCommitNext += i;
}
s = SymbolicInfoCurrent;
SymbolicBackTrace = s;
for (i=0; i<Depth; i++) {
if (BackTrace[ i ] == 0) {
break;
}
s += GetDhSymbolicNameForAddress( (HANDLE)ProcessId, (ULONG_PTR)BackTrace[ i ], s, MAX_PATH );
*s++ = '\0';
}
*s++ = '\0';
SymbolicInfoCurrent = s;
return SymbolicBackTrace;
}
////////////////////////////////////////////////////////////////////////////////////////////
BOOLEAN
ComputeSymbolicBackTraces(
PRTL_PROCESS_BACKTRACES BackTraces1
)
{
PRTL_PROCESS_BACKTRACE_INFORMATION BackTraceInfo;
ULONG BackTraceIndex, NumberOfBackTraces;
BackTraces = BackTraces1;
NumberOfBackTraces = BackTraces->NumberOfBackTraces;
BackTraceInfo = &BackTraces->BackTraces[ 0 ];
BackTraceIndex = 0;
while (NumberOfBackTraces--) {
if (!(BackTraceIndex++ % 50)) {
printf( "Getting symbols for Stack Back Trace %05u\r", BackTraceIndex );
}
BackTraceInfo->SymbolicBackTrace = SaveSymbolicBackTrace( BackTraceInfo->Depth,
&BackTraceInfo->BackTrace[ 0 ]
);
BackTraceInfo += 1;
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////
PRTL_PROCESS_BACKTRACE_INFORMATION
FindBackTrace(
IN ULONG BackTraceIndex
)
{
PRTL_PROCESS_BACKTRACE_INFORMATION BackTraceInfo;
if (!BackTraceIndex ||
BackTraces == NULL ||
BackTraceIndex >= BackTraces->NumberOfBackTraces
) {
return( NULL );
}
return &BackTraces->BackTraces[ BackTraceIndex-1 ];
}
////////////////////////////////////////////////////////////////////////////////////////////
VOID
FormatHeapHeader(
PRTL_HEAP_INFORMATION HeapInfo,
PCHAR Title
)
{
CHAR TempBuffer[ 64 ];
PCHAR s;
if (HeapInfo->BaseAddress == (PVOID)IntToPtr(SystemPagedPoolInformation)) {
s = "Paged Pool";
}
else
if (HeapInfo->BaseAddress == (PVOID)IntToPtr(SystemNonPagedPoolInformation)) {
s = "NonPaged Pool";
}
else {
sprintf( TempBuffer, "Heap %p", HeapInfo->BaseAddress );
s = TempBuffer;
}
sprintf( DumpLine, "\n\n*********** %s %s ********************\n\n", s, Title );
DumpOutputString();
}
////////////////////////////////////////////////////////////////////////////////////////////
VOID
DumpModules(
PRTL_PROCESS_MODULES Modules
)
{
PRTL_PROCESS_MODULE_INFORMATION ModuleInfo;
ULONG ModuleNumber;
if (fVerbose) {
fprintf( stderr, "DH: Dumping module information.\n" );
}
ModuleInfo = &Modules->Modules[ 0 ];
sprintf( DumpLine, "\n\n*********** Module Information ********************\n\n" );
DumpOutputString();
sprintf( DumpLine, "Number of loaded modules: %u\n", Modules->NumberOfModules );
DumpOutputString();
ModuleNumber = 0;
while (ModuleNumber++ < Modules->NumberOfModules) {
sprintf( DumpLine, "Module%02u (%02u,%02u,%02u): [%p .. %p] %s\n",
ModuleNumber,
(ULONG)ModuleInfo->LoadOrderIndex,
(ULONG)ModuleInfo->InitOrderIndex,
(ULONG)ModuleInfo->LoadCount,
ModuleInfo->ImageBase,
(ULONG_PTR)ModuleInfo->ImageBase + ModuleInfo->ImageSize - 1,
ModuleInfo->FullPathName
);
DumpOutputString();
ModuleInfo++;
}
return;
}
////////////////////////////////////////////////////////////////////////////////////////////
VOID
DumpBackTraces( VOID )
{
PRTL_PROCESS_BACKTRACE_INFORMATION BackTraceInfo;
ULONG BackTraceIndex;
CHAR *s;
if (BackTraces == NULL) {
return;
}
if (fVerbose) {
fprintf( stderr, "DH: Dumping back trace information.\n" );
}
sprintf( DumpLine, "\n\n*********** BackTrace Information ********************\n\n" );
DumpOutputString();
sprintf( DumpLine, "Number of back traces: %u Looked Up Count: %u\n",
BackTraces->NumberOfBackTraces - 1,
BackTraces->NumberOfBackTraceLookups
);
DumpOutputString();
sprintf( DumpLine, "Reserved Memory: %08x Committed Memory: %08x\n",
BackTraces->ReservedMemory,
BackTraces->CommittedMemory
);
DumpOutputString();
BackTraceInfo = BackTraces->BackTraces;
for (BackTraceIndex=0; BackTraceIndex<BackTraces->NumberOfBackTraces; BackTraceIndex++) {
sprintf( DumpLine, "BackTrace%05lu\n", BackTraceInfo->Index );
DumpOutputString();
if (BackTraceInfo->SymbolicBackTrace == NULL) {
BackTraceInfo->SymbolicBackTrace = SaveSymbolicBackTrace( BackTraceInfo->Depth,
&BackTraceInfo->BackTrace[ 0 ]
);
}
if (s = BackTraceInfo->SymbolicBackTrace) {
while (*s) {
sprintf( DumpLine, " %s\n", s );
DumpOutputString();
while (*s++) {
}
}
}
BackTraceInfo += 1;
}
}
////////////////////////////////////////////////////////////////////////////////////////////
typedef struct _VA_CHUNK {
ULONG_PTR Base;
ULONG_PTR End;
ULONG_PTR Committed;
} VA_CHUNK, *PVA_CHUNK;
VOID
DumpHeapSummary(
PRTL_HEAP_INFORMATION HeapInfo
)
{
PRTL_PROCESS_BACKTRACE_INFORMATION BackTraceInfo;
PUCHAR s;
PRTL_HEAP_ENTRY p;
PCHAR HeapEntryAddress;
ULONG i, HeapEntryNumber;
SIZE_T AddressSpaceUsed;
ULONG NumberOfChunks;
ULONG MaxNumberOfChunks;
PVA_CHUNK Chunks, NewChunks;
MaxNumberOfChunks = 0;
NumberOfChunks = 0;
Chunks = NULL;
p = HeapInfo->Entries;
if (p != NULL && HeapInfo->NumberOfEntries != 0) {
HeapEntryAddress = NULL;
for (HeapEntryNumber=0; HeapEntryNumber<HeapInfo->NumberOfEntries; HeapEntryNumber++) {
if (p->Flags != 0xFF && p->Flags & RTL_HEAP_SEGMENT) {
if (NumberOfChunks == MaxNumberOfChunks) {
MaxNumberOfChunks += 16;
NewChunks = RtlAllocateHeap( RtlProcessHeap(),
HEAP_ZERO_MEMORY,
MaxNumberOfChunks * sizeof( VA_CHUNK )
);
if (Chunks != NULL) {
if (NewChunks != NULL) {
RtlMoveMemory( NewChunks, Chunks, NumberOfChunks * sizeof( VA_CHUNK ) );
}
RtlFreeHeap( RtlProcessHeap(), 0, Chunks );
}
Chunks = NewChunks;
if (Chunks == NULL) {
NumberOfChunks = 0;
break;
}
}
HeapEntryAddress = (PCHAR)p->u.s2.FirstBlock;
Chunks[ NumberOfChunks ].Base = (ULONG_PTR)HeapEntryAddress & ~(4096-1);
if (((ULONG_PTR)HeapEntryAddress - (ULONG_PTR)Chunks[ NumberOfChunks ].Base) < 32) {
HeapEntryAddress = (PCHAR)Chunks[ NumberOfChunks ].Base;
}
Chunks[ NumberOfChunks ].Committed = p->u.s2.CommittedSize;
NumberOfChunks += 1;
}
else {
HeapEntryAddress += p->Size;
if (NumberOfChunks > 0) {
Chunks[ NumberOfChunks-1 ].End = (ULONG_PTR)HeapEntryAddress;
}
}
p += 1;
}
}
sprintf( DumpLine, " Flags: %08x\n", HeapInfo->Flags );
DumpOutputString();
sprintf( DumpLine, " Number Of Entries: %u\n", HeapInfo->NumberOfEntries );
DumpOutputString();
sprintf( DumpLine, " Number Of Tags: %u\n", HeapInfo->NumberOfTags );
DumpOutputString();
sprintf( DumpLine, " Bytes Allocated: %08x\n", HeapInfo->BytesAllocated );
DumpOutputString();
sprintf( DumpLine, " Bytes Committed: %08x\n", HeapInfo->BytesCommitted );
DumpOutputString();
sprintf( DumpLine, " Total FreeSpace: %08x\n", HeapInfo->BytesCommitted -
HeapInfo->BytesAllocated );
DumpOutputString();
sprintf( DumpLine, " Number of Virtual Address chunks used: %u\n", NumberOfChunks );
DumpOutputString();
AddressSpaceUsed = 0;
for (i=0; i<NumberOfChunks; i++) {
sprintf( DumpLine, " Chunk[ %2u ]: [%08x .. %08x) %08x committed\n",
i+1,
Chunks[i].Base,
Chunks[i].End,
Chunks[i].Committed
);
DumpOutputString();
AddressSpaceUsed += (Chunks[i].End - Chunks[i].Base);
}
sprintf( DumpLine, " Address Space Used: %08x\n", AddressSpaceUsed );
DumpOutputString();
sprintf( DumpLine, " Entry Overhead: %u\n", HeapInfo->EntryOverhead );
DumpOutputString();
sprintf( DumpLine, " Creator: (Backtrace%05lu)\n", HeapInfo->CreatorBackTraceIndex );
DumpOutputString();
BackTraceInfo = FindBackTrace( HeapInfo->CreatorBackTraceIndex );
if (BackTraceInfo != NULL && (s = BackTraceInfo->SymbolicBackTrace)) {
while (*s) {
sprintf( DumpLine, " %s\n", s );
DumpOutputString();
while (*s++) {
}
}
}
return;
}
////////////////////////////////////////////////////////////////////////////////////////////
__inline int DiffSizeT(SIZE_T s1, SIZE_T s2)
{
if (s1 == s2)
return 0;
if (s1 > s2)
return -1;
else
return 1;
}
////////////////////////////////////////////////////////////////////////////////////////////
int
__cdecl
CmpTagsRoutine(
const void *Element1,
const void *Element2
)
{
return( DiffSizeT((*(PRTL_HEAP_TAG *)Element2)->BytesAllocated,
(*(PRTL_HEAP_TAG *)Element1)->BytesAllocated)
);
}
////////////////////////////////////////////////////////////////////////////////////////////
PRTL_HEAP_TAG
FindTagEntry(
PRTL_HEAP_INFORMATION HeapInfo,
ULONG TagIndex
)
{
if (TagIndex == 0 || (TagIndex & ~HEAP_PSEUDO_TAG_FLAG) >= HeapInfo->NumberOfTags) {
return NULL;
}
else {
if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
return HeapInfo->Tags + (TagIndex & ~HEAP_PSEUDO_TAG_FLAG);
}
else {
return HeapInfo->Tags + HeapInfo->NumberOfPseudoTags + TagIndex;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////
VOID
DumpHeapTags(
PRTL_HEAP_INFORMATION HeapInfo
)
{
PRTL_HEAP_TAG *TagEntries, TagEntry;
ULONG TagIndex;
PUCHAR s;
UCHAR HeapName[ 64 ];
if (HeapInfo->Tags == NULL || HeapInfo->NumberOfTags == 0) {
return;
}
TagEntries = RtlAllocateHeap( RtlProcessHeap(),
HEAP_ZERO_MEMORY,
HeapInfo->NumberOfTags * sizeof( PRTL_HEAP_TAG )
);
if (TagEntries == NULL) {
fprintf(stderr,"DH: RtlAllocateHeap failed at %d\n",__LINE__ );
return;
}
for (TagIndex=1; TagIndex<HeapInfo->NumberOfTags; TagIndex++) {
TagEntries[ TagIndex-1 ] = &HeapInfo->Tags[ TagIndex ];
}
qsort( (void *)TagEntries,
HeapInfo->NumberOfTags - 1,
sizeof( PRTL_HEAP_TAG ),
CmpTagsRoutine
);
TagEntry = &HeapInfo->Tags[ HeapInfo->NumberOfPseudoTags ];
if (HeapInfo->NumberOfTags > HeapInfo->NumberOfPseudoTags &&
TagEntry->TagName[ 0 ] != UNICODE_NULL
) {
sprintf( HeapName, "Tags for %ws heap", TagEntry->TagName );
}
else {
sprintf( HeapName, "Tags" );
}
FormatHeapHeader( HeapInfo, HeapName );
sprintf( DumpLine, " Allocs Frees Diff Bytes Tag\n" );
DumpOutputString();
for (TagIndex=1; TagIndex<(HeapInfo->NumberOfTags-1); TagIndex++) {
TagEntry = TagEntries[ TagIndex ];
if (TagEntry->BytesAllocated != 0) {
sprintf( DumpLine, " %08x %08x %08x %08x %ws\n",
TagEntry->NumberOfAllocations,
TagEntry->NumberOfFrees,
TagEntry->NumberOfAllocations - TagEntry->NumberOfFrees,
TagEntry->BytesAllocated,
TagEntry->TagName
);
DumpOutputString();
}
}
RtlFreeHeap( RtlProcessHeap(), 0, TagEntries );
return;
}
////////////////////////////////////////////////////////////////////////////////////////////
typedef struct _HEAP_CALLER {
SIZE_T TotalAllocated;
USHORT NumberOfAllocations;
USHORT CallerBackTraceIndex;
PRTL_HEAP_TAG TagEntry;
} HEAP_CALLER, *PHEAP_CALLER;
int
__cdecl
CmpCallerRoutine(
const void *Element1,
const void *Element2
)
{
return( DiffSizeT(((PHEAP_CALLER)Element2)->TotalAllocated,
((PHEAP_CALLER)Element1)->TotalAllocated)
);
}
////////////////////////////////////////////////////////////////////////////////////////////
VOID
DumpHeapHogs(
PRTL_HEAP_INFORMATION HeapInfo
)
{
PRTL_PROCESS_BACKTRACE_INFORMATION BackTraceInfo;
PUCHAR s;
ULONG BackTraceNumber, HeapEntryNumber;
USHORT TagIndex;
PRTL_HEAP_ENTRY p;
PHEAP_CALLER HogList;
if (BackTraces == NULL) {
return;
}
HogList = (PHEAP_CALLER)VirtualAlloc( NULL,
BackTraces->NumberOfBackTraces *
sizeof( HEAP_CALLER ),
MEM_COMMIT,
PAGE_READWRITE
);
if (HogList == NULL) {
fprintf(stderr,"DH: VirtualAlloc failed at %d size: %d\n",__LINE__,
BackTraces->NumberOfBackTraces * sizeof( HEAP_CALLER ) );
return;
}
p = HeapInfo->Entries;
if (p == NULL) {
VirtualFree( HogList, 0, MEM_RELEASE );
return;
}
for (HeapEntryNumber=0; HeapEntryNumber<HeapInfo->NumberOfEntries; HeapEntryNumber++) {
if (p->Flags & RTL_HEAP_BUSY) {
if (p->AllocatorBackTraceIndex >= BackTraces->NumberOfBackTraces) {
p->AllocatorBackTraceIndex = 0;
}
HogList[ p->AllocatorBackTraceIndex ].NumberOfAllocations++;
HogList[ p->AllocatorBackTraceIndex ].TotalAllocated += p->Size;
if (p->u.s1.Tag != 0) {
HogList[ p->AllocatorBackTraceIndex ].TagEntry = FindTagEntry( HeapInfo, p->u.s1.Tag );
}
else
if (HeapInfo->NumberOfPseudoTags != 0) {
TagIndex = HEAP_PSEUDO_TAG_FLAG;
if (p->Size < (HeapInfo->NumberOfPseudoTags * HeapInfo->PseudoTagGranularity)) {
TagIndex |= (p->Size / HeapInfo->PseudoTagGranularity);
}
HogList[ p->AllocatorBackTraceIndex ].TagEntry = FindTagEntry( HeapInfo, TagIndex );
}
}
p++;
}
for (BackTraceNumber = 1;
BackTraceNumber < BackTraces->NumberOfBackTraces;
BackTraceNumber++
) {
HogList[ BackTraceNumber ].CallerBackTraceIndex = (USHORT)BackTraceNumber;
}
qsort( (void *)HogList,
BackTraces->NumberOfBackTraces,
sizeof( HEAP_CALLER ),
CmpCallerRoutine
);
FormatHeapHeader( HeapInfo, "Hogs" );
for (BackTraceNumber=0;
BackTraceNumber<BackTraces->NumberOfBackTraces;
BackTraceNumber++
) {
if (HogList[ BackTraceNumber ].TotalAllocated != 0) {
BackTraceInfo = FindBackTrace( HogList[ BackTraceNumber ].CallerBackTraceIndex );
sprintf( DumpLine, "%08x bytes",
HogList[ BackTraceNumber ].TotalAllocated
);
DumpOutputString();
if (HogList[ BackTraceNumber ].NumberOfAllocations > 1) {
sprintf( DumpLine, " in %04lx allocations (@ %04lx)",
HogList[ BackTraceNumber ].NumberOfAllocations,
HogList[ BackTraceNumber ].TotalAllocated /
HogList[ BackTraceNumber ].NumberOfAllocations
);
DumpOutputString();
}
sprintf( DumpLine, " by: BackTrace%05lu",
BackTraceInfo ? BackTraceInfo->Index : 99999
);
DumpOutputString();
if (HogList[ BackTraceNumber ].TagEntry != NULL) {
sprintf( DumpLine, " (%ws)\n", HogList[ BackTraceNumber ].TagEntry->TagName );
}
else {
sprintf( DumpLine, "\n" );
}
DumpOutputString();
if (BackTraceInfo != NULL && (s = BackTraceInfo->SymbolicBackTrace)) {
while (*s) {
sprintf( DumpLine, " %s\n", s );
DumpOutputString();
while (*s++) {
}
}
}
sprintf( DumpLine, " \n" );
DumpOutputString();
}
}
VirtualFree( HogList, 0, MEM_RELEASE );
}
////////////////////////////////////////////////////////////////////////////////////////////
VOID
DumpHeapEntries(
PRTL_HEAP_INFORMATION HeapInfo
)
{
PRTL_PROCESS_BACKTRACE_INFORMATION BackTraceInfo;
PUCHAR s;
PRTL_HEAP_ENTRY p;
PRTL_HEAP_TAG TagEntry;
PCHAR HeapEntryAddress;
SIZE_T HeapEntrySize;
ULONG HeapEntryNumber;
p = HeapInfo->Entries;
if (p == NULL || HeapInfo->NumberOfEntries == 0) {
return;
}
FormatHeapHeader( HeapInfo, "Entries" );
HeapEntryAddress = NULL;
for (HeapEntryNumber=0; HeapEntryNumber<HeapInfo->NumberOfEntries; HeapEntryNumber++) {
if (p->Flags != 0xFF && p->Flags & RTL_HEAP_SEGMENT) {
HeapEntryAddress = (PCHAR)p->u.s2.FirstBlock;
sprintf( DumpLine, "\n[%p : %p]\n",
(ULONG_PTR)HeapEntryAddress & ~(4096-1),
p->u.s2.CommittedSize
);
DumpOutputString();
}
else {
HeapEntrySize = p->Size;
if (p->Flags == RTL_HEAP_UNCOMMITTED_RANGE) {
sprintf( DumpLine, "%p: %p - UNCOMMITTED\n",
HeapEntryAddress,
HeapEntrySize
);
DumpOutputString();
}
else
if (p->Flags & RTL_HEAP_BUSY) {
s = DumpLine;
s += sprintf( s, "%p: %p - BUSY [%02x]",
HeapEntryAddress,
HeapEntrySize,
p->Flags
);
TagEntry = FindTagEntry( HeapInfo, p->u.s1.Tag );
if (TagEntry != NULL) {
s += sprintf( s, "(%ws)", TagEntry->TagName );
}
if (BackTraces != NULL) {
s += sprintf( s, " (BackTrace%05lu)",
p->AllocatorBackTraceIndex
);
}
if (p->Flags & RTL_HEAP_SETTABLE_VALUE &&
p->Flags & RTL_HEAP_SETTABLE_FLAG1
) {
s += sprintf( s, " (Handle: %x)", p->u.s1.Settable );
}
if (p->Flags & RTL_HEAP_SETTABLE_FLAG2) {
s += sprintf( s, " (DDESHARE)" );
}
if (p->Flags & RTL_HEAP_PROTECTED_ENTRY) {
s += sprintf( s, " (Protected)" );
}
sprintf( s, "\n" );
DumpOutputString();
}
else {
sprintf( DumpLine, "%p: %p - FREE\n",
HeapEntryAddress,
HeapEntrySize
);
DumpOutputString();
}
sprintf( DumpLine, "\n" );
DumpOutputString();
HeapEntryAddress += HeapEntrySize;
}
p++;
}
return;
}
////////////////////////////////////////////////////////////////////////////////////////////
VOID
DumpHeaps(
PRTL_PROCESS_HEAPS Heaps,
BOOL fDumpSummary,
BOOL fDumpHogs,
BOOL fDumpTags,
BOOL fDumpEntries
)
{
ULONG HeapNumber;
PRTL_HEAP_INFORMATION HeapInfo;
if (fVerbose) {
fprintf( stderr, "DH: Dumping heap information.\n" );
}
HeapInfo = &Heaps->Heaps[ 0 ];
for (HeapNumber = 0; HeapNumber < Heaps->NumberOfHeaps; HeapNumber++) {
FormatHeapHeader( HeapInfo, "Information" );
if (fDumpSummary) {
DumpHeapSummary( HeapInfo );
}
if (fDumpTags) {
DumpHeapTags( HeapInfo );
}
if (fDumpHogs) {
DumpHeapHogs( HeapInfo );
}
if (fDumpEntries) {
DumpHeapEntries( HeapInfo );
}
HeapInfo += 1;
}
return;
}
////////////////////////////////////////////////////////////////////////////////////////////
VOID
DumpLocks(
PRTL_PROCESS_LOCKS Locks
)
{
PRTL_PROCESS_LOCK_INFORMATION LockInfo;
PRTL_PROCESS_BACKTRACE_INFORMATION BackTraceInfo;
ULONG LockNumber;
PUCHAR s;
if (fVerbose) {
fprintf( stderr, "DH: Dumping lock information.\n" );
}
sprintf( DumpLine, "\n\n*********** Lock Information ********************\n\n" );
DumpOutputString();
if (Locks == NULL) {
return;
}
sprintf( DumpLine, "NumberOfLocks == %u\n", Locks->NumberOfLocks );
DumpOutputString();
LockInfo = &Locks->Locks[ 0 ];
LockNumber = 0;
while (LockNumber++ < Locks->NumberOfLocks) {
sprintf( DumpLine, "Lock%u at %p (%s)\n",
LockNumber,
LockInfo->Address,
LockInfo->Type == RTL_CRITSECT_TYPE ? "CriticalSection" : "Resource"
);
DumpOutputString();
sprintf( DumpLine, " Contention: %u\n", LockInfo->ContentionCount );
DumpOutputString();
sprintf( DumpLine, " Usage: %u\n", LockInfo->EntryCount );
DumpOutputString();
if (LockInfo->CreatorBackTraceIndex != 0) {
sprintf( DumpLine, " Creator: (Backtrace%05lu)\n", LockInfo->CreatorBackTraceIndex );
DumpOutputString();
BackTraceInfo = FindBackTrace( LockInfo->CreatorBackTraceIndex );
if (BackTraceInfo != NULL && (s = BackTraceInfo->SymbolicBackTrace)) {
while (*s) {
sprintf( DumpLine, " %s\n", s );
DumpOutputString();
while (*s++) {
}
}
}
}
if (LockInfo->OwningThread) {
sprintf( DumpLine, " Owner: (ThreadID == %p)\n", LockInfo->OwningThread );
DumpOutputString();
}
sprintf( DumpLine, "\n" );
DumpOutputString();
LockInfo++;
}
}
////////////////////////////////////////////////////////////////////////////////////////////
#define RTL_NEW( p ) RtlAllocateHeap( RtlProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *p ) )
BOOLEAN
LoadSystemModules(
PRTL_DEBUG_INFORMATION Buffer
);
BOOLEAN
LoadSystemBackTraces(
PRTL_DEBUG_INFORMATION Buffer
);
BOOLEAN
LoadSystemPools(
PRTL_DEBUG_INFORMATION Buffer
);
BOOLEAN
LoadSystemTags(
PRTL_HEAP_INFORMATION PagedPoolInfo,
PRTL_HEAP_INFORMATION NonPagedPoolInfo
);
BOOLEAN
LoadSystemPool(
PRTL_HEAP_INFORMATION HeapInfo,
SYSTEM_INFORMATION_CLASS SystemInformationClass
);
BOOLEAN
LoadSystemLocks(
PRTL_DEBUG_INFORMATION Buffer
);
BOOLEAN
LoadSystemObjects(
PRTL_DEBUG_INFORMATION Buffer
);
BOOLEAN
LoadSystemHandles(
PRTL_DEBUG_INFORMATION Buffer
);
BOOLEAN
LoadSystemProcesses(
PRTL_DEBUG_INFORMATION Buffer
);
PSYSTEM_PROCESS_INFORMATION
FindProcessInfoForCid(
IN HANDLE UniqueProcessId
);
////////////////////////////////////////////////////////////////////////////////////////////
PRTL_DEBUG_INFORMATION
RtlQuerySystemDebugInformation(
ULONG Flags
)
{
PRTL_DEBUG_INFORMATION Buffer;
Buffer = RTL_NEW( Buffer );
if (Buffer == NULL) {
fprintf(stderr,"DH: allocation failure for %d byte at line %d\n",sizeof(*Buffer),__LINE__);
return NULL;
}
if ((Flags & RTL_QUERY_PROCESS_MODULES) != 0 && !LoadSystemModules( Buffer )) {
fputs( "DH: Unable to query system module list.\n", stderr );
}
if ((Flags & RTL_QUERY_PROCESS_BACKTRACES) != 0 && !LoadSystemBackTraces( Buffer )) {
fputs( "DH: Unable to query system back trace information.\n"
" Be sure the system was booted with the\n"
" 'Create kernel mode stack trace DB' enabled\n"
" Use the GFLAGS.EXE application to do this.\n"
, stderr);
}
if ((Flags & (RTL_QUERY_PROCESS_HEAP_SUMMARY |
RTL_QUERY_PROCESS_HEAP_TAGS |
RTL_QUERY_PROCESS_HEAP_ENTRIES
)
) != 0 &&
!LoadSystemPools( Buffer )
) {
fputs( "DH: Unable to query system pool information.\n", stderr );
}
if ((Flags & RTL_QUERY_PROCESS_LOCKS) != 0 && !LoadSystemLocks( Buffer )) {
fputs( "DH: Unable to query system lock information.\n", stderr);
}
if (fDumpSystemObjects && !LoadSystemObjects( Buffer )) {
fputs( "DH: Unable to query system object information.\n", stderr );
}
if (fDumpSystemObjects && !LoadSystemHandles( Buffer )) {
fputs( "DH: Unable to query system handle information.\n", stderr );
}
if (!LoadSystemProcesses( Buffer )) {
fputs( "DH: Unable to query system process information.\n", stderr );
}
return Buffer;
}
////////////////////////////////////////////////////////////////////////////////////////////
PVOID
BufferAlloc(
IN OUT SIZE_T *Length
)
{
PVOID Buffer;
MEMORY_BASIC_INFORMATION MemoryInformation;
Buffer = VirtualAlloc( NULL,
*Length,
MEM_COMMIT,
PAGE_READWRITE
);
if (Buffer != NULL &&
VirtualQuery( Buffer, &MemoryInformation, sizeof( MemoryInformation ) )
) {
*Length = MemoryInformation.RegionSize;
}
if( Buffer == NULL ) {
fprintf(stderr,"DH: VirtualAlloc failed for %d bytes at line %d\n",*Length,__LINE__);
}
return Buffer;
}
////////////////////////////////////////////////////////////////////////////////////////////
BOOLEAN
LoadSystemModules(
PRTL_DEBUG_INFORMATION Buffer
)
{
NTSTATUS Status;
PVOID BufferToFree;
RTL_PROCESS_MODULES ModulesBuffer;
PRTL_PROCESS_MODULES Modules;
SIZE_T RequiredLength;
Modules = &ModulesBuffer;
RequiredLength = sizeof( ModulesBuffer );
BufferToFree = NULL;
while (TRUE) {
Status = NtQuerySystemInformation( SystemModuleInformation,
Modules,
(ULONG)RequiredLength,
(ULONG *)&RequiredLength
);
if (Status == STATUS_INFO_LENGTH_MISMATCH) {
if (Modules != &ModulesBuffer) {
break;
}
Modules = BufferAlloc( &RequiredLength );
if (Modules == NULL) {
break;
}
BufferToFree = Modules;
}
else
if (NT_SUCCESS( Status )) {
Buffer->Modules = Modules;
return TRUE;
}
else {
fprintf(stderr,"DH: QuerySystemInformation failed ntstatus: %08x line: %d\n",
Status,__LINE__);
break;
}
}
if (Modules != &ModulesBuffer) {
VirtualFree( Modules, 0, MEM_RELEASE );
}
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////////////////
BOOLEAN
LoadSystemBackTraces(
PRTL_DEBUG_INFORMATION Buffer
)
{
NTSTATUS Status;
RTL_PROCESS_BACKTRACES BackTracesBuffer;
SIZE_T RequiredLength;
BackTraces = &BackTracesBuffer;
RequiredLength = sizeof( BackTracesBuffer );
while (TRUE) {
Status = NtQuerySystemInformation( SystemStackTraceInformation,
BackTraces,
(ULONG)RequiredLength,
(ULONG *)&RequiredLength
);
if (Status == STATUS_INFO_LENGTH_MISMATCH) {
if (BackTraces != &BackTracesBuffer) {
break;
}
RequiredLength += 4096; // slop, since we may trigger more allocs.
BackTraces = BufferAlloc( &RequiredLength );
if (BackTraces == NULL) {
return FALSE;
}
}
else
if (!NT_SUCCESS( Status )) {
fprintf(stderr,"DH: QuerySystemInformation failed ntstatus: %08x line: %d\n",Status,__LINE__);
break;
}
else {
Buffer->BackTraces = BackTraces;
return TRUE;
}
}
if (BackTraces != &BackTracesBuffer) {
VirtualFree( BackTraces, 0, MEM_RELEASE );
}
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////////////////
BOOLEAN
LoadSystemPools(
PRTL_DEBUG_INFORMATION Buffer
)
{
PRTL_PROCESS_HEAPS Heaps;
SIZE_T Size;
Size= FIELD_OFFSET( RTL_PROCESS_HEAPS, Heaps) + 2 * sizeof( RTL_HEAP_INFORMATION );
Heaps = RtlAllocateHeap( RtlProcessHeap(),
HEAP_ZERO_MEMORY,
Size );
if (Heaps == NULL) {
fprintf(stderr,"DH: AllocateHeap failed for %d bytes at line %d\n",Size,__LINE__);
return FALSE;
}
Buffer->Heaps = Heaps;
if (LoadSystemTags( &Heaps->Heaps[ 0 ], &Heaps->Heaps[ 1 ] )) {
if (LoadSystemPool( &Heaps->Heaps[ 0 ], SystemPagedPoolInformation )) {
Heaps->NumberOfHeaps = 1;
if (LoadSystemPool( &Heaps->Heaps[ 1 ], SystemNonPagedPoolInformation )) {
Heaps->NumberOfHeaps = 2;
return TRUE;
}
}
}
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////////////////
NTSTATUS
QueryPoolTagInformationIterative(
PVOID *CurrentBuffer,
SIZE_T *CurrentBufferSize
)
{
SIZE_T NewBufferSize;
NTSTATUS ReturnedStatus = STATUS_SUCCESS;
if( CurrentBuffer == NULL || CurrentBufferSize == NULL ) {
return STATUS_INVALID_PARAMETER;
}
if( *CurrentBufferSize == 0 || *CurrentBuffer == NULL ) {
//
// there is no buffer allocated yet
//
NewBufferSize = sizeof( UCHAR ) * BUFFER_SIZE_STEP;
*CurrentBuffer = VirtualAlloc(
NULL,
NewBufferSize,
MEM_COMMIT,
PAGE_READWRITE
);
if( *CurrentBuffer != NULL ) {
*CurrentBufferSize = NewBufferSize;
} else {
//
// insufficient memory
//
ReturnedStatus = STATUS_INSUFFICIENT_RESOURCES;
}
}
//
// iterate by buffer's size
//
while( *CurrentBuffer != NULL ) {
ReturnedStatus = NtQuerySystemInformation (
SystemPoolTagInformation,
*CurrentBuffer,
(ULONG)*CurrentBufferSize,
NULL );
if( ! NT_SUCCESS(ReturnedStatus) ) {
//
// free the current buffer
//
VirtualFree(
*CurrentBuffer,
0,
MEM_RELEASE );
*CurrentBuffer = NULL;
if (ReturnedStatus == STATUS_INFO_LENGTH_MISMATCH) {
//
// try with a greater buffer size
//
NewBufferSize = *CurrentBufferSize + BUFFER_SIZE_STEP;
*CurrentBuffer = VirtualAlloc(
NULL,
NewBufferSize,
MEM_COMMIT,
PAGE_READWRITE
);
if( *CurrentBuffer != NULL ) {
//
// allocated new buffer
//
*CurrentBufferSize = NewBufferSize;
} else {
//
// insufficient memory
//
ReturnedStatus = STATUS_INSUFFICIENT_RESOURCES;
*CurrentBufferSize = 0;
}
} else {
*CurrentBufferSize = 0;
}
} else {
//
// NtQuerySystemInformation returned success
//
break;
}
}
return ReturnedStatus;
}
////////////////////////////////////////////////////////////////////////////////////////////
BOOLEAN
LoadSystemTags(
PRTL_HEAP_INFORMATION PagedPoolInfo,
PRTL_HEAP_INFORMATION NonPagedPoolInfo
)
{
NTSTATUS Status;
SIZE_T RequiredLength;
PSYSTEM_POOLTAG_INFORMATION Tags;
PSYSTEM_POOLTAG TagInfo;
PRTL_HEAP_TAG pPagedPoolTag, pNonPagedPoolTag;
ULONG n, TagIndex;
PagedPoolInfo->NumberOfTags = 0;
PagedPoolInfo->Tags = NULL;
NonPagedPoolInfo->NumberOfTags = 0;
NonPagedPoolInfo->Tags = NULL;
Tags = NULL;
RequiredLength = 0;
while (TRUE) {
Status = QueryPoolTagInformationIterative(
&Tags,
&RequiredLength
);
if (!NT_SUCCESS( Status )) {
fprintf(stderr,"DH: QuerySystemInformation failed ntstatus: %08x line: %d\n",Status,__LINE__);
break;
}
else {
PagedPoolInfo->NumberOfTags = Tags->Count + 1;
NonPagedPoolInfo->NumberOfTags = Tags->Count + 1;
n = (Tags->Count + 1) * sizeof( RTL_HEAP_TAG );
PagedPoolInfo->Tags = RtlAllocateHeap( RtlProcessHeap(), HEAP_ZERO_MEMORY, n );
NonPagedPoolInfo->Tags = RtlAllocateHeap( RtlProcessHeap(), HEAP_ZERO_MEMORY, n );
TagInfo = &Tags->TagInfo[ 0 ];
pPagedPoolTag = PagedPoolInfo->Tags + 1;
pNonPagedPoolTag = NonPagedPoolInfo->Tags + 1;
for (TagIndex=1; TagIndex<=Tags->Count; TagIndex++) {
UNICODE_STRING UnicodeString;
ANSI_STRING AnsiString;
pPagedPoolTag->TagIndex = (USHORT)TagIndex;
pPagedPoolTag->NumberOfAllocations = TagInfo->PagedAllocs;
pPagedPoolTag->NumberOfFrees = TagInfo->PagedFrees;
pPagedPoolTag->BytesAllocated = TagInfo->PagedUsed;
UnicodeString.Buffer = pPagedPoolTag->TagName;
UnicodeString.MaximumLength = sizeof( pPagedPoolTag->TagName );
AnsiString.Buffer = TagInfo->Tag;
AnsiString.Length = sizeof( TagInfo->Tag );
AnsiString.MaximumLength = AnsiString.Length;
RtlAnsiStringToUnicodeString( &UnicodeString, &AnsiString, FALSE );
pNonPagedPoolTag->TagIndex = (USHORT)TagIndex;
pNonPagedPoolTag->NumberOfAllocations = TagInfo->NonPagedAllocs;
pNonPagedPoolTag->NumberOfFrees = TagInfo->NonPagedFrees;
pNonPagedPoolTag->BytesAllocated = TagInfo->NonPagedUsed;
wcscpy( pNonPagedPoolTag->TagName, pPagedPoolTag->TagName );
pPagedPoolTag += 1;
pNonPagedPoolTag += 1;
TagInfo += 1;
}
break;
}
}
if (Tags != NULL) {
VirtualFree( Tags, 0, MEM_RELEASE );
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////
USHORT
FindPoolTagIndex(
PRTL_HEAP_TAG Tags,
ULONG NumberOfTags,
PCHAR Tag
)
{
ULONG i;
UCHAR AnsiTagName[ 5 ];
WCHAR UnicodeTagName[ 5 ];
UNICODE_STRING UnicodeString;
ANSI_STRING AnsiString;
strncpy( AnsiTagName, Tag, 4 );
UnicodeString.Buffer = UnicodeTagName;
UnicodeString.MaximumLength = sizeof( UnicodeTagName );
AnsiString.Buffer = AnsiTagName;
AnsiString.Length = (USHORT)strlen( AnsiTagName );
AnsiString.MaximumLength = AnsiString.Length;
RtlAnsiStringToUnicodeString( &UnicodeString, &AnsiString, FALSE );
Tags += 1;
for (i=1; i<NumberOfTags; i++) {
if (!_wcsicmp( UnicodeTagName, Tags->TagName )) {
return (USHORT)i;
}
Tags += 1;
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////
BOOLEAN
LoadSystemPool(
PRTL_HEAP_INFORMATION HeapInfo,
SYSTEM_INFORMATION_CLASS SystemInformationClass
)
{
NTSTATUS Status;
SIZE_T RequiredLength;
SYSTEM_POOL_INFORMATION PoolInfoBuffer;
PSYSTEM_POOL_INFORMATION PoolInfo;
PSYSTEM_POOL_ENTRY PoolEntry;
PRTL_HEAP_ENTRY p;
ULONG n;
BOOLEAN Result;
HeapInfo->BaseAddress = (PVOID)SystemInformationClass;
PoolInfo = &PoolInfoBuffer;
RequiredLength = sizeof( PoolInfoBuffer );
Result = FALSE;
while (TRUE) {
Status = NtQuerySystemInformation( SystemInformationClass,
PoolInfo,
(ULONG)RequiredLength,
(ULONG *)&RequiredLength
);
if (Status == STATUS_INFO_LENGTH_MISMATCH) {
if (PoolInfo != &PoolInfoBuffer) {
break;
}
RequiredLength += 4096; // slop, since we may trigger more allocs.
PoolInfo = BufferAlloc( &RequiredLength );
if (PoolInfo == NULL) {
return FALSE;
}
}
else
if (!NT_SUCCESS( Status )) {
fprintf(stderr,"DH: QuerySystemInformation failed ntstatus: %08x line: %d\n",Status,__LINE__);
break;
}
else {
n = PoolInfo->NumberOfEntries;
HeapInfo->NumberOfEntries = n + 1;
HeapInfo->EntryOverhead = PoolInfo->EntryOverhead;
HeapInfo->Entries = RtlAllocateHeap( RtlProcessHeap(),
HEAP_ZERO_MEMORY,
HeapInfo->NumberOfEntries * sizeof( RTL_HEAP_ENTRY )
);
if( HeapInfo->Entries == NULL ) {
fprintf(stderr,"DH: Alloc failed for %d bytes at line %d\n",
HeapInfo->NumberOfEntries * sizeof( RTL_HEAP_ENTRY),__LINE__);
break;
}
p = HeapInfo->Entries;
p->Flags = RTL_HEAP_SEGMENT;
p->u.s2.CommittedSize = PoolInfo->TotalSize;
p->u.s2.FirstBlock = PoolInfo->FirstEntry;
p += 1;
PoolEntry = &PoolInfo->Entries[ 0 ];
while (n--) {
p->Size = PoolEntry->Size;
if (PoolEntry->TagUlong & PROTECTED_POOL) {
p->Flags |= RTL_HEAP_PROTECTED_ENTRY;
PoolEntry->TagUlong &= ~PROTECTED_POOL;
}
p->u.s1.Tag = FindPoolTagIndex( HeapInfo->Tags,
HeapInfo->NumberOfTags,
PoolEntry->Tag
);
HeapInfo->BytesCommitted += p->Size;
if (PoolEntry->Allocated) {
p->Flags |= RTL_HEAP_BUSY;
p->AllocatorBackTraceIndex = PoolEntry->AllocatorBackTraceIndex;
HeapInfo->BytesAllocated += p->Size;
}
p += 1;
PoolEntry += 1;
}
Result = TRUE;
break;
}
}
if (PoolInfo != &PoolInfoBuffer) {
VirtualFree( PoolInfo, 0, MEM_RELEASE );
}
return Result;
}
////////////////////////////////////////////////////////////////////////////////////////////
BOOLEAN
LoadSystemLocks(
PRTL_DEBUG_INFORMATION Buffer
)
{
NTSTATUS Status;
RTL_PROCESS_LOCKS LocksBuffer;
PRTL_PROCESS_LOCKS Locks;
SIZE_T RequiredLength;
Locks = &LocksBuffer;
RequiredLength = sizeof( LocksBuffer );
while (TRUE) {
Status = NtQuerySystemInformation( SystemLocksInformation,
Locks,
(ULONG)RequiredLength,
(ULONG *)&RequiredLength
);
if (Status == STATUS_INFO_LENGTH_MISMATCH) {
if (Locks != &LocksBuffer) {
break;
}
Locks = BufferAlloc( &RequiredLength );
if (Locks == NULL) {
return FALSE;
}
}
else
if (!NT_SUCCESS( Status )) {
fprintf(stderr,"DH: QuerySystemInformation failed ntstatus: %08x line: %d\n",Status,__LINE__);
break;
}
else {
Buffer->Locks = Locks;
return TRUE;
}
}
if (Locks != &LocksBuffer) {
VirtualFree( Locks, 0, MEM_RELEASE );
}
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////////////////
BOOLEAN
LoadSystemObjects(
PRTL_DEBUG_INFORMATION Buffer
)
{
NTSTATUS Status;
SYSTEM_OBJECTTYPE_INFORMATION ObjectInfoBuffer;
SIZE_T RequiredLength;
ULONG i;
PSYSTEM_OBJECTTYPE_INFORMATION TypeInfo;
ObjectInformation = &ObjectInfoBuffer;
RequiredLength = sizeof( *ObjectInformation );
while (TRUE) {
Status = NtQuerySystemInformation( SystemObjectInformation,
ObjectInformation,
(ULONG)RequiredLength,
(ULONG *)&RequiredLength
);
if (Status == STATUS_INFO_LENGTH_MISMATCH) {
if (ObjectInformation != &ObjectInfoBuffer) {
return FALSE;
}
RequiredLength += 4096; // slop, since we may trigger more object creations.
ObjectInformation = BufferAlloc( &RequiredLength );
if (ObjectInformation == NULL) {
return FALSE;
}
}
else
if (!NT_SUCCESS( Status )) {
fprintf(stderr,"DH: QuerySystemInformation failed ntstatus: %08x line: %d\n",Status,__LINE__);
return FALSE;
}
else {
break;
}
}
TypeNames = RtlAllocateHeap( RtlProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof( PUNICODE_STRING ) * (MAX_TYPE_NAMES+1)
);
if (TypeNames == NULL) {
fprintf(stderr,"DH: Alloc failed for %d bytes at line %d\n",
sizeof( PUNICODE_STRING ) * (MAX_TYPE_NAMES+1) ,__LINE__);
return FALSE;
}
TypeInfo = ObjectInformation;
while (TRUE) {
if (TypeInfo->TypeIndex < MAX_TYPE_NAMES) {
TypeNames[ TypeInfo->TypeIndex ] = &TypeInfo->TypeName;
}
if (TypeInfo->NextEntryOffset == 0) {
break;
}
TypeInfo = (PSYSTEM_OBJECTTYPE_INFORMATION)
((PCHAR)ObjectInformation + TypeInfo->NextEntryOffset);
}
RtlInitUnicodeString( &UnknownTypeIndex, L"Unknown Type Index" );
for (i=0; i<=MAX_TYPE_NAMES; i++) {
if (TypeNames[ i ] == NULL) {
TypeNames[ i ] = &UnknownTypeIndex;
}
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////
BOOLEAN
LoadSystemHandles(
PRTL_DEBUG_INFORMATION Buffer
)
{
NTSTATUS Status;
SYSTEM_HANDLE_INFORMATION_EX HandleInfoBuffer;
SIZE_T RequiredLength;
PSYSTEM_OBJECTTYPE_INFORMATION TypeInfo;
PSYSTEM_OBJECT_INFORMATION ObjectInfo;
HandleInformation = &HandleInfoBuffer;
RequiredLength = sizeof( *HandleInformation );
while (TRUE) {
Status = NtQuerySystemInformation( SystemExtendedHandleInformation,
HandleInformation,
(ULONG)RequiredLength,
(ULONG *)&RequiredLength
);
if (Status == STATUS_INFO_LENGTH_MISMATCH) {
if (HandleInformation != &HandleInfoBuffer) {
return FALSE;
}
RequiredLength += 4096; // slop, since we may trigger more handle creations.
HandleInformation = (PSYSTEM_HANDLE_INFORMATION_EX)BufferAlloc( &RequiredLength );
if (HandleInformation == NULL) {
return FALSE;
}
}
else
if (!NT_SUCCESS( Status )) {
fprintf(stderr,"DH: QuerySystemInformation failed ntstatus: %08x line: %d\n",Status,__LINE__);
return FALSE;
}
else {
break;
}
}
TypeInfo = ObjectInformation;
while (TRUE) {
ObjectInfo = (PSYSTEM_OBJECT_INFORMATION)
((PCHAR)TypeInfo->TypeName.Buffer + TypeInfo->TypeName.MaximumLength);
while (TRUE) {
if (ObjectInfo->HandleCount != 0) {
PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleEntry;
ULONG HandleNumber;
HandleEntry = &HandleInformation->Handles[ 0 ];
HandleNumber = 0;
while (HandleNumber++ < HandleInformation->NumberOfHandles) {
if (!(HandleEntry->HandleAttributes & 0x80) &&
HandleEntry->Object == ObjectInfo->Object
) {
HandleEntry->Object = ObjectInfo;
HandleEntry->HandleAttributes |= 0x80;
}
HandleEntry++;
}
}
if (ObjectInfo->NextEntryOffset == 0) {
break;
}
ObjectInfo = (PSYSTEM_OBJECT_INFORMATION)
((PCHAR)ObjectInformation + ObjectInfo->NextEntryOffset);
}
if (TypeInfo->NextEntryOffset == 0) {
break;
}
TypeInfo = (PSYSTEM_OBJECTTYPE_INFORMATION)
((PCHAR)ObjectInformation + TypeInfo->NextEntryOffset);
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////
BOOLEAN
LoadSystemProcesses(
PRTL_DEBUG_INFORMATION Buffer
)
{
NTSTATUS Status;
SIZE_T RequiredLength;
ULONG i, TotalOffset;
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
PSYSTEM_THREAD_INFORMATION ThreadInfo;
PPROCESS_INFO ProcessEntry;
UCHAR NameBuffer[ MAX_PATH ];
ANSI_STRING AnsiString;
SIZE_T Size;
RequiredLength = 64 * 1024;
ProcessInformation = BufferAlloc( &RequiredLength );
if (ProcessInformation == NULL) {
return FALSE;
}
Status = NtQuerySystemInformation( SystemProcessInformation,
ProcessInformation,
(ULONG)RequiredLength,
(ULONG *)&RequiredLength
);
if (!NT_SUCCESS( Status )) {
fprintf(stderr,"DH: QuerySystemInformation failed ntstatus: %08x line: %d\n",Status,__LINE__);
return FALSE;
}
InitializeListHead( &ProcessListHead );
ProcessInfo = ProcessInformation;
TotalOffset = 0;
while (TRUE) {
if (ProcessInfo->ImageName.Buffer == NULL) {
sprintf( NameBuffer, "System Process (%p)", ProcessInfo->UniqueProcessId );
}
else {
sprintf( NameBuffer, "%wZ (%p)",
&ProcessInfo->ImageName,
ProcessInfo->UniqueProcessId
);
}
RtlInitAnsiString( &AnsiString, NameBuffer );
RtlAnsiStringToUnicodeString( &ProcessInfo->ImageName, &AnsiString, TRUE );
Size = sizeof( *ProcessEntry ) + (sizeof( ThreadInfo ) * ProcessInfo->NumberOfThreads);
ProcessEntry = RtlAllocateHeap( RtlProcessHeap(),
HEAP_ZERO_MEMORY,
Size );
if (ProcessEntry == NULL) {
fprintf(stderr,"DH: Alloc failed for %d bytes at line %d\n",Size,__LINE__);
return FALSE;
}
InitializeListHead( &ProcessEntry->Entry );
ProcessEntry->ProcessInfo = ProcessInfo;
ThreadInfo = (PSYSTEM_THREAD_INFORMATION)(ProcessInfo + 1);
for (i = 0; i < ProcessInfo->NumberOfThreads; i++) {
ProcessEntry->ThreadInfo[ i ] = ThreadInfo++;
}
InsertTailList( &ProcessListHead, &ProcessEntry->Entry );
if (ProcessInfo->NextEntryOffset == 0) {
break;
}
TotalOffset += ProcessInfo->NextEntryOffset;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
((PCHAR)ProcessInformation + TotalOffset);
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////
PSYSTEM_PROCESS_INFORMATION
FindProcessInfoForCid(
IN HANDLE UniqueProcessId
)
{
PLIST_ENTRY Next, Head;
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
PPROCESS_INFO ProcessEntry;
UCHAR NameBuffer[ 64 ];
ANSI_STRING AnsiString;
Head = &ProcessListHead;
Next = Head->Flink;
while (Next != Head) {
ProcessEntry = CONTAINING_RECORD( Next,
PROCESS_INFO,
Entry
);
ProcessInfo = ProcessEntry->ProcessInfo;
if (ProcessInfo->UniqueProcessId == UniqueProcessId) {
return ProcessInfo;
}
Next = Next->Flink;
}
ProcessEntry = RtlAllocateHeap( RtlProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof( *ProcessEntry ) +
sizeof( *ProcessInfo )
);
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)(ProcessEntry+1);
ProcessEntry->ProcessInfo = ProcessInfo;
sprintf( NameBuffer, "Unknown Process (%p)", UniqueProcessId );
RtlInitAnsiString( &AnsiString, NameBuffer );
RtlAnsiStringToUnicodeString( (PUNICODE_STRING)&ProcessInfo->ImageName, &AnsiString, TRUE );
ProcessInfo->UniqueProcessId = UniqueProcessId;
InitializeListHead( &ProcessEntry->Entry );
InsertTailList( &ProcessListHead, &ProcessEntry->Entry );
return ProcessInfo;
}
////////////////////////////////////////////////////////////////////////////////////////////
VOID
DumpSystemThread(
PSYSTEM_THREAD_INFORMATION ThreadInfo
)
{
UCHAR Buffer[ MAX_PATH ];
Buffer[ 0 ] = '\0';
GetDhSymbolicNameForAddress( NULL, (ULONG_PTR)ThreadInfo->StartAddress, Buffer, sizeof( Buffer ) );
sprintf( DumpLine, " Thread Id: %p Start Address: %p (%s)\n",
ThreadInfo->ClientId.UniqueThread,
ThreadInfo->StartAddress,
Buffer
);
DumpOutputString();
return;
}
////////////////////////////////////////////////////////////////////////////////////////////
VOID
DumpSystemProcess(
PPROCESS_INFO ProcessEntry
)
{
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
ULONG ThreadNumber;
ProcessInfo = ProcessEntry->ProcessInfo;
sprintf( DumpLine, "\n\n*********** %p (%wZ) Information ********************\n\n",
ProcessInfo->UniqueProcessId,
&ProcessInfo->ImageName
);
DumpOutputString();
if (ProcessInfo->InheritedFromUniqueProcessId) {
sprintf( DumpLine, " Parent Process: %p (%wZ)\n",
ProcessInfo->InheritedFromUniqueProcessId,
&(FindProcessInfoForCid( ProcessInfo->InheritedFromUniqueProcessId )->ImageName)
);
DumpOutputString();
}
sprintf( DumpLine, " BasePriority: %u\n",
ProcessInfo->BasePriority
);
DumpOutputString();
sprintf( DumpLine, " VirtualSize: %08x\n",
ProcessInfo->VirtualSize
);
DumpOutputString();
sprintf( DumpLine, " PeakVirtualSize: %08x\n",
ProcessInfo->PeakVirtualSize
);
DumpOutputString();
sprintf( DumpLine, " WorkingSetSize: %08x\n",
ProcessInfo->WorkingSetSize
);
DumpOutputString();
sprintf( DumpLine, " PeakWorkingSetSize: %08x\n",
ProcessInfo->PeakWorkingSetSize
);
DumpOutputString();
sprintf( DumpLine, " PagefileUsage: %08x\n",
ProcessInfo->PagefileUsage
);
DumpOutputString();
sprintf( DumpLine, " PeakPagefileUsage: %08x\n",
ProcessInfo->PeakPagefileUsage
);
DumpOutputString();
sprintf( DumpLine, " PageFaultCount: %08x\n",
ProcessInfo->PageFaultCount
);
DumpOutputString();
sprintf( DumpLine, " PrivatePageCount: %08x\n",
ProcessInfo->PrivatePageCount
);
DumpOutputString();
sprintf( DumpLine, " Number of Threads: %u\n",
ProcessInfo->NumberOfThreads
);
DumpOutputString();
for (ThreadNumber=0; ThreadNumber<ProcessInfo->NumberOfThreads; ThreadNumber++) {
DumpSystemThread( ProcessEntry->ThreadInfo[ ThreadNumber ] );
}
return;
}
////////////////////////////////////////////////////////////////////////////////////////////
VOID
DumpSystemProcesses( VOID )
{
PLIST_ENTRY Next, Head;
PPROCESS_INFO ProcessEntry;
if (fVerbose) {
fprintf( stderr, "DH: Dumping object information.\n" );
}
sprintf( DumpLine, "\n\n*********** Process Information ********************\n\n" );
DumpOutputString();
Head = &ProcessListHead;
Next = Head->Flink;
while (Next != Head) {
ProcessEntry = CONTAINING_RECORD( Next,
PROCESS_INFO,
Entry
);
DumpSystemProcess( ProcessEntry );
Next = Next->Flink;
}
return;
}
////////////////////////////////////////////////////////////////////////////////////////////
VOID
DumpObjects( VOID )
{
PSYSTEM_OBJECTTYPE_INFORMATION TypeInfo;
PSYSTEM_OBJECT_INFORMATION ObjectInfo;
PRTL_PROCESS_BACKTRACE_INFORMATION BackTraceInfo;
UNICODE_STRING ObjectName;
PUCHAR s;
if (fVerbose) {
fprintf( stderr, "DH: Dumping object information.\n" );
}
sprintf( DumpLine, "\n\n*********** Object Information ********************\n\n" );
DumpOutputString();
TypeInfo = ObjectInformation;
while (TRUE) {
sprintf( DumpLine, "\n\n*********** %wZ Object Type ********************\n\n",
&TypeInfo->TypeName
);
DumpOutputString();
sprintf( DumpLine, " NumberOfObjects: %u\n", TypeInfo->NumberOfObjects );
DumpOutputString();
ObjectInfo = (PSYSTEM_OBJECT_INFORMATION)
((PCHAR)TypeInfo->TypeName.Buffer + TypeInfo->TypeName.MaximumLength);
while (TRUE) {
ObjectName = ObjectInfo->NameInfo.Name;
try {
if (ObjectName.Length != 0 && *ObjectName.Buffer == UNICODE_NULL) {
ObjectName.Length = 0;
}
sprintf( DumpLine, " Object: %p Name: %wZ Creator: %wZ (Backtrace%05lu)\n",
ObjectInfo->Object,
&ObjectName,
&(FindProcessInfoForCid( ObjectInfo->CreatorUniqueProcess )->ImageName),
ObjectInfo->CreatorBackTraceIndex
);
}
except( EXCEPTION_EXECUTE_HANDLER ) {
sprintf( DumpLine, " Object: %p Name: [%04x, %04x, %p]\n",
ObjectInfo->Object,
ObjectName.MaximumLength,
ObjectName.Length,
ObjectName.Buffer
);
}
DumpOutputString();
BackTraceInfo = FindBackTrace( ObjectInfo->CreatorBackTraceIndex );
if (BackTraceInfo != NULL && (s = BackTraceInfo->SymbolicBackTrace)) {
while (*s) {
sprintf( DumpLine, " %s\n", s );
DumpOutputString();
while (*s++) {
}
}
}
s = DumpLine;
s += sprintf( s, "\n PointerCount: %u HandleCount: %u",
ObjectInfo->PointerCount,
ObjectInfo->HandleCount
);
if (ObjectInfo->SecurityDescriptor != NULL) {
s += sprintf( s, " Security: %p", ObjectInfo->SecurityDescriptor );
}
if (ObjectInfo->ExclusiveProcessId) {
s += sprintf( s, " Exclusive by Process: %p", ObjectInfo->ExclusiveProcessId );
}
s += sprintf( s, " Flags: %02x", ObjectInfo->Flags );
if (ObjectInfo->Flags & OB_FLAG_NEW_OBJECT) {
s += sprintf( s, " New" );
}
if (ObjectInfo->Flags & OB_FLAG_KERNEL_OBJECT) {
s += sprintf( s, " KernelMode" );
}
if (ObjectInfo->Flags & OB_FLAG_PERMANENT_OBJECT) {
s += sprintf( s, " Permanent" );
}
if (ObjectInfo->Flags & OB_FLAG_DEFAULT_SECURITY_QUOTA) {
s += sprintf( s, " DefaultSecurityQuota" );
}
if (ObjectInfo->Flags & OB_FLAG_SINGLE_HANDLE_ENTRY) {
s += sprintf( s, " Single Handle Entry" );
}
s += sprintf( s, "\n" );
DumpOutputString();
if (ObjectInfo->HandleCount != 0) {
PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleEntry;
ULONG HandleNumber;
HandleEntry = &HandleInformation->Handles[ 0 ];
HandleNumber = 0;
while (HandleNumber++ < HandleInformation->NumberOfHandles) {
if (((HandleEntry->HandleAttributes & 0x80) && HandleEntry->Object == ObjectInfo) ||
(!(HandleEntry->HandleAttributes & 0x80) && HandleEntry->Object == ObjectInfo->Object)
) {
sprintf( DumpLine, " Handle: %08lx Access:%08lx Process: %wZ\n",
HandleEntry->HandleValue,
HandleEntry->GrantedAccess,
&(FindProcessInfoForCid( (HANDLE)HandleEntry->UniqueProcessId )->ImageName)
);
DumpOutputString();
}
HandleEntry++;
}
}
sprintf( DumpLine, "\n" );
DumpOutputString();
if (ObjectInfo->NextEntryOffset == 0) {
break;
}
ObjectInfo = (PSYSTEM_OBJECT_INFORMATION)
((PCHAR)ObjectInformation + ObjectInfo->NextEntryOffset);
}
if (TypeInfo->NextEntryOffset == 0) {
break;
}
TypeInfo = (PSYSTEM_OBJECTTYPE_INFORMATION)
((PCHAR)ObjectInformation + TypeInfo->NextEntryOffset);
}
return;
}
////////////////////////////////////////////////////////////////////////////////////////////
VOID
DumpHandles( VOID )
{
PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleEntry;
HANDLE PreviousUniqueProcessId;
ULONG HandleNumber;
PRTL_PROCESS_BACKTRACE_INFORMATION BackTraceInfo;
PSYSTEM_OBJECT_INFORMATION ObjectInfo;
PVOID Object;
PUCHAR s;
if (fVerbose) {
fprintf( stderr, "DH: Dumping handle information.\n" );
}
sprintf( DumpLine, "\n\n*********** Object Handle Information ********************\n\n" );
DumpOutputString();
sprintf( DumpLine, "Number of handles: %u\n", HandleInformation->NumberOfHandles );
DumpOutputString();
HandleEntry = &HandleInformation->Handles[ 0 ];
HandleNumber = 0;
PreviousUniqueProcessId = INVALID_HANDLE_VALUE;
while (HandleNumber++ < HandleInformation->NumberOfHandles) {
if (PreviousUniqueProcessId != (HANDLE)HandleEntry->UniqueProcessId) {
PreviousUniqueProcessId = (HANDLE)HandleEntry->UniqueProcessId;
sprintf( DumpLine, "\n\n*********** Handles for %wZ ********************\n\n",
&(FindProcessInfoForCid( PreviousUniqueProcessId )->ImageName)
);
DumpOutputString();
}
if (HandleEntry->HandleAttributes & 0x80) {
ObjectInfo = HandleEntry->Object;
Object = ObjectInfo->Object;
}
else {
ObjectInfo = NULL;
Object = HandleEntry->Object;
}
sprintf( DumpLine, " Handle: %08lx%c Type: %wZ Object: %p Access: %08lx\n",
HandleEntry->HandleValue,
HandleEntry->HandleAttributes & OBJ_INHERIT ? 'i' : ' ',
TypeNames[ HandleEntry->ObjectTypeIndex < MAX_TYPE_NAMES ? HandleEntry->ObjectTypeIndex : MAX_TYPE_NAMES ],
Object,
HandleEntry->GrantedAccess
);
DumpOutputString();
if (ObjectInfo != NULL) {
UNICODE_STRING ObjectName;
ObjectName = ObjectInfo->NameInfo.Name;
try {
if (ObjectName.Length != 0 && *ObjectName.Buffer == UNICODE_NULL) {
ObjectName.Length = 0;
}
sprintf( DumpLine, " Name: %wZ\n",
&ObjectName
);
}
except( EXCEPTION_EXECUTE_HANDLER ) {
sprintf( DumpLine, " Name: [%04x, %04x, %p]\n",
ObjectName.MaximumLength,
ObjectName.Length,
ObjectName.Buffer
);
}
DumpOutputString();
}
if (HandleEntry->CreatorBackTraceIndex != 0) {
sprintf( DumpLine, " Creator: (Backtrace%05lu)\n", HandleEntry->CreatorBackTraceIndex );
DumpOutputString();
BackTraceInfo = FindBackTrace( HandleEntry->CreatorBackTraceIndex );
if (BackTraceInfo != NULL && (s = BackTraceInfo->SymbolicBackTrace)) {
while (*s) {
sprintf( DumpLine, " %s\n", s );
DumpOutputString();
while (*s++) {
}
}
}
}
sprintf( DumpLine, " \n" );
DumpOutputString();
HandleEntry++;
}
return;
}
////////////////////////////////////////////////////////////////////////////////////////////
ULONG
GetDhSymbolicNameForAddress(
IN HANDLE UniqueProcess,
IN ULONG_PTR Address,
OUT LPSTR Name,
IN ULONG MaxNameLength
)
{
IMAGEHLP_MODULE ModuleInfo;
ULONG i, ModuleNameLength, Result;
ULONG_PTR Offset;
LPSTR s;
ModuleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE);
ModuleNameLength = 0;
if (SymGetModuleInfo( UniqueProcess, Address, &ModuleInfo )) {
ModuleNameLength = strlen( ModuleInfo.ModuleName );
}
if (SymGetSymFromAddr( UniqueProcess, Address, &Offset, sym )) {
s = sym->Name;
i = 1;
while ( sym->MaxNameLength > i &&
isdigit( s[ sym->MaxNameLength - i ] )
) {
i += 1;
}
if (s[ sym->MaxNameLength - i ] == '@') {
sym->MaxNameLength = sym->MaxNameLength - i;
}
s = Name;
Result = _snprintf( s, MaxNameLength,
"%.*s!%s",
ModuleNameLength,
ModuleInfo.ModuleName,
sym->Name
);
if (Offset != 0) {
Result += _snprintf( s + Result, MaxNameLength - Result, "+0x%x", Offset );
}
}
else {
if (ModuleNameLength != 0) {
Result = _snprintf( Name, MaxNameLength,
"%.*s!0x%08x",
ModuleNameLength,
ModuleInfo.ModuleName,
Address
);
}
else {
Result = _snprintf( Name, MaxNameLength, "0x%08x", Address );
}
}
return Result;
}