Files
dmex c62006afad Patch by dmex: Fixed 4996 warnings (Enabled usage of __declspec(deprecated)); Added Debug folder to SVN:ignore list;
git-svn-id: svn://svn.code.sf.net/p/processhacker/code@6287 21ef857c-d57f-4fe0-8362-d861dc6d29cd
2016-01-15 04:29:55 +00:00

1690 lines
53 KiB
C

/*
* Process Hacker -
* debug console
*
* Copyright (C) 2010-2011 wj32
*
* This file is part of Process Hacker.
*
* Process Hacker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Process Hacker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* This is a simple debugging console which is able to explore phlib's
* systems easily. Commands are provided to debug reference counting
* problems and memory usage, as well as to do general performance testing.
*/
#include <phapp.h>
#include <phintrnl.h>
#include <symprv.h>
#include <refp.h>
typedef struct _STRING_TABLE_ENTRY
{
PPH_STRING String;
ULONG_PTR Count;
} STRING_TABLE_ENTRY, *PSTRING_TABLE_ENTRY;
BOOL ConsoleHandlerRoutine(
_In_ DWORD dwCtrlType
);
VOID PhpPrintHashtableStatistics(
_In_ PPH_HASHTABLE Hashtable
);
NTSTATUS PhpDebugConsoleThreadStart(
_In_ PVOID Parameter
);
extern PH_FREE_LIST PhObjectSmallFreeList;
static HANDLE DebugConsoleThreadHandle;
static PPH_SYMBOL_PROVIDER DebugConsoleSymbolProvider;
static PPH_HASHTABLE ObjectListSnapshot = NULL;
#ifdef DEBUG
static PPH_LIST NewObjectList = NULL;
static PH_QUEUED_LOCK NewObjectListLock;
#endif
static BOOLEAN ShowAllLeaks = FALSE;
static BOOLEAN InLeakDetection = FALSE;
static ULONG NumberOfLeaks;
static ULONG NumberOfLeaksShown;
VOID PhShowDebugConsole(
VOID
)
{
if (AllocConsole())
{
HMENU menu;
// Disable the close button because it's impossible to handle
// those events.
menu = GetSystemMenu(GetConsoleWindow(), FALSE);
EnableMenuItem(menu, SC_CLOSE, MF_GRAYED | MF_DISABLED);
DeleteMenu(menu, SC_CLOSE, 0);
// Set a handler so we can catch Ctrl+C and Ctrl+Break.
SetConsoleCtrlHandler(ConsoleHandlerRoutine, TRUE);
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
freopen("CONIN$", "r", stdin);
DebugConsoleThreadHandle = PhCreateThread(0, PhpDebugConsoleThreadStart, NULL);
}
else
{
HWND consoleWindow;
consoleWindow = GetConsoleWindow();
// Console window already exists, so bring it to the top.
if (IsIconic(consoleWindow))
ShowWindow(consoleWindow, SW_RESTORE);
else
BringWindowToTop(consoleWindow);
return;
}
}
VOID PhCloseDebugConsole(
VOID
)
{
freopen("NUL", "w", stdout);
freopen("NUL", "w", stderr);
freopen("NUL", "r", stdin);
FreeConsole();
}
static BOOL ConsoleHandlerRoutine(
_In_ DWORD dwCtrlType
)
{
switch (dwCtrlType)
{
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_CLOSE_EVENT:
PhCloseDebugConsole();
return TRUE;
}
return FALSE;
}
static BOOLEAN NTAPI PhpLoadCurrentProcessSymbolsCallback(
_In_ PPH_MODULE_INFO Module,
_In_opt_ PVOID Context
)
{
PhLoadModuleSymbolProvider((PPH_SYMBOL_PROVIDER)Context, Module->FileName->Buffer,
(ULONG64)Module->BaseAddress, Module->Size);
return TRUE;
}
static PWSTR PhpGetSymbolForAddress(
_In_ PVOID Address
)
{
return ((PPH_STRING)PhAutoDereferenceObject(PhGetSymbolFromAddress(
DebugConsoleSymbolProvider, (ULONG64)Address, NULL, NULL, NULL, NULL
)))->Buffer;
}
static VOID PhpPrintObjectInfo(
_In_ PPH_OBJECT_HEADER ObjectHeader,
_In_ LONG RefToSubtract
)
{
PVOID object;
PPH_OBJECT_TYPE objectType;
WCHAR c = ' ';
object = PhObjectHeaderToObject(ObjectHeader);
wprintf(L"%Ix", (ULONG_PTR)object);
objectType = PhGetObjectType(object);
wprintf(L"\t% 20s", objectType->Name);
if (ObjectHeader->Flags & PH_OBJECT_FROM_SMALL_FREE_LIST)
c = 'f';
else if (ObjectHeader->Flags & PH_OBJECT_FROM_TYPE_FREE_LIST)
c = 'F';
wprintf(L"\t%4d %c", ObjectHeader->RefCount - RefToSubtract, c);
if (!objectType)
{
// Dummy
}
else if (objectType == PhObjectTypeObject)
{
wprintf(L"\t%.32s", ((PPH_OBJECT_TYPE)object)->Name);
}
else if (objectType == PhStringType)
{
wprintf(L"\t%.32s", ((PPH_STRING)object)->Buffer);
}
else if (objectType == PhBytesType)
{
wprintf(L"\t%.32S", ((PPH_BYTES)object)->Buffer);
}
else if (objectType == PhListType)
{
wprintf(L"\tCount: %u", ((PPH_LIST)object)->Count);
}
else if (objectType == PhPointerListType)
{
wprintf(L"\tCount: %u", ((PPH_POINTER_LIST)object)->Count);
}
else if (objectType == PhHashtableType)
{
wprintf(L"\tCount: %u", ((PPH_HASHTABLE)object)->Count);
}
else if (objectType == PhProcessItemType)
{
wprintf(
L"\t%.28s (%d)",
((PPH_PROCESS_ITEM)object)->ProcessName->Buffer,
HandleToLong(((PPH_PROCESS_ITEM)object)->ProcessId)
);
}
else if (objectType == PhServiceItemType)
{
wprintf(L"\t%s", ((PPH_SERVICE_ITEM)object)->Name->Buffer);
}
else if (objectType == PhThreadItemType)
{
wprintf(L"\tTID: %u", HandleToUlong(((PPH_THREAD_ITEM)object)->ThreadId));
}
wprintf(L"\n");
}
static VOID PhpDumpObjectInfo(
_In_ PPH_OBJECT_HEADER ObjectHeader
)
{
PVOID object;
PPH_OBJECT_TYPE objectType;
object = PhObjectHeaderToObject(ObjectHeader);
objectType = PhGetObjectType(object);
__try
{
wprintf(L"Type: %s\n", objectType->Name);
wprintf(L"Reference count: %d\n", ObjectHeader->RefCount);
wprintf(L"Flags: %x\n", ObjectHeader->Flags);
if (objectType == PhObjectTypeObject)
{
wprintf(L"Name: %s\n", ((PPH_OBJECT_TYPE)object)->Name);
wprintf(L"Number of objects: %u\n", ((PPH_OBJECT_TYPE)object)->NumberOfObjects);
wprintf(L"Flags: %u\n", ((PPH_OBJECT_TYPE)object)->Flags);
wprintf(L"Type index: %u\n", ((PPH_OBJECT_TYPE)object)->TypeIndex);
wprintf(L"Free list count: %u\n", ((PPH_OBJECT_TYPE)object)->FreeList.Count);
}
else if (objectType == PhStringType)
{
wprintf(L"%s\n", ((PPH_STRING)object)->Buffer);
}
else if (objectType == PhBytesType)
{
wprintf(L"%S\n", ((PPH_BYTES)object)->Buffer);
}
else if (objectType == PhHashtableType)
{
PhpPrintHashtableStatistics((PPH_HASHTABLE)object);
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
wprintf(L"Error.\n");
}
}
static VOID PhpPrintHashtableStatistics(
_In_ PPH_HASHTABLE Hashtable
)
{
ULONG i;
ULONG expectedLookupMisses = 0;
wprintf(L"Count: %u\n", Hashtable->Count);
wprintf(L"Allocated buckets: %u\n", Hashtable->AllocatedBuckets);
wprintf(L"Allocated entries: %u\n", Hashtable->AllocatedEntries);
wprintf(L"Next free entry: %d\n", Hashtable->FreeEntry);
wprintf(L"Next usable entry: %d\n", Hashtable->NextEntry);
wprintf(L"Hash function: %s\n", PhpGetSymbolForAddress(Hashtable->HashFunction));
wprintf(L"Compare function: %s\n", PhpGetSymbolForAddress(Hashtable->CompareFunction));
wprintf(L"\nBuckets:\n");
for (i = 0; i < Hashtable->AllocatedBuckets; i++)
{
ULONG index;
ULONG count = 0;
// Count the number of entries in this bucket.
index = Hashtable->Buckets[i];
while (index != -1)
{
index = PH_HASHTABLE_GET_ENTRY(Hashtable, index)->Next;
count++;
}
if (count != 0)
{
expectedLookupMisses += count - 1;
}
if (count != 0)
{
wprintf(L"%lu: ", i);
// Print out the entry indicies.
index = Hashtable->Buckets[i];
while (index != -1)
{
wprintf(L"%lu", index);
index = PH_HASHTABLE_GET_ENTRY(Hashtable, index)->Next;
count--;
if (count != 0)
wprintf(L", ");
}
wprintf(L"\n");
}
else
{
//wprintf(L"%u: (empty)\n");
}
}
wprintf(L"\nExpected lookup misses: %lu\n", expectedLookupMisses);
}
#ifdef DEBUG
static VOID PhpDebugCreateObjectHook(
_In_ PVOID Object,
_In_ SIZE_T Size,
_In_ ULONG Flags,
_In_ PPH_OBJECT_TYPE ObjectType
)
{
PhAcquireQueuedLockExclusive(&NewObjectListLock);
if (NewObjectList)
{
PhReferenceObject(Object);
PhAddItemList(NewObjectList, Object);
}
PhReleaseQueuedLockExclusive(&NewObjectListLock);
}
#endif
#ifdef DEBUG
static VOID PhpDeleteNewObjectList(
VOID
)
{
if (NewObjectList)
{
ULONG i;
for (i = 0; i < NewObjectList->Count; i++)
PhDereferenceObject(NewObjectList->Items[i]);
PhDereferenceObject(NewObjectList);
NewObjectList = NULL;
}
}
#endif
static BOOLEAN PhpStringHashtableCompareFunction(
_In_ PVOID Entry1,
_In_ PVOID Entry2
)
{
PSTRING_TABLE_ENTRY entry1 = Entry1;
PSTRING_TABLE_ENTRY entry2 = Entry2;
return PhEqualString(entry1->String, entry2->String, FALSE);
}
static ULONG PhpStringHashtableHashFunction(
_In_ PVOID Entry
)
{
PSTRING_TABLE_ENTRY entry = Entry;
return PhHashBytes((PUCHAR)entry->String->Buffer, entry->String->Length);
}
static int __cdecl PhpStringEntryCompareByCount(
_In_ const void *elem1,
_In_ const void *elem2
)
{
PSTRING_TABLE_ENTRY entry1 = *(PSTRING_TABLE_ENTRY *)elem1;
PSTRING_TABLE_ENTRY entry2 = *(PSTRING_TABLE_ENTRY *)elem2;
return uintptrcmp(entry2->Count, entry1->Count);
}
static NTSTATUS PhpLeakEnumerationRoutine(
_In_ LONG Reserved,
_In_ PVOID HeapHandle,
_In_ PVOID BaseAddress,
_In_ SIZE_T BlockSize,
_In_ ULONG StackTraceDepth,
_In_ PVOID *StackTrace
)
{
ULONG i;
if (!InLeakDetection)
return 0;
if (!HeapHandle) // means no more entries
return 0;
if (ShowAllLeaks || HeapHandle == PhHeapHandle)
{
wprintf(L"Leak at 0x%Ix (%Iu bytes). Stack trace:\n", (ULONG_PTR)BaseAddress, BlockSize);
for (i = 0; i < StackTraceDepth; i++)
{
PPH_STRING symbol;
symbol = PhGetSymbolFromAddress(DebugConsoleSymbolProvider, (ULONG64)StackTrace[i], NULL, NULL, NULL, NULL);
if (symbol)
wprintf(L"\t%s\n", symbol->Buffer);
else
wprintf(L"\t?\n");
PhDereferenceObject(symbol);
}
NumberOfLeaksShown++;
}
NumberOfLeaks++;
return 0;
}
typedef struct _STOPWATCH
{
LARGE_INTEGER StartCounter;
LARGE_INTEGER EndCounter;
LARGE_INTEGER Frequency;
} STOPWATCH, *PSTOPWATCH;
static VOID PhInitializeStopwatch(
_Out_ PSTOPWATCH Stopwatch
)
{
Stopwatch->StartCounter.QuadPart = 0;
Stopwatch->EndCounter.QuadPart = 0;
}
static VOID PhStartStopwatch(
_Inout_ PSTOPWATCH Stopwatch
)
{
NtQueryPerformanceCounter(&Stopwatch->StartCounter, &Stopwatch->Frequency);
}
static VOID PhStopStopwatch(
_Inout_ PSTOPWATCH Stopwatch
)
{
NtQueryPerformanceCounter(&Stopwatch->EndCounter, NULL);
}
static ULONG PhGetMillisecondsStopwatch(
_In_ PSTOPWATCH Stopwatch
)
{
LARGE_INTEGER countsPerMs;
countsPerMs = Stopwatch->Frequency;
countsPerMs.QuadPart /= 1000;
return (ULONG)((Stopwatch->EndCounter.QuadPart - Stopwatch->StartCounter.QuadPart) /
countsPerMs.QuadPart);
}
typedef VOID (FASTCALL *PPHF_RW_LOCK_FUNCTION)(
_In_ PVOID Parameter
);
typedef struct _RW_TEST_CONTEXT
{
PWSTR Name;
PPHF_RW_LOCK_FUNCTION AcquireExclusive;
PPHF_RW_LOCK_FUNCTION AcquireShared;
PPHF_RW_LOCK_FUNCTION ReleaseExclusive;
PPHF_RW_LOCK_FUNCTION ReleaseShared;
PVOID Parameter;
} RW_TEST_CONTEXT, *PRW_TEST_CONTEXT;
static PH_BARRIER RwStartBarrier;
static LONG RwReadersActive;
static LONG RwWritersActive;
static NTSTATUS PhpRwLockTestThreadStart(
_In_ PVOID Parameter
)
{
#define RW_ITERS 10000
#define RW_READ_ITERS 100
#define RW_WRITE_ITERS 10
#define RW_READ_SPIN_ITERS 60
#define RW_WRITE_SPIN_ITERS 200
RW_TEST_CONTEXT context = *(PRW_TEST_CONTEXT)Parameter;
ULONG i;
ULONG j;
ULONG k;
ULONG m;
PhWaitForBarrier(&RwStartBarrier, FALSE);
for (i = 0; i < RW_ITERS; i++)
{
for (j = 0; j < RW_READ_ITERS; j++)
{
// Read zone
context.AcquireShared(context.Parameter);
_InterlockedIncrement(&RwReadersActive);
for (m = 0; m < RW_READ_SPIN_ITERS; m++)
YieldProcessor();
if (RwWritersActive != 0)
{
wprintf(L"[fail]: writers active in read zone!\n");
NtWaitForSingleObject(NtCurrentProcess(), FALSE, NULL);
}
_InterlockedDecrement(&RwReadersActive);
context.ReleaseShared(context.Parameter);
// Spin for a while
for (m = 0; m < 10; m++)
YieldProcessor();
if (j == RW_READ_ITERS / 2)
{
// Write zone
for (k = 0; k < RW_WRITE_ITERS; k++)
{
context.AcquireExclusive(context.Parameter);
_InterlockedIncrement(&RwWritersActive);
for (m = 0; m < RW_WRITE_SPIN_ITERS; m++)
YieldProcessor();
if (RwReadersActive != 0)
{
wprintf(L"[fail]: readers active in write zone!\n");
NtWaitForSingleObject(NtCurrentProcess(), FALSE, NULL);
}
_InterlockedDecrement(&RwWritersActive);
context.ReleaseExclusive(context.Parameter);
}
}
}
}
return STATUS_SUCCESS;
}
static VOID PhpTestRwLock(
_In_ PRW_TEST_CONTEXT Context
)
{
#define RW_PROCESSORS 4
STOPWATCH stopwatch;
ULONG i;
HANDLE threadHandles[RW_PROCESSORS];
// Dummy
Context->AcquireExclusive(Context->Parameter);
Context->ReleaseExclusive(Context->Parameter);
Context->AcquireShared(Context->Parameter);
Context->ReleaseShared(Context->Parameter);
// Null test
PhStartStopwatch(&stopwatch);
for (i = 0; i < 2000000; i++)
{
Context->AcquireExclusive(Context->Parameter);
Context->ReleaseExclusive(Context->Parameter);
Context->AcquireShared(Context->Parameter);
Context->ReleaseShared(Context->Parameter);
}
PhStopStopwatch(&stopwatch);
wprintf(L"[null] %s: %ums\n", Context->Name, PhGetMillisecondsStopwatch(&stopwatch));
// Stress test
PhInitializeBarrier(&RwStartBarrier, RW_PROCESSORS + 1);
RwReadersActive = 0;
RwWritersActive = 0;
for (i = 0; i < RW_PROCESSORS; i++)
{
threadHandles[i] = PhCreateThread(0, PhpRwLockTestThreadStart, Context);
}
PhWaitForBarrier(&RwStartBarrier, FALSE);
PhStartStopwatch(&stopwatch);
NtWaitForMultipleObjects(RW_PROCESSORS, threadHandles, WaitAll, FALSE, NULL);
PhStopStopwatch(&stopwatch);
for (i = 0; i < RW_PROCESSORS; i++)
NtClose(threadHandles[i]);
wprintf(L"[strs] %s: %ums\n", Context->Name, PhGetMillisecondsStopwatch(&stopwatch));
}
VOID FASTCALL PhfAcquireCriticalSection(
_In_ PRTL_CRITICAL_SECTION CriticalSection
)
{
RtlEnterCriticalSection(CriticalSection);
}
VOID FASTCALL PhfReleaseCriticalSection(
_In_ PRTL_CRITICAL_SECTION CriticalSection
)
{
RtlLeaveCriticalSection(CriticalSection);
}
VOID FASTCALL PhfReleaseQueuedLockExclusiveUsingInline(
_In_ PPH_QUEUED_LOCK QueuedLock
)
{
PhReleaseQueuedLockExclusive(QueuedLock);
}
NTSTATUS PhpDebugConsoleThreadStart(
_In_ PVOID Parameter
)
{
PH_AUTO_POOL autoPool;
BOOLEAN exit = FALSE;
PhInitializeAutoPool(&autoPool);
DebugConsoleSymbolProvider = PhCreateSymbolProvider(NtCurrentProcessId());
{
WCHAR buffer[512];
UNICODE_STRING name = RTL_CONSTANT_STRING(L"_NT_SYMBOL_PATH");
UNICODE_STRING var;
PPH_STRING newSearchPath;
var.Buffer = buffer;
var.MaximumLength = sizeof(buffer);
if (!NT_SUCCESS(RtlQueryEnvironmentVariable_U(NULL, &name, &var)))
buffer[0] = 0;
newSearchPath = PhFormatString(L"%s;%s", buffer, PhApplicationDirectory->Buffer);
PhSetSearchPathSymbolProvider(DebugConsoleSymbolProvider, newSearchPath->Buffer);
PhDereferenceObject(newSearchPath);
}
PhEnumGenericModules(NtCurrentProcessId(), NtCurrentProcess(),
0, PhpLoadCurrentProcessSymbolsCallback, DebugConsoleSymbolProvider);
#ifdef DEBUG
PhInitializeQueuedLock(&NewObjectListLock);
PhDbgCreateObjectHook = PhpDebugCreateObjectHook;
#endif
wprintf(L"Press Ctrl+C or type \"exit\" to close the debug console. Type \"help\" for a list of commands.\n");
while (!exit)
{
static PWSTR delims = L" \t";
static PWSTR commandDebugOnly = L"This command is not available on non-debug builds.\n";
WCHAR line[201];
ULONG inputLength;
PWSTR context;
PWSTR command;
wprintf(L"dbg>");
if (!fgetws(line, sizeof(line) / 2 - 1, stdin))
break;
// Remove the terminating new line character.
inputLength = (ULONG)PhCountStringZ(line);
if (inputLength != 0)
line[inputLength - 1] = 0;
context = NULL;
command = wcstok_s(line, delims, &context);
if (!command)
{
continue;
}
else if (PhEqualStringZ(command, L"help", TRUE))
{
wprintf(
L"Commands:\n"
L"exit\n"
L"testperf\n"
L"testlocks\n"
L"stats\n"
L"objects [type-name-filter]\n"
L"objtrace object-address\n"
L"objmksnap\n"
L"objcmpsnap\n"
L"objmknew\n"
L"objdelnew\n"
L"objviewnew\n"
L"dumpobj\n"
L"dumpautopool\n"
L"threads\n"
L"provthreads\n"
L"workqueues\n"
L"procrecords\n"
L"procitem\n"
L"uniquestr\n"
L"enableleakdetect\n"
L"leakdetect\n"
L"mem\n"
);
}
else if (PhEqualStringZ(command, L"exit", TRUE))
{
PhCloseDebugConsole();
exit = TRUE;
}
else if (PhEqualStringZ(command, L"testperf", TRUE))
{
STOPWATCH stopwatch;
ULONG i;
PPH_STRING testString;
RTL_CRITICAL_SECTION testCriticalSection;
PH_FAST_LOCK testFastLock;
PH_QUEUED_LOCK testQueuedLock;
// Control (string reference counting)
testString = PhCreateString(L"");
PhReferenceObject(testString);
PhDereferenceObject(testString);
PhStartStopwatch(&stopwatch);
for (i = 0; i < 10000000; i++)
{
PhReferenceObject(testString);
PhDereferenceObject(testString);
}
PhStopStopwatch(&stopwatch);
PhDereferenceObject(testString);
wprintf(L"Referencing: %ums\n", PhGetMillisecondsStopwatch(&stopwatch));
// Critical section
RtlInitializeCriticalSection(&testCriticalSection);
RtlEnterCriticalSection(&testCriticalSection);
RtlLeaveCriticalSection(&testCriticalSection);
PhStartStopwatch(&stopwatch);
for (i = 0; i < 10000000; i++)
{
RtlEnterCriticalSection(&testCriticalSection);
RtlLeaveCriticalSection(&testCriticalSection);
}
PhStopStopwatch(&stopwatch);
RtlDeleteCriticalSection(&testCriticalSection);
wprintf(L"Critical section: %ums\n", PhGetMillisecondsStopwatch(&stopwatch));
// Fast lock
PhInitializeFastLock(&testFastLock);
PhAcquireFastLockExclusive(&testFastLock);
PhReleaseFastLockExclusive(&testFastLock);
PhStartStopwatch(&stopwatch);
for (i = 0; i < 10000000; i++)
{
PhAcquireFastLockExclusive(&testFastLock);
PhReleaseFastLockExclusive(&testFastLock);
}
PhStopStopwatch(&stopwatch);
PhDeleteFastLock(&testFastLock);
wprintf(L"Fast lock: %ums\n", PhGetMillisecondsStopwatch(&stopwatch));
// Queued lock
PhInitializeQueuedLock(&testQueuedLock);
PhAcquireQueuedLockExclusive(&testQueuedLock);
PhReleaseQueuedLockExclusive(&testQueuedLock);
PhStartStopwatch(&stopwatch);
for (i = 0; i < 10000000; i++)
{
PhAcquireQueuedLockExclusive(&testQueuedLock);
PhReleaseQueuedLockExclusive(&testQueuedLock);
}
PhStopStopwatch(&stopwatch);
wprintf(L"Queued lock: %ums\n", PhGetMillisecondsStopwatch(&stopwatch));
}
else if (PhEqualStringZ(command, L"testlocks", TRUE))
{
RW_TEST_CONTEXT testContext;
PH_FAST_LOCK fastLock;
PH_QUEUED_LOCK queuedLock;
RTL_CRITICAL_SECTION criticalSection;
testContext.Name = L"FastLock";
testContext.AcquireExclusive = PhfAcquireFastLockExclusive;
testContext.AcquireShared = PhfAcquireFastLockShared;
testContext.ReleaseExclusive = PhfReleaseFastLockExclusive;
testContext.ReleaseShared = PhfReleaseFastLockShared;
testContext.Parameter = &fastLock;
PhInitializeFastLock(&fastLock);
PhpTestRwLock(&testContext);
PhDeleteFastLock(&fastLock);
testContext.Name = L"QueuedLock";
testContext.AcquireExclusive = PhfAcquireQueuedLockExclusive;
testContext.AcquireShared = PhfAcquireQueuedLockShared;
testContext.ReleaseExclusive = PhfReleaseQueuedLockExclusive;
testContext.ReleaseShared = PhfReleaseQueuedLockShared;
testContext.Parameter = &queuedLock;
PhInitializeQueuedLock(&queuedLock);
PhpTestRwLock(&testContext);
testContext.Name = L"CriticalSection";
testContext.AcquireExclusive = PhfAcquireCriticalSection;
testContext.AcquireShared = PhfAcquireCriticalSection;
testContext.ReleaseExclusive = PhfReleaseCriticalSection;
testContext.ReleaseShared = PhfReleaseCriticalSection;
testContext.Parameter = &criticalSection;
RtlInitializeCriticalSection(&criticalSection);
PhpTestRwLock(&testContext);
RtlDeleteCriticalSection(&criticalSection);
testContext.Name = L"QueuedLockMutex";
testContext.AcquireExclusive = PhfAcquireQueuedLockExclusive;
testContext.AcquireShared = PhfAcquireQueuedLockExclusive;
testContext.ReleaseExclusive = PhfReleaseQueuedLockExclusive;
testContext.ReleaseShared = PhfReleaseQueuedLockExclusive;
testContext.Parameter = &queuedLock;
PhInitializeQueuedLock(&queuedLock);
PhpTestRwLock(&testContext);
}
else if (PhEqualStringZ(command, L"stats", TRUE))
{
#ifdef DEBUG
wprintf(L"Object small free list count: %u\n", PhObjectSmallFreeList.Count);
wprintf(L"Statistics:\n");
#define PRINT_STATISTIC(Name) wprintf(L#Name L": %u\n", PhLibStatisticsBlock.Name);
PRINT_STATISTIC(BaseThreadsCreated);
PRINT_STATISTIC(BaseThreadsCreateFailed);
PRINT_STATISTIC(BaseStringBuildersCreated);
PRINT_STATISTIC(BaseStringBuildersResized);
PRINT_STATISTIC(RefObjectsCreated);
PRINT_STATISTIC(RefObjectsDestroyed);
PRINT_STATISTIC(RefObjectsAllocated);
PRINT_STATISTIC(RefObjectsFreed);
PRINT_STATISTIC(RefObjectsAllocatedFromSmallFreeList);
PRINT_STATISTIC(RefObjectsFreedToSmallFreeList);
PRINT_STATISTIC(RefObjectsAllocatedFromTypeFreeList);
PRINT_STATISTIC(RefObjectsFreedToTypeFreeList);
PRINT_STATISTIC(RefObjectsDeleteDeferred);
PRINT_STATISTIC(RefAutoPoolsCreated);
PRINT_STATISTIC(RefAutoPoolsDestroyed);
PRINT_STATISTIC(RefAutoPoolsDynamicAllocated);
PRINT_STATISTIC(RefAutoPoolsDynamicResized);
PRINT_STATISTIC(QlBlockSpins);
PRINT_STATISTIC(QlBlockWaits);
PRINT_STATISTIC(QlAcquireExclusiveBlocks);
PRINT_STATISTIC(QlAcquireSharedBlocks);
PRINT_STATISTIC(WqWorkQueueThreadsCreated);
PRINT_STATISTIC(WqWorkQueueThreadsCreateFailed);
PRINT_STATISTIC(WqWorkItemsQueued);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"objects", TRUE))
{
#ifdef DEBUG
PWSTR typeFilter = wcstok_s(NULL, delims, &context);
PLIST_ENTRY currentEntry;
ULONG totalNumberOfObjects = 0;
//SIZE_T totalNumberOfBytes = 0;
if (typeFilter)
_wcslwr(typeFilter);
PhAcquireQueuedLockShared(&PhDbgObjectListLock);
currentEntry = PhDbgObjectListHead.Flink;
while (currentEntry != &PhDbgObjectListHead)
{
PPH_OBJECT_HEADER objectHeader;
WCHAR typeName[32];
objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry);
// Make sure the object isn't being destroyed.
if (!PhReferenceObjectSafe(PhObjectHeaderToObject(objectHeader)))
{
currentEntry = currentEntry->Flink;
continue;
}
totalNumberOfObjects++;
//totalNumberOfBytes += objectHeader->Size;
if (typeFilter)
{
wcscpy_s(typeName, sizeof(typeName) / 2, PhGetObjectType(PhObjectHeaderToObject(objectHeader))->Name);
_wcslwr(typeName);
}
if (
!typeFilter ||
(typeFilter && wcsstr(typeName, typeFilter))
)
{
PhpPrintObjectInfo(objectHeader, 1);
}
currentEntry = currentEntry->Flink;
PhDereferenceObjectDeferDelete(PhObjectHeaderToObject(objectHeader));
}
PhReleaseQueuedLockShared(&PhDbgObjectListLock);
wprintf(L"\n");
wprintf(L"Total number: %lu\n", totalNumberOfObjects);
/*wprintf(L"Total size (excl. header): %s\n",
((PPH_STRING)PhAutoDereferenceObject(PhFormatSize(totalNumberOfBytes, 1)))->Buffer);*/
wprintf(L"Total overhead (header): %s\n",
((PPH_STRING)PhAutoDereferenceObject(
PhFormatSize(PhAddObjectHeaderSize(0) * totalNumberOfObjects, 1)
))->Buffer);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"objtrace", TRUE))
{
#ifdef DEBUG
PWSTR objectAddress = wcstok_s(NULL, delims, &context);
PH_STRINGREF objectAddressString;
ULONG64 address;
if (!objectAddress)
{
wprintf(L"Missing object address.\n");
goto EndCommand;
}
PhInitializeStringRef(&objectAddressString, objectAddress);
if (PhStringToInteger64(&objectAddressString, 16, &address))
{
PPH_OBJECT_HEADER objectHeader = (PPH_OBJECT_HEADER)PhObjectToObjectHeader((PVOID)address);
PVOID stackBackTrace[16];
ULONG i;
// The address may not be valid.
__try
{
memcpy(stackBackTrace, objectHeader->StackBackTrace, 16 * sizeof(PVOID));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
PPH_STRING message;
message = PhGetNtMessage(GetExceptionCode());
PhAutoDereferenceObject(message);
wprintf(L"Error: %s\n", PhGetString(message));
goto EndCommand;
}
for (i = 0; i < 16; i++)
{
if (!stackBackTrace[i])
break;
wprintf(L"%s\n", PhpGetSymbolForAddress(stackBackTrace[i]));
}
}
else
{
wprintf(L"Invalid object address.\n");
}
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"objmksnap", TRUE))
{
#ifdef DEBUG
PLIST_ENTRY currentEntry;
if (ObjectListSnapshot)
{
PhDereferenceObject(ObjectListSnapshot);
ObjectListSnapshot = NULL;
}
ObjectListSnapshot = PhCreateSimpleHashtable(100);
PhAcquireQueuedLockShared(&PhDbgObjectListLock);
currentEntry = PhDbgObjectListHead.Flink;
while (currentEntry != &PhDbgObjectListHead)
{
PPH_OBJECT_HEADER objectHeader;
objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry);
currentEntry = currentEntry->Flink;
if (PhObjectHeaderToObject(objectHeader) != ObjectListSnapshot)
PhAddItemSimpleHashtable(ObjectListSnapshot, objectHeader, NULL);
}
PhReleaseQueuedLockShared(&PhDbgObjectListLock);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"objcmpsnap", TRUE))
{
#ifdef DEBUG
PLIST_ENTRY currentEntry;
PPH_LIST newObjects;
ULONG i;
if (!ObjectListSnapshot)
{
wprintf(L"No snapshot.\n");
goto EndCommand;
}
newObjects = PhCreateList(10);
PhAcquireQueuedLockShared(&PhDbgObjectListLock);
currentEntry = PhDbgObjectListHead.Flink;
while (currentEntry != &PhDbgObjectListHead)
{
PPH_OBJECT_HEADER objectHeader;
objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry);
currentEntry = currentEntry->Flink;
if (
PhObjectHeaderToObject(objectHeader) != ObjectListSnapshot &&
PhObjectHeaderToObject(objectHeader) != newObjects
)
{
if (!PhFindItemSimpleHashtable(ObjectListSnapshot, objectHeader))
{
if (PhReferenceObjectSafe(PhObjectHeaderToObject(objectHeader)))
PhAddItemList(newObjects, objectHeader);
}
}
}
PhReleaseQueuedLockShared(&PhDbgObjectListLock);
for (i = 0; i < newObjects->Count; i++)
{
PPH_OBJECT_HEADER objectHeader = newObjects->Items[i];
PhpPrintObjectInfo(objectHeader, 1);
PhDereferenceObject(PhObjectHeaderToObject(objectHeader));
}
PhDereferenceObject(newObjects);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"objmknew", TRUE))
{
#ifdef DEBUG
PhAcquireQueuedLockExclusive(&NewObjectListLock);
PhpDeleteNewObjectList();
PhReleaseQueuedLockExclusive(&NewObjectListLock);
// Creation needs to be done outside of the lock,
// otherwise a deadlock will occur.
NewObjectList = PhCreateList(100);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"objdelnew", TRUE))
{
#ifdef DEBUG
PhAcquireQueuedLockExclusive(&NewObjectListLock);
PhpDeleteNewObjectList();
PhReleaseQueuedLockExclusive(&NewObjectListLock);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"objviewnew", TRUE))
{
#ifdef DEBUG
ULONG i;
PhAcquireQueuedLockExclusive(&NewObjectListLock);
if (!NewObjectList)
{
wprintf(L"Object creation hooking not active.\n");
PhReleaseQueuedLockExclusive(&NewObjectListLock);
goto EndCommand;
}
for (i = 0; i < NewObjectList->Count; i++)
{
PhpPrintObjectInfo(PhObjectToObjectHeader(NewObjectList->Items[i]), 1);
}
PhReleaseQueuedLockExclusive(&NewObjectListLock);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"dumpobj", TRUE))
{
PWSTR addressString = wcstok_s(NULL, delims, &context);
PH_STRINGREF addressStringRef;
ULONG64 address;
if (!addressString)
goto EndCommand;
PhInitializeStringRef(&addressStringRef, addressString);
if (PhStringToInteger64(&addressStringRef, 16, &address))
{
PhpDumpObjectInfo((PPH_OBJECT_HEADER)PhObjectToObjectHeader((PVOID)address));
}
}
else if (PhEqualStringZ(command, L"dumpautopool", TRUE))
{
PWSTR addressString = wcstok_s(NULL, delims, &context);
PH_STRINGREF addressStringRef;
ULONG64 address;
if (!addressString)
goto EndCommand;
PhInitializeStringRef(&addressStringRef, addressString);
if (PhStringToInteger64(&addressStringRef, 16, &address))
{
PPH_AUTO_POOL userAutoPool = (PPH_AUTO_POOL)address;
ULONG i;
__try
{
wprintf(L"Static count: %u\n", userAutoPool->StaticCount);
wprintf(L"Dynamic count: %u\n", userAutoPool->DynamicCount);
wprintf(L"Dynamic allocated: %u\n", userAutoPool->DynamicAllocated);
wprintf(L"Static objects:\n");
for (i = 0; i < userAutoPool->StaticCount; i++)
PhpPrintObjectInfo(PhObjectToObjectHeader(userAutoPool->StaticObjects[i]), 0);
wprintf(L"Dynamic objects:\n");
for (i = 0; i < userAutoPool->DynamicCount; i++)
PhpPrintObjectInfo(PhObjectToObjectHeader(userAutoPool->DynamicObjects[i]), 0);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
goto EndCommand;
}
}
}
else if (PhEqualStringZ(command, L"threads", TRUE))
{
#ifdef DEBUG
PLIST_ENTRY currentEntry;
PhAcquireQueuedLockShared(&PhDbgThreadListLock);
currentEntry = PhDbgThreadListHead.Flink;
while (currentEntry != &PhDbgThreadListHead)
{
PPHP_BASE_THREAD_DBG dbg;
dbg = CONTAINING_RECORD(currentEntry, PHP_BASE_THREAD_DBG, ListEntry);
wprintf(L"Thread %u\n", HandleToUlong(dbg->ClientId.UniqueThread));
wprintf(L"\tStart Address: %s\n", PhpGetSymbolForAddress(dbg->StartAddress));
wprintf(L"\tParameter: %Ix\n", (ULONG_PTR)dbg->Parameter);
wprintf(L"\tCurrent auto-pool: %Ix\n", (ULONG_PTR)dbg->CurrentAutoPool);
currentEntry = currentEntry->Flink;
}
PhReleaseQueuedLockShared(&PhDbgThreadListLock);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"provthreads", TRUE))
{
#ifdef DEBUG
ULONG i;
if (PhDbgProviderList)
{
PhAcquireQueuedLockShared(&PhDbgProviderListLock);
for (i = 0; i < PhDbgProviderList->Count; i++)
{
PPH_PROVIDER_THREAD providerThread = PhDbgProviderList->Items[i];
THREAD_BASIC_INFORMATION basicInfo;
PLIST_ENTRY providerEntry;
if (providerThread->ThreadHandle)
{
PhGetThreadBasicInformation(providerThread->ThreadHandle, &basicInfo);
wprintf(L"Thread %u\n", HandleToUlong(basicInfo.ClientId.UniqueThread));
}
else
{
wprintf(L"Thread not running\n");
}
PhAcquireQueuedLockExclusive(&providerThread->Lock);
providerEntry = providerThread->ListHead.Flink;
while (providerEntry != &providerThread->ListHead)
{
PPH_PROVIDER_REGISTRATION registration;
registration = CONTAINING_RECORD(providerEntry, PH_PROVIDER_REGISTRATION, ListEntry);
wprintf(L"\tProvider registration at %Ix\n", (ULONG_PTR)registration);
wprintf(L"\t\tEnabled: %s\n", registration->Enabled ? L"Yes" : L"No");
wprintf(L"\t\tFunction: %s\n", PhpGetSymbolForAddress(registration->Function));
if (registration->Object)
{
wprintf(L"\t\tObject:\n");
PhpPrintObjectInfo(PhObjectToObjectHeader(registration->Object), 0);
}
providerEntry = providerEntry->Flink;
}
PhReleaseQueuedLockExclusive(&providerThread->Lock);
wprintf(L"\n");
}
PhReleaseQueuedLockShared(&PhDbgProviderListLock);
}
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"workqueues", TRUE))
{
#ifdef DEBUG
ULONG i;
if (PhDbgWorkQueueList)
{
PhAcquireQueuedLockShared(&PhDbgWorkQueueListLock);
for (i = 0; i < PhDbgWorkQueueList->Count; i++)
{
PPH_WORK_QUEUE workQueue = PhDbgWorkQueueList->Items[i];
PLIST_ENTRY workQueueItemEntry;
wprintf(L"Work queue at %s\n", PhpGetSymbolForAddress(workQueue));
wprintf(L"Maximum threads: %u\n", workQueue->MaximumThreads);
wprintf(L"Minimum threads: %u\n", workQueue->MinimumThreads);
wprintf(L"No work timeout: %d\n", workQueue->NoWorkTimeout);
wprintf(L"Current threads: %u\n", workQueue->CurrentThreads);
wprintf(L"Busy count: %u\n", workQueue->BusyCount);
PhAcquireQueuedLockExclusive(&workQueue->QueueLock);
// List the items backwards.
workQueueItemEntry = workQueue->QueueListHead.Blink;
while (workQueueItemEntry != &workQueue->QueueListHead)
{
PPH_WORK_QUEUE_ITEM workQueueItem;
workQueueItem = CONTAINING_RECORD(workQueueItemEntry, PH_WORK_QUEUE_ITEM, ListEntry);
wprintf(L"\tWork queue item at %Ix\n", (ULONG_PTR)workQueueItem);
wprintf(L"\t\tFunction: %s\n", PhpGetSymbolForAddress(workQueueItem->Function));
wprintf(L"\t\tContext: %Ix\n", (ULONG_PTR)workQueueItem->Context);
workQueueItemEntry = workQueueItemEntry->Blink;
}
PhReleaseQueuedLockExclusive(&workQueue->QueueLock);
wprintf(L"\n");
}
PhReleaseQueuedLockShared(&PhDbgWorkQueueListLock);
}
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"procrecords", TRUE))
{
PPH_PROCESS_RECORD record;
ULONG i;
SYSTEMTIME systemTime;
PPH_PROCESS_RECORD startRecord;
PhAcquireQueuedLockShared(&PhProcessRecordListLock);
for (i = 0; i < PhProcessRecordList->Count; i++)
{
record = (PPH_PROCESS_RECORD)PhProcessRecordList->Items[i];
PhLargeIntegerToLocalSystemTime(&systemTime, &record->CreateTime);
wprintf(L"Records for %s %s:\n",
((PPH_STRING)PhAutoDereferenceObject(PhFormatDate(&systemTime, NULL)))->Buffer,
((PPH_STRING)PhAutoDereferenceObject(PhFormatTime(&systemTime, NULL)))->Buffer
);
startRecord = record;
do
{
wprintf(L"\tRecord at %Ix: %s (%u) (refs: %d)\n", (ULONG_PTR)record, record->ProcessName->Buffer, HandleToUlong(record->ProcessId), record->RefCount);
if (record->FileName)
wprintf(L"\t\t%s\n", record->FileName->Buffer);
record = CONTAINING_RECORD(record->ListEntry.Flink, PH_PROCESS_RECORD, ListEntry);
} while (record != startRecord);
}
PhReleaseQueuedLockShared(&PhProcessRecordListLock);
}
else if (PhEqualStringZ(command, L"procitem", TRUE))
{
PWSTR filterString;
PH_STRINGREF filterRef;
ULONG64 filter64;
LONG_PTR processIdFilter = -9; // can't use -2, -1 or 0 because they're all used for process IDs
ULONG_PTR processAddressFilter = 0;
PWSTR imageNameFilter = NULL;
BOOLEAN showAll = FALSE;
PPH_PROCESS_ITEM *processes;
ULONG numberOfProcesses;
ULONG i;
filterString = wcstok_s(NULL, delims, &context);
if (filterString)
{
PhInitializeStringRef(&filterRef, filterString);
if (PhStringToInteger64(&filterRef, 10, &filter64))
processIdFilter = (LONG_PTR)filter64;
if (PhStringToInteger64(&filterRef, 16, &filter64))
processAddressFilter = (ULONG_PTR)filter64;
imageNameFilter = filterString;
}
else
{
showAll = TRUE;
}
PhEnumProcessItems(&processes, &numberOfProcesses);
for (i = 0; i < numberOfProcesses; i++)
{
PPH_PROCESS_ITEM process = processes[i];
if (
showAll ||
(processIdFilter != -9 && (LONG_PTR)process->ProcessId == processIdFilter) ||
(processAddressFilter != 0 && (ULONG_PTR)process == processAddressFilter) ||
(imageNameFilter && PhMatchWildcards(imageNameFilter, process->ProcessName->Buffer, TRUE))
)
{
wprintf(L"Process item at %Ix: %s (%u)\n", (ULONG_PTR)process, process->ProcessName->Buffer, HandleToUlong(process->ProcessId));
wprintf(L"\tRecord at %Ix\n", (ULONG_PTR)process->Record);
wprintf(L"\tQuery handle %Ix\n", (ULONG_PTR)process->QueryHandle);
wprintf(L"\tFile name at %Ix: %s\n", (ULONG_PTR)process->FileName, PhGetStringOrDefault(process->FileName, L"(null)"));
wprintf(L"\tCommand line at %Ix: %s\n", (ULONG_PTR)process->CommandLine, PhGetStringOrDefault(process->CommandLine, L"(null)"));
wprintf(L"\tFlags: %u\n", process->Flags);
wprintf(L"\n");
}
}
PhDereferenceObjects(processes, numberOfProcesses);
}
else if (PhEqualStringZ(command, L"uniquestr", TRUE))
{
#ifdef DEBUG
PLIST_ENTRY currentEntry;
PPH_HASHTABLE hashtable;
PPH_LIST list;
PSTRING_TABLE_ENTRY stringEntry;
ULONG enumerationKey;
ULONG i;
hashtable = PhCreateHashtable(
sizeof(STRING_TABLE_ENTRY),
PhpStringHashtableCompareFunction,
PhpStringHashtableHashFunction,
1024
);
PhAcquireQueuedLockShared(&PhDbgObjectListLock);
currentEntry = PhDbgObjectListHead.Flink;
while (currentEntry != &PhDbgObjectListHead)
{
PPH_OBJECT_HEADER objectHeader;
PPH_STRING string;
STRING_TABLE_ENTRY localStringEntry;
BOOLEAN added;
objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry);
currentEntry = currentEntry->Flink;
string = PhObjectHeaderToObject(objectHeader);
// Make sure this is a string.
if (PhGetObjectType(string) != PhStringType)
continue;
// Make sure the object isn't being destroyed.
if (!PhReferenceObjectSafe(string))
continue;
localStringEntry.String = string;
stringEntry = PhAddEntryHashtableEx(hashtable, &localStringEntry, &added);
if (added)
{
stringEntry->Count = 1;
PhReferenceObject(string);
}
else
{
stringEntry->Count++;
}
PhDereferenceObjectDeferDelete(string);
}
PhReleaseQueuedLockShared(&PhDbgObjectListLock);
// Sort the string entries by count.
list = PhCreateList(hashtable->Count);
enumerationKey = 0;
while (PhEnumHashtable(hashtable, &stringEntry, &enumerationKey))
{
PhAddItemList(list, stringEntry);
}
qsort(list->Items, list->Count, sizeof(PSTRING_TABLE_ENTRY), PhpStringEntryCompareByCount);
// Display...
for (i = 0; i < 40 && i < list->Count; i++)
{
stringEntry = list->Items[i];
wprintf(L"%Iu\t%.64s\n", stringEntry->Count, stringEntry->String->Buffer);
}
wprintf(L"\nTotal unique strings: %u\n", list->Count);
// Cleanup
for (i = 0; i < list->Count; i++)
{
stringEntry = list->Items[i];
PhDereferenceObject(stringEntry->String);
}
PhDereferenceObject(list);
PhDereferenceObject(hashtable);
#else
wprintf(commandDebugOnly);
#endif
}
else if (PhEqualStringZ(command, L"enableleakdetect", TRUE))
{
HEAP_DEBUGGING_INFORMATION debuggingInfo;
memset(&debuggingInfo, 0, sizeof(HEAP_DEBUGGING_INFORMATION));
debuggingInfo.StackTraceDepth = 32;
debuggingInfo.HeapLeakEnumerationRoutine = PhpLeakEnumerationRoutine;
if (!NT_SUCCESS(RtlSetHeapInformation(NULL, HeapDebuggingInformation, &debuggingInfo, sizeof(HEAP_DEBUGGING_INFORMATION))))
{
wprintf(L"Unable to initialize heap debugging. Make sure that you are using Windows 7 or above.");
}
}
else if (PhEqualStringZ(command, L"leakdetect", TRUE))
{
VOID (NTAPI *rtlDetectHeapLeaks)(VOID);
PWSTR options = wcstok_s(NULL, delims, &context);
rtlDetectHeapLeaks = PhGetModuleProcAddress(L"ntdll.dll", "RtlDetectHeapLeaks");
if (!(NtCurrentPeb()->NtGlobalFlag & FLG_USER_STACK_TRACE_DB))
{
wprintf(L"Warning: user-mode stack trace database is not enabled. Stack traces will not be displayed.\n");
}
ShowAllLeaks = FALSE;
if (options)
{
if (PhEqualStringZ(options, L"all", TRUE))
ShowAllLeaks = TRUE;
}
if (rtlDetectHeapLeaks)
{
InLeakDetection = TRUE;
NumberOfLeaks = 0;
NumberOfLeaksShown = 0;
rtlDetectHeapLeaks();
InLeakDetection = FALSE;
wprintf(L"\nNumber of leaks: %lu (%lu displayed)\n", NumberOfLeaks, NumberOfLeaksShown);
}
}
else if (PhEqualStringZ(command, L"mem", TRUE))
{
PWSTR addressString;
PWSTR bytesString;
PH_STRINGREF addressStringRef;
PH_STRINGREF bytesStringRef;
ULONG64 address64;
ULONG64 numberOfBytes64;
PUCHAR address;
ULONG numberOfBytes;
ULONG blockSize;
UCHAR buffer[16];
ULONG i;
addressString = wcstok_s(NULL, delims, &context);
if (!addressString)
goto PrintMemUsage;
bytesString = wcstok_s(NULL, delims, &context);
if (!bytesString)
{
bytesString = L"16";
}
PhInitializeStringRef(&addressStringRef, addressString);
PhInitializeStringRef(&bytesStringRef, bytesString);
if (PhStringToInteger64(&addressStringRef, 16, &address64) && PhStringToInteger64(&bytesStringRef, 10, &numberOfBytes64))
{
address = (PUCHAR)address64;
numberOfBytes = (ULONG)numberOfBytes64;
if (numberOfBytes > 256)
{
wprintf(L"Number of bytes must be 256 or smaller.\n");
goto EndCommand;
}
blockSize = sizeof(buffer);
while (numberOfBytes != 0)
{
if (blockSize > numberOfBytes)
blockSize = numberOfBytes;
__try
{
memcpy(buffer, address, blockSize);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
wprintf(L"Error reading address near %Ix.\n", (ULONG_PTR)address);
goto EndCommand;
}
// Print hex dump
for (i = 0; i < blockSize; i++)
wprintf(L"%02x ", buffer[i]);
// Fill remaining space (for last, possibly partial block)
for (; i < sizeof(buffer); i++)
wprintf(L" ");
wprintf(L" ");
// Print ASCII dump
for (i = 0; i < blockSize; i++)
putwchar((ULONG)(buffer[i] - ' ') <= (ULONG)('~' - ' ') ? buffer[i] : '.');
wprintf(L"\n");
address += blockSize;
numberOfBytes -= blockSize;
}
}
goto EndCommand;
PrintMemUsage:
wprintf(L"Usage: mem address [numberOfBytes]\n");
wprintf(L"Example: mem 12345678 16\n");
}
else
{
wprintf(L"Unrecognized command.\n");
goto EndCommand; // get rid of the compiler warning about the label being unreferenced
}
EndCommand:
PhDrainAutoPool(&autoPool);
}
PhDereferenceObject(DebugConsoleSymbolProvider);
PhDeleteAutoPool(&autoPool);
return STATUS_SUCCESS;
}