Files
mirror-processhacker/2.x/trunk/ProcessHacker/dbgcon.c
T
wj32 32223a9ff4 improved PhReleaseQueuedLockExclusive
git-svn-id: svn://svn.code.sf.net/p/processhacker/code@4236 21ef857c-d57f-4fe0-8362-d861dc6d29cd
2011-06-18 11:57:29 +00:00

1710 lines
54 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 <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()
{
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()
{
fclose(stdout);
fclose(stderr);
fclose(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)PHA_DEREFERENCE(PhGetSymbolFromAddress(
DebugConsoleSymbolProvider, (ULONG64)Address, NULL, NULL, NULL, NULL
)))->Buffer;
}
static VOID PhpPrintObjectInfo(
__in PPH_OBJECT_HEADER ObjectHeader,
__in LONG RefToSubtract
)
{
WCHAR c = ' ';
wprintf(L"%Ix", PhObjectHeaderToObject(ObjectHeader));
wprintf(L"\t% 20s", ObjectHeader->Type->Name);
if (ObjectHeader->Flags & PHOBJ_FROM_SMALL_FREE_LIST)
c = 'f';
else if (ObjectHeader->Flags & PHOBJ_FROM_TYPE_FREE_LIST)
c = 'F';
wprintf(L"\t%4d %c", ObjectHeader->RefCount - RefToSubtract, c);
if (!ObjectHeader->Type)
{
// Dummy
}
else if (ObjectHeader->Type == PhObjectTypeObject)
{
wprintf(L"\t%.32s", ((PPH_OBJECT_TYPE)PhObjectHeaderToObject(ObjectHeader))->Name);
}
else if (ObjectHeader->Type == PhStringType)
{
wprintf(L"\t%.32s", ((PPH_STRING)PhObjectHeaderToObject(ObjectHeader))->Buffer);
}
else if (ObjectHeader->Type == PhAnsiStringType)
{
wprintf(L"\t%.32S", ((PPH_ANSI_STRING)PhObjectHeaderToObject(ObjectHeader))->Buffer);
}
else if (ObjectHeader->Type == PhFullStringType)
{
wprintf(L"\t%.32s", ((PPH_FULL_STRING)PhObjectHeaderToObject(ObjectHeader))->Buffer);
}
else if (ObjectHeader->Type == PhListType)
{
wprintf(L"\tCount: %u", ((PPH_LIST)PhObjectHeaderToObject(ObjectHeader))->Count);
}
else if (ObjectHeader->Type == PhPointerListType)
{
wprintf(L"\tCount: %u", ((PPH_POINTER_LIST)PhObjectHeaderToObject(ObjectHeader))->Count);
}
else if (ObjectHeader->Type == PhQueueType)
{
wprintf(L"\tCount: %u", ((PPH_QUEUE)PhObjectHeaderToObject(ObjectHeader))->Count);
}
else if (ObjectHeader->Type == PhHashtableType)
{
wprintf(L"\tCount: %u", ((PPH_HASHTABLE)PhObjectHeaderToObject(ObjectHeader))->Count);
}
else if (ObjectHeader->Type == PhProcessItemType)
{
wprintf(
L"\t%.28s (%Id)",
(ULONG)((PPH_PROCESS_ITEM)PhObjectHeaderToObject(ObjectHeader))->ProcessName->Buffer,
(ULONG)((PPH_PROCESS_ITEM)PhObjectHeaderToObject(ObjectHeader))->ProcessId
);
}
else if (ObjectHeader->Type == PhServiceItemType)
{
wprintf(L"\t%s",
(ULONG)((PPH_SERVICE_ITEM)PhObjectHeaderToObject(ObjectHeader))->Name->Buffer);
}
else if (ObjectHeader->Type == PhThreadItemType)
{
wprintf(L"\tTID: %u",
(ULONG)((PPH_THREAD_ITEM)PhObjectHeaderToObject(ObjectHeader))->ThreadId);
}
wprintf(L"\n");
}
static VOID PhpDumpObjectInfo(
__in PPH_OBJECT_HEADER ObjectHeader
)
{
__try
{
wprintf(L"Type: %s\n", ObjectHeader->Type->Name);
wprintf(L"Reference count: %d\n", ObjectHeader->RefCount);
wprintf(L"Flags: %x\n", ObjectHeader->Flags);
wprintf(L"Size / Next to free: %Iu / %Ix\n", ObjectHeader->Size, ObjectHeader->NextToFree);
if (ObjectHeader->Type == PhObjectTypeObject)
{
wprintf(L"Name: %s\n", ((PPH_OBJECT_TYPE)PhObjectHeaderToObject(ObjectHeader))->Name);
wprintf(L"Number of objects: %u\n", ((PPH_OBJECT_TYPE)PhObjectHeaderToObject(ObjectHeader))->NumberOfObjects);
wprintf(L"Free list count: %u\n", ((PPH_OBJECT_TYPE)PhObjectHeaderToObject(ObjectHeader))->FreeList.Count);
}
else if (ObjectHeader->Type == PhStringType)
{
wprintf(L"%s\n", ((PPH_STRING)PhObjectHeaderToObject(ObjectHeader))->Buffer);
}
else if (ObjectHeader->Type == PhAnsiStringType)
{
wprintf(L"%S\n", ((PPH_ANSI_STRING)PhObjectHeaderToObject(ObjectHeader))->Buffer);
}
else if (ObjectHeader->Type == PhFullStringType)
{
wprintf(L"%s\n", ((PPH_FULL_STRING)PhObjectHeaderToObject(ObjectHeader))->Buffer);
}
else if (ObjectHeader->Type == PhHashtableType)
{
PhpPrintHashtableStatistics((PPH_HASHTABLE)PhObjectHeaderToObject(ObjectHeader));
}
}
__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"%u: ", i);
// Print out the entry indicies.
index = Hashtable->Buckets[i];
while (index != -1)
{
wprintf(L"%u", 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: %u\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()
{
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 RtlEqualUnicodeString(&entry1->String->us, &entry2->String->us, 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", 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;
PhInitializeAutoPool(&autoPool);
DebugConsoleSymbolProvider = PhCreateSymbolProvider(NtCurrentProcessId());
{
WCHAR buffer[512];
PH_STRINGREF name = PH_STRINGREF_INIT(L"_NT_SYMBOL_PATH");
UNICODE_STRING var;
PPH_STRING newSearchPath;
var.Buffer = buffer;
var.MaximumLength = sizeof(buffer);
if (!NT_SUCCESS(RtlQueryEnvironmentVariable_U(NULL, &name.us, &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 (TRUE)
{
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)wcslen(line);
if (inputLength != 0)
line[inputLength - 1] = 0;
context = NULL;
command = wcstok_s(line, delims, &context);
if (!command)
{
continue;
}
else if (WSTR_IEQUAL(command, L"help"))
{
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 (WSTR_IEQUAL(command, L"exit"))
{
PhCloseDebugConsole();
}
else if (WSTR_IEQUAL(command, L"testperf"))
{
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 (WSTR_IEQUAL(command, L"testlocks"))
{
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 (WSTR_IEQUAL(command, L"stats"))
{
#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 (WSTR_IEQUAL(command, L"objects"))
{
#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, objectHeader->Type->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: %u\n", totalNumberOfObjects);
wprintf(L"Total size (excl. header): %s\n",
((PPH_STRING)PHA_DEREFERENCE(PhFormatSize(totalNumberOfBytes, 1)))->Buffer);
wprintf(L"Total overhead (header): %s\n",
((PPH_STRING)PHA_DEREFERENCE(
PhFormatSize(PhpAddObjectHeaderSize(0) * totalNumberOfObjects, 1)
))->Buffer);
#else
wprintf(commandDebugOnly);
#endif
}
else if (WSTR_IEQUAL(command, L"objtrace"))
{
#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());
PHA_DEREFERENCE(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 (WSTR_IEQUAL(command, L"objmksnap"))
{
#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 (WSTR_IEQUAL(command, L"objcmpsnap"))
{
#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 (WSTR_IEQUAL(command, L"objmknew"))
{
#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 (WSTR_IEQUAL(command, L"objdelnew"))
{
#ifdef DEBUG
PhAcquireQueuedLockExclusive(&NewObjectListLock);
PhpDeleteNewObjectList();
PhReleaseQueuedLockExclusive(&NewObjectListLock);
#else
wprintf(commandDebugOnly);
#endif
}
else if (WSTR_IEQUAL(command, L"objviewnew"))
{
#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 (WSTR_IEQUAL(command, L"dumpobj"))
{
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 (WSTR_IEQUAL(command, L"dumpautopool"))
{
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 (WSTR_IEQUAL(command, L"threads"))
{
#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", (ULONG)dbg->ClientId.UniqueThread);
wprintf(L"\tStart Address: %s\n", PhpGetSymbolForAddress(dbg->StartAddress));
wprintf(L"\tParameter: %Ix\n", dbg->Parameter);
wprintf(L"\tCurrent auto-pool: %Ix\n", dbg->CurrentAutoPool);
currentEntry = currentEntry->Flink;
}
PhReleaseQueuedLockShared(&PhDbgThreadListLock);
#else
wprintf(commandDebugOnly);
#endif
}
else if (WSTR_IEQUAL(command, L"provthreads"))
{
#ifdef DEBUG
PLIST_ENTRY currentEntry;
PhAcquireQueuedLockShared(&PhDbgProviderListLock);
currentEntry = PhDbgProviderListHead.Flink;
while (currentEntry != &PhDbgProviderListHead)
{
PPH_PROVIDER_THREAD providerThread;
THREAD_BASIC_INFORMATION basicInfo;
PLIST_ENTRY providerEntry;
providerThread = CONTAINING_RECORD(currentEntry, PH_PROVIDER_THREAD, DbgListEntry);
if (providerThread->ThreadHandle)
{
PhGetThreadBasicInformation(providerThread->ThreadHandle, &basicInfo);
wprintf(L"Thread %u\n", (ULONG)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", 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");
currentEntry = currentEntry->Flink;
}
PhReleaseQueuedLockShared(&PhDbgProviderListLock);
#else
wprintf(commandDebugOnly);
#endif
}
else if (WSTR_IEQUAL(command, L"workqueues"))
{
#ifdef DEBUG
PLIST_ENTRY currentEntry;
PhAcquireQueuedLockShared(&PhDbgWorkQueueListLock);
currentEntry = PhDbgWorkQueueListHead.Flink;
while (currentEntry != &PhDbgWorkQueueListHead)
{
PPH_WORK_QUEUE workQueue;
PLIST_ENTRY workQueueItemEntry;
workQueue = CONTAINING_RECORD(currentEntry, PH_WORK_QUEUE, DbgListEntry);
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 threads: %u\n", workQueue->BusyThreads);
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", workQueueItem);
wprintf(L"\t\tFunction: %s\n", PhpGetSymbolForAddress(workQueueItem->Function));
wprintf(L"\t\tContext: %Ix\n", workQueueItem->Context);
workQueueItemEntry = workQueueItemEntry->Blink;
}
PhReleaseQueuedLockExclusive(&workQueue->QueueLock);
wprintf(L"\n");
currentEntry = currentEntry->Flink;
}
PhReleaseQueuedLockShared(&PhDbgWorkQueueListLock);
#else
wprintf(commandDebugOnly);
#endif
}
else if (WSTR_IEQUAL(command, L"procrecords"))
{
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)PHA_DEREFERENCE(PhFormatDate(&systemTime, NULL)))->Buffer,
((PPH_STRING)PHA_DEREFERENCE(PhFormatTime(&systemTime, NULL)))->Buffer
);
startRecord = record;
do
{
wprintf(L"\tRecord at %Ix: %s (%u) (refs: %d)\n", record, record->ProcessName->Buffer, (ULONG)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 (WSTR_IEQUAL(command, L"procitem"))
{
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)
{
ULONG length;
PhInitializeStringRef(&filterRef, filterString);
// Never interpret the filter string as a process ID if it isn't
// base 10, to reduce FPs (since PhStringToInteger64 never actually fails
// regardless of what characters are in the input).
length = filterRef.Length / 2;
for (i = 0; i < length; i++)
{
if (!PhIsDigitCharacter(filterRef.Buffer[i]))
break;
}
if (i == filterRef.Length / 2)
{
if (PhStringToInteger64(&filterRef, 10, &filter64))
processIdFilter = (LONG_PTR)filter64;
}
// Always try to interpret the filter string as an address or image name,
// because these have very few false positives.
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", process, process->ProcessName->Buffer, (ULONG)process->ProcessId);
wprintf(L"\tRecord at %Ix\n", process->Record);
wprintf(L"\tQuery handle %Ix\n", process->QueryHandle);
wprintf(L"\tFile name at %Ix: %s\n", process->FileName, PhGetStringOrDefault(process->FileName, L"(null)"));
wprintf(L"\tCommand line at %Ix: %s\n", process->CommandLine, PhGetStringOrDefault(process->CommandLine, L"(null)"));
wprintf(L"\tFlags: %u\n", process->Flags);
wprintf(L"\n");
}
}
PhDereferenceObjects(processes, numberOfProcesses);
}
else if (WSTR_IEQUAL(command, L"uniquestr"))
{
#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 (objectHeader->Type != 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 (WSTR_IEQUAL(command, L"enableleakdetect"))
{
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 (WSTR_IEQUAL(command, L"leakdetect"))
{
VOID (NTAPI *rtlDetectHeapLeaks)();
PWSTR options = wcstok_s(NULL, delims, &context);
rtlDetectHeapLeaks = PhGetProcAddress(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 (WSTR_IEQUAL(options, L"all"))
ShowAllLeaks = TRUE;
}
if (rtlDetectHeapLeaks)
{
InLeakDetection = TRUE;
NumberOfLeaks = 0;
NumberOfLeaksShown = 0;
rtlDetectHeapLeaks();
InLeakDetection = FALSE;
wprintf(L"\nNumber of leaks: %u (%u displayed)\n", NumberOfLeaks, NumberOfLeaksShown);
}
}
else if (WSTR_IEQUAL(command, L"mem"))
{
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", 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;
}