Process Hacker
dbgcon.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * debug console
4  *
5  * Copyright (C) 2010-2011 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 /*
24  * This is a simple debugging console which is able to explore phlib's
25  * systems easily. Commands are provided to debug reference counting
26  * problems and memory usage, as well as to do general performance testing.
27  */
28 
29 #include <phapp.h>
30 #include <phintrnl.h>
31 #include <symprv.h>
32 #include <refp.h>
33 
34 typedef struct _STRING_TABLE_ENTRY
35 {
36  PPH_STRING String;
37  ULONG_PTR Count;
39 
41  _In_ DWORD dwCtrlType
42  );
43 
45  _In_ PPH_HASHTABLE Hashtable
46  );
47 
49  _In_ PVOID Parameter
50  );
51 
53 
54 static HANDLE DebugConsoleThreadHandle;
55 static PPH_SYMBOL_PROVIDER DebugConsoleSymbolProvider;
56 
57 static PPH_HASHTABLE ObjectListSnapshot = NULL;
58 #ifdef DEBUG
59 static PPH_LIST NewObjectList = NULL;
60 static PH_QUEUED_LOCK NewObjectListLock;
61 #endif
62 
63 static BOOLEAN ShowAllLeaks = FALSE;
64 static BOOLEAN InLeakDetection = FALSE;
65 static ULONG NumberOfLeaks;
66 static ULONG NumberOfLeaksShown;
67 
69  VOID
70  )
71 {
72  if (AllocConsole())
73  {
74  HMENU menu;
75 
76  // Disable the close button because it's impossible to handle
77  // those events.
78  menu = GetSystemMenu(GetConsoleWindow(), FALSE);
79  EnableMenuItem(menu, SC_CLOSE, MF_GRAYED | MF_DISABLED);
80  DeleteMenu(menu, SC_CLOSE, 0);
81 
82  // Set a handler so we can catch Ctrl+C and Ctrl+Break.
83  SetConsoleCtrlHandler(ConsoleHandlerRoutine, TRUE);
84 
85  freopen("CONOUT$", "w", stdout);
86  freopen("CONOUT$", "w", stderr);
87  freopen("CONIN$", "r", stdin);
88  DebugConsoleThreadHandle = PhCreateThread(0, PhpDebugConsoleThreadStart, NULL);
89  }
90  else
91  {
92  HWND consoleWindow;
93 
94  consoleWindow = GetConsoleWindow();
95 
96  // Console window already exists, so bring it to the top.
97  if (IsIconic(consoleWindow))
98  ShowWindow(consoleWindow, SW_RESTORE);
99  else
100  BringWindowToTop(consoleWindow);
101 
102  return;
103  }
104 }
105 
107  VOID
108  )
109 {
110  fclose(stdout);
111  fclose(stderr);
112  fclose(stdin);
113 
114  FreeConsole();
115 }
116 
118  _In_ DWORD dwCtrlType
119  )
120 {
121  switch (dwCtrlType)
122  {
123  case CTRL_C_EVENT:
124  case CTRL_BREAK_EVENT:
125  case CTRL_CLOSE_EVENT:
127  return TRUE;
128  }
129 
130  return FALSE;
131 }
132 
133 static BOOLEAN NTAPI PhpLoadCurrentProcessSymbolsCallback(
134  _In_ PPH_MODULE_INFO Module,
135  _In_opt_ PVOID Context
136  )
137 {
138  PhLoadModuleSymbolProvider((PPH_SYMBOL_PROVIDER)Context, Module->FileName->Buffer,
139  (ULONG64)Module->BaseAddress, Module->Size);
140 
141  return TRUE;
142 }
143 
144 static PWSTR PhpGetSymbolForAddress(
145  _In_ PVOID Address
146  )
147 {
149  DebugConsoleSymbolProvider, (ULONG64)Address, NULL, NULL, NULL, NULL
150  )))->Buffer;
151 }
152 
153 static VOID PhpPrintObjectInfo(
154  _In_ PPH_OBJECT_HEADER ObjectHeader,
155  _In_ LONG RefToSubtract
156  )
157 {
158  PVOID object;
159  PPH_OBJECT_TYPE objectType;
160  WCHAR c = ' ';
161 
162  object = PhObjectHeaderToObject(ObjectHeader);
163  wprintf(L"%Ix", object);
164  objectType = PhGetObjectType(object);
165 
166  wprintf(L"\t% 20s", objectType->Name);
167 
168  if (ObjectHeader->Flags & PH_OBJECT_FROM_SMALL_FREE_LIST)
169  c = 'f';
170  else if (ObjectHeader->Flags & PH_OBJECT_FROM_TYPE_FREE_LIST)
171  c = 'F';
172 
173  wprintf(L"\t%4d %c", ObjectHeader->RefCount - RefToSubtract, c);
174 
175  if (!objectType)
176  {
177  // Dummy
178  }
179  else if (objectType == PhObjectTypeObject)
180  {
181  wprintf(L"\t%.32s", ((PPH_OBJECT_TYPE)object)->Name);
182  }
183  else if (objectType == PhStringType)
184  {
185  wprintf(L"\t%.32s", ((PPH_STRING)object)->Buffer);
186  }
187  else if (objectType == PhBytesType)
188  {
189  wprintf(L"\t%.32S", ((PPH_BYTES)object)->Buffer);
190  }
191  else if (objectType == PhListType)
192  {
193  wprintf(L"\tCount: %u", ((PPH_LIST)object)->Count);
194  }
195  else if (objectType == PhPointerListType)
196  {
197  wprintf(L"\tCount: %u", ((PPH_POINTER_LIST)object)->Count);
198  }
199  else if (objectType == PhHashtableType)
200  {
201  wprintf(L"\tCount: %u", ((PPH_HASHTABLE)object)->Count);
202  }
203  else if (objectType == PhProcessItemType)
204  {
205  wprintf(
206  L"\t%.28s (%Id)",
207  ((PPH_PROCESS_ITEM)object)->ProcessName->Buffer,
208  (ULONG)((PPH_PROCESS_ITEM)object)->ProcessId
209  );
210  }
211  else if (objectType == PhServiceItemType)
212  {
213  wprintf(L"\t%s", ((PPH_SERVICE_ITEM)object)->Name->Buffer);
214  }
215  else if (objectType == PhThreadItemType)
216  {
217  wprintf(L"\tTID: %u", (ULONG)((PPH_THREAD_ITEM)object)->ThreadId);
218  }
219 
220  wprintf(L"\n");
221 }
222 
223 static VOID PhpDumpObjectInfo(
224  _In_ PPH_OBJECT_HEADER ObjectHeader
225  )
226 {
227  PVOID object;
228  PPH_OBJECT_TYPE objectType;
229 
230  object = PhObjectHeaderToObject(ObjectHeader);
231  objectType = PhGetObjectType(object);
232 
233  __try
234  {
235  wprintf(L"Type: %s\n", objectType->Name);
236  wprintf(L"Reference count: %d\n", ObjectHeader->RefCount);
237  wprintf(L"Flags: %x\n", ObjectHeader->Flags);
238 
239  if (objectType == PhObjectTypeObject)
240  {
241  wprintf(L"Name: %s\n", ((PPH_OBJECT_TYPE)object)->Name);
242  wprintf(L"Number of objects: %u\n", ((PPH_OBJECT_TYPE)object)->NumberOfObjects);
243  wprintf(L"Flags: %u\n", ((PPH_OBJECT_TYPE)object)->Flags);
244  wprintf(L"Type index: %u\n", ((PPH_OBJECT_TYPE)object)->TypeIndex);
245  wprintf(L"Free list count: %u\n", ((PPH_OBJECT_TYPE)object)->FreeList.Count);
246  }
247  else if (objectType == PhStringType)
248  {
249  wprintf(L"%s\n", ((PPH_STRING)object)->Buffer);
250  }
251  else if (objectType == PhBytesType)
252  {
253  wprintf(L"%S\n", ((PPH_BYTES)object)->Buffer);
254  }
255  else if (objectType == PhHashtableType)
256  {
258  }
259  }
260  __except (EXCEPTION_EXECUTE_HANDLER)
261  {
262  wprintf(L"Error.\n");
263  }
264 }
265 
267  _In_ PPH_HASHTABLE Hashtable
268  )
269 {
270  ULONG i;
271  ULONG expectedLookupMisses = 0;
272 
273  wprintf(L"Count: %u\n", Hashtable->Count);
274  wprintf(L"Allocated buckets: %u\n", Hashtable->AllocatedBuckets);
275  wprintf(L"Allocated entries: %u\n", Hashtable->AllocatedEntries);
276  wprintf(L"Next free entry: %d\n", Hashtable->FreeEntry);
277  wprintf(L"Next usable entry: %d\n", Hashtable->NextEntry);
278 
279  wprintf(L"Hash function: %s\n", PhpGetSymbolForAddress(Hashtable->HashFunction));
280  wprintf(L"Compare function: %s\n", PhpGetSymbolForAddress(Hashtable->CompareFunction));
281 
282  wprintf(L"\nBuckets:\n");
283 
284  for (i = 0; i < Hashtable->AllocatedBuckets; i++)
285  {
286  ULONG index;
287  ULONG count = 0;
288 
289  // Count the number of entries in this bucket.
290 
291  index = Hashtable->Buckets[i];
292 
293  while (index != -1)
294  {
295  index = PH_HASHTABLE_GET_ENTRY(Hashtable, index)->Next;
296  count++;
297  }
298 
299  if (count != 0)
300  {
301  expectedLookupMisses += count - 1;
302  }
303 
304  if (count != 0)
305  {
306  wprintf(L"%lu: ", i);
307 
308  // Print out the entry indicies.
309 
310  index = Hashtable->Buckets[i];
311 
312  while (index != -1)
313  {
314  wprintf(L"%lu", index);
315 
316  index = PH_HASHTABLE_GET_ENTRY(Hashtable, index)->Next;
317  count--;
318 
319  if (count != 0)
320  wprintf(L", ");
321  }
322 
323  wprintf(L"\n");
324  }
325  else
326  {
327  //wprintf(L"%u: (empty)\n");
328  }
329  }
330 
331  wprintf(L"\nExpected lookup misses: %lu\n", expectedLookupMisses);
332 }
333 
334 #ifdef DEBUG
335 static VOID PhpDebugCreateObjectHook(
336  _In_ PVOID Object,
337  _In_ SIZE_T Size,
338  _In_ ULONG Flags,
339  _In_ PPH_OBJECT_TYPE ObjectType
340  )
341 {
342  PhAcquireQueuedLockExclusive(&NewObjectListLock);
343 
344  if (NewObjectList)
345  {
346  PhReferenceObject(Object);
347  PhAddItemList(NewObjectList, Object);
348  }
349 
350  PhReleaseQueuedLockExclusive(&NewObjectListLock);
351 }
352 #endif
353 
354 #ifdef DEBUG
355 static VOID PhpDeleteNewObjectList(
356  VOID
357  )
358 {
359  if (NewObjectList)
360  {
361  ULONG i;
362 
363  for (i = 0; i < NewObjectList->Count; i++)
364  PhDereferenceObject(NewObjectList->Items[i]);
365 
366  PhDereferenceObject(NewObjectList);
367  NewObjectList = NULL;
368  }
369 }
370 #endif
371 
372 static BOOLEAN PhpStringHashtableCompareFunction(
373  _In_ PVOID Entry1,
374  _In_ PVOID Entry2
375  )
376 {
377  PSTRING_TABLE_ENTRY entry1 = Entry1;
378  PSTRING_TABLE_ENTRY entry2 = Entry2;
379 
380  return PhEqualString(entry1->String, entry2->String, FALSE);
381 }
382 
383 static ULONG PhpStringHashtableHashFunction(
384  _In_ PVOID Entry
385  )
386 {
387  PSTRING_TABLE_ENTRY entry = Entry;
388 
389  return PhHashBytes((PUCHAR)entry->String->Buffer, entry->String->Length);
390 }
391 
392 static int __cdecl PhpStringEntryCompareByCount(
393  _In_ const void *elem1,
394  _In_ const void *elem2
395  )
396 {
397  PSTRING_TABLE_ENTRY entry1 = *(PSTRING_TABLE_ENTRY *)elem1;
398  PSTRING_TABLE_ENTRY entry2 = *(PSTRING_TABLE_ENTRY *)elem2;
399 
400  return uintptrcmp(entry2->Count, entry1->Count);
401 }
402 
403 static NTSTATUS PhpLeakEnumerationRoutine(
404  _In_ LONG Reserved,
405  _In_ PVOID HeapHandle,
406  _In_ PVOID BaseAddress,
407  _In_ SIZE_T BlockSize,
408  _In_ ULONG StackTraceDepth,
409  _In_ PVOID *StackTrace
410  )
411 {
412  ULONG i;
413 
414  if (!InLeakDetection)
415  return 0;
416 
417  if (!HeapHandle) // means no more entries
418  return 0;
419 
420  if (ShowAllLeaks || HeapHandle == PhHeapHandle)
421  {
422  wprintf(L"Leak at 0x%Ix (%Iu bytes). Stack trace:\n", BaseAddress, BlockSize);
423 
424  for (i = 0; i < StackTraceDepth; i++)
425  {
426  PPH_STRING symbol;
427 
428  symbol = PhGetSymbolFromAddress(DebugConsoleSymbolProvider, (ULONG64)StackTrace[i], NULL, NULL, NULL, NULL);
429 
430  if (symbol)
431  wprintf(L"\t%s\n", symbol->Buffer);
432  else
433  wprintf(L"\t?\n");
434 
435  PhDereferenceObject(symbol);
436  }
437 
438  NumberOfLeaksShown++;
439  }
440 
441  NumberOfLeaks++;
442 
443  return 0;
444 }
445 
446 typedef struct _STOPWATCH
447 {
448  LARGE_INTEGER StartCounter;
449  LARGE_INTEGER EndCounter;
450  LARGE_INTEGER Frequency;
452 
453 static VOID PhInitializeStopwatch(
454  _Out_ PSTOPWATCH Stopwatch
455  )
456 {
457  Stopwatch->StartCounter.QuadPart = 0;
458  Stopwatch->EndCounter.QuadPart = 0;
459 }
460 
461 static VOID PhStartStopwatch(
462  _Inout_ PSTOPWATCH Stopwatch
463  )
464 {
465  NtQueryPerformanceCounter(&Stopwatch->StartCounter, &Stopwatch->Frequency);
466 }
467 
468 static VOID PhStopStopwatch(
469  _Inout_ PSTOPWATCH Stopwatch
470  )
471 {
472  NtQueryPerformanceCounter(&Stopwatch->EndCounter, NULL);
473 }
474 
475 static ULONG PhGetMillisecondsStopwatch(
476  _In_ PSTOPWATCH Stopwatch
477  )
478 {
479  LARGE_INTEGER countsPerMs;
480 
481  countsPerMs = Stopwatch->Frequency;
482  countsPerMs.QuadPart /= 1000;
483 
484  return (ULONG)((Stopwatch->EndCounter.QuadPart - Stopwatch->StartCounter.QuadPart) /
485  countsPerMs.QuadPart);
486 }
487 
489  _In_ PVOID Parameter
490  );
491 
492 typedef struct _RW_TEST_CONTEXT
493 {
494  PWSTR Name;
495 
496  PPHF_RW_LOCK_FUNCTION AcquireExclusive;
497  PPHF_RW_LOCK_FUNCTION AcquireShared;
498  PPHF_RW_LOCK_FUNCTION ReleaseExclusive;
499  PPHF_RW_LOCK_FUNCTION ReleaseShared;
500 
501  PVOID Parameter;
503 
504 static PH_BARRIER RwStartBarrier;
505 static LONG RwReadersActive;
506 static LONG RwWritersActive;
507 
508 static NTSTATUS PhpRwLockTestThreadStart(
509  _In_ PVOID Parameter
510  )
511 {
512 #define RW_ITERS 10000
513 #define RW_READ_ITERS 100
514 #define RW_WRITE_ITERS 10
515 #define RW_READ_SPIN_ITERS 60
516 #define RW_WRITE_SPIN_ITERS 200
517 
518  RW_TEST_CONTEXT context = *(PRW_TEST_CONTEXT)Parameter;
519  ULONG i;
520  ULONG j;
521  ULONG k;
522  ULONG m;
523 
524  PhWaitForBarrier(&RwStartBarrier, FALSE);
525 
526  for (i = 0; i < RW_ITERS; i++)
527  {
528  for (j = 0; j < RW_READ_ITERS; j++)
529  {
530  // Read zone
531 
532  context.AcquireShared(context.Parameter);
533  _InterlockedIncrement(&RwReadersActive);
534 
535  for (m = 0; m < RW_READ_SPIN_ITERS; m++)
536  YieldProcessor();
537 
538  if (RwWritersActive != 0)
539  {
540  wprintf(L"[fail]: writers active in read zone!\n");
541  NtWaitForSingleObject(NtCurrentProcess(), FALSE, NULL);
542  }
543 
544  _InterlockedDecrement(&RwReadersActive);
545  context.ReleaseShared(context.Parameter);
546 
547  // Spin for a while
548 
549  for (m = 0; m < 10; m++)
550  YieldProcessor();
551 
552  if (j == RW_READ_ITERS / 2)
553  {
554  // Write zone
555 
556  for (k = 0; k < RW_WRITE_ITERS; k++)
557  {
558  context.AcquireExclusive(context.Parameter);
559  _InterlockedIncrement(&RwWritersActive);
560 
561  for (m = 0; m < RW_WRITE_SPIN_ITERS; m++)
562  YieldProcessor();
563 
564  if (RwReadersActive != 0)
565  {
566  wprintf(L"[fail]: readers active in write zone!\n");
567  NtWaitForSingleObject(NtCurrentProcess(), FALSE, NULL);
568  }
569 
570  _InterlockedDecrement(&RwWritersActive);
571  context.ReleaseExclusive(context.Parameter);
572  }
573  }
574  }
575  }
576 
577  return STATUS_SUCCESS;
578 }
579 
580 static VOID PhpTestRwLock(
581  _In_ PRW_TEST_CONTEXT Context
582  )
583 {
584 #define RW_PROCESSORS 4
585 
586  STOPWATCH stopwatch;
587  ULONG i;
588  HANDLE threadHandles[RW_PROCESSORS];
589 
590  // Dummy
591 
592  Context->AcquireExclusive(Context->Parameter);
593  Context->ReleaseExclusive(Context->Parameter);
594  Context->AcquireShared(Context->Parameter);
595  Context->ReleaseShared(Context->Parameter);
596 
597  // Null test
598 
599  PhStartStopwatch(&stopwatch);
600 
601  for (i = 0; i < 2000000; i++)
602  {
603  Context->AcquireExclusive(Context->Parameter);
604  Context->ReleaseExclusive(Context->Parameter);
605  Context->AcquireShared(Context->Parameter);
606  Context->ReleaseShared(Context->Parameter);
607  }
608 
609  PhStopStopwatch(&stopwatch);
610 
611  wprintf(L"[null] %s: %ums\n", Context->Name, PhGetMillisecondsStopwatch(&stopwatch));
612 
613  // Stress test
614 
615  PhInitializeBarrier(&RwStartBarrier, RW_PROCESSORS + 1);
616  RwReadersActive = 0;
617  RwWritersActive = 0;
618 
619  for (i = 0; i < RW_PROCESSORS; i++)
620  {
621  threadHandles[i] = PhCreateThread(0, PhpRwLockTestThreadStart, Context);
622  }
623 
624  PhWaitForBarrier(&RwStartBarrier, FALSE);
625  PhStartStopwatch(&stopwatch);
626  NtWaitForMultipleObjects(RW_PROCESSORS, threadHandles, WaitAll, FALSE, NULL);
627  PhStopStopwatch(&stopwatch);
628 
629  for (i = 0; i < RW_PROCESSORS; i++)
630  NtClose(threadHandles[i]);
631 
632  wprintf(L"[strs] %s: %ums\n", Context->Name, PhGetMillisecondsStopwatch(&stopwatch));
633 }
634 
636  _In_ PRTL_CRITICAL_SECTION CriticalSection
637  )
638 {
639  RtlEnterCriticalSection(CriticalSection);
640 }
641 
643  _In_ PRTL_CRITICAL_SECTION CriticalSection
644  )
645 {
646  RtlLeaveCriticalSection(CriticalSection);
647 }
648 
650  _In_ PPH_QUEUED_LOCK QueuedLock
651  )
652 {
653  PhReleaseQueuedLockExclusive(QueuedLock);
654 }
655 
657  _In_ PVOID Parameter
658  )
659 {
660  PH_AUTO_POOL autoPool;
661 
662  PhInitializeAutoPool(&autoPool);
663 
664  DebugConsoleSymbolProvider = PhCreateSymbolProvider(NtCurrentProcessId());
665 
666  {
667  WCHAR buffer[512];
668  UNICODE_STRING name = RTL_CONSTANT_STRING(L"_NT_SYMBOL_PATH");
669  UNICODE_STRING var;
670  PPH_STRING newSearchPath;
671 
672  var.Buffer = buffer;
673  var.MaximumLength = sizeof(buffer);
674 
675  if (!NT_SUCCESS(RtlQueryEnvironmentVariable_U(NULL, &name, &var)))
676  buffer[0] = 0;
677 
678  newSearchPath = PhFormatString(L"%s;%s", buffer, PhApplicationDirectory->Buffer);
679  PhSetSearchPathSymbolProvider(DebugConsoleSymbolProvider, newSearchPath->Buffer);
680  PhDereferenceObject(newSearchPath);
681  }
682 
683  PhEnumGenericModules(NtCurrentProcessId(), NtCurrentProcess(),
684  0, PhpLoadCurrentProcessSymbolsCallback, DebugConsoleSymbolProvider);
685 
686 #ifdef DEBUG
687  PhInitializeQueuedLock(&NewObjectListLock);
688  PhDbgCreateObjectHook = PhpDebugCreateObjectHook;
689 #endif
690 
691  wprintf(L"Press Ctrl+C or type \"exit\" to close the debug console. Type \"help\" for a list of commands.\n");
692 
693  while (TRUE)
694  {
695  static PWSTR delims = L" \t";
696  static PWSTR commandDebugOnly = L"This command is not available on non-debug builds.\n";
697 
698  WCHAR line[201];
699  ULONG inputLength;
700  PWSTR context;
701  PWSTR command;
702 
703  wprintf(L"dbg>");
704 
705  if (!fgetws(line, sizeof(line) / 2 - 1, stdin))
706  break;
707 
708  // Remove the terminating new line character.
709 
710  inputLength = (ULONG)PhCountStringZ(line);
711 
712  if (inputLength != 0)
713  line[inputLength - 1] = 0;
714 
715  context = NULL;
716  command = wcstok_s(line, delims, &context);
717 
718  if (!command)
719  {
720  continue;
721  }
722  else if (PhEqualStringZ(command, L"help", TRUE))
723  {
724  wprintf(
725  L"Commands:\n"
726  L"exit\n"
727  L"testperf\n"
728  L"testlocks\n"
729  L"stats\n"
730  L"objects [type-name-filter]\n"
731  L"objtrace object-address\n"
732  L"objmksnap\n"
733  L"objcmpsnap\n"
734  L"objmknew\n"
735  L"objdelnew\n"
736  L"objviewnew\n"
737  L"dumpobj\n"
738  L"dumpautopool\n"
739  L"threads\n"
740  L"provthreads\n"
741  L"workqueues\n"
742  L"procrecords\n"
743  L"procitem\n"
744  L"uniquestr\n"
745  L"enableleakdetect\n"
746  L"leakdetect\n"
747  L"mem\n"
748  );
749  }
750  else if (PhEqualStringZ(command, L"exit", TRUE))
751  {
753  }
754  else if (PhEqualStringZ(command, L"testperf", TRUE))
755  {
756  STOPWATCH stopwatch;
757  ULONG i;
758  PPH_STRING testString;
759  RTL_CRITICAL_SECTION testCriticalSection;
760  PH_FAST_LOCK testFastLock;
761  PH_QUEUED_LOCK testQueuedLock;
762 
763  // Control (string reference counting)
764 
765  testString = PhCreateString(L"");
766  PhReferenceObject(testString);
767  PhDereferenceObject(testString);
768  PhStartStopwatch(&stopwatch);
769 
770  for (i = 0; i < 10000000; i++)
771  {
772  PhReferenceObject(testString);
773  PhDereferenceObject(testString);
774  }
775 
776  PhStopStopwatch(&stopwatch);
777  PhDereferenceObject(testString);
778 
779  wprintf(L"Referencing: %ums\n", PhGetMillisecondsStopwatch(&stopwatch));
780 
781  // Critical section
782 
783  RtlInitializeCriticalSection(&testCriticalSection);
784  RtlEnterCriticalSection(&testCriticalSection);
785  RtlLeaveCriticalSection(&testCriticalSection);
786  PhStartStopwatch(&stopwatch);
787 
788  for (i = 0; i < 10000000; i++)
789  {
790  RtlEnterCriticalSection(&testCriticalSection);
791  RtlLeaveCriticalSection(&testCriticalSection);
792  }
793 
794  PhStopStopwatch(&stopwatch);
795  RtlDeleteCriticalSection(&testCriticalSection);
796 
797  wprintf(L"Critical section: %ums\n", PhGetMillisecondsStopwatch(&stopwatch));
798 
799  // Fast lock
800 
801  PhInitializeFastLock(&testFastLock);
802  PhAcquireFastLockExclusive(&testFastLock);
803  PhReleaseFastLockExclusive(&testFastLock);
804  PhStartStopwatch(&stopwatch);
805 
806  for (i = 0; i < 10000000; i++)
807  {
808  PhAcquireFastLockExclusive(&testFastLock);
809  PhReleaseFastLockExclusive(&testFastLock);
810  }
811 
812  PhStopStopwatch(&stopwatch);
813  PhDeleteFastLock(&testFastLock);
814 
815  wprintf(L"Fast lock: %ums\n", PhGetMillisecondsStopwatch(&stopwatch));
816 
817  // Queued lock
818 
819  PhInitializeQueuedLock(&testQueuedLock);
820  PhAcquireQueuedLockExclusive(&testQueuedLock);
821  PhReleaseQueuedLockExclusive(&testQueuedLock);
822  PhStartStopwatch(&stopwatch);
823 
824  for (i = 0; i < 10000000; i++)
825  {
826  PhAcquireQueuedLockExclusive(&testQueuedLock);
827  PhReleaseQueuedLockExclusive(&testQueuedLock);
828  }
829 
830  PhStopStopwatch(&stopwatch);
831 
832  wprintf(L"Queued lock: %ums\n", PhGetMillisecondsStopwatch(&stopwatch));
833  }
834  else if (PhEqualStringZ(command, L"testlocks", TRUE))
835  {
836  RW_TEST_CONTEXT testContext;
837  PH_FAST_LOCK fastLock;
838  PH_QUEUED_LOCK queuedLock;
839  RTL_CRITICAL_SECTION criticalSection;
840 
841  testContext.Name = L"FastLock";
842  testContext.AcquireExclusive = PhfAcquireFastLockExclusive;
843  testContext.AcquireShared = PhfAcquireFastLockShared;
844  testContext.ReleaseExclusive = PhfReleaseFastLockExclusive;
845  testContext.ReleaseShared = PhfReleaseFastLockShared;
846  testContext.Parameter = &fastLock;
847  PhInitializeFastLock(&fastLock);
848  PhpTestRwLock(&testContext);
849  PhDeleteFastLock(&fastLock);
850 
851  testContext.Name = L"QueuedLock";
852  testContext.AcquireExclusive = PhfAcquireQueuedLockExclusive;
853  testContext.AcquireShared = PhfAcquireQueuedLockShared;
854  testContext.ReleaseExclusive = PhfReleaseQueuedLockExclusive;
855  testContext.ReleaseShared = PhfReleaseQueuedLockShared;
856  testContext.Parameter = &queuedLock;
857  PhInitializeQueuedLock(&queuedLock);
858  PhpTestRwLock(&testContext);
859 
860  testContext.Name = L"CriticalSection";
861  testContext.AcquireExclusive = PhfAcquireCriticalSection;
862  testContext.AcquireShared = PhfAcquireCriticalSection;
863  testContext.ReleaseExclusive = PhfReleaseCriticalSection;
864  testContext.ReleaseShared = PhfReleaseCriticalSection;
865  testContext.Parameter = &criticalSection;
866  RtlInitializeCriticalSection(&criticalSection);
867  PhpTestRwLock(&testContext);
868  RtlDeleteCriticalSection(&criticalSection);
869 
870  testContext.Name = L"QueuedLockMutex";
871  testContext.AcquireExclusive = PhfAcquireQueuedLockExclusive;
872  testContext.AcquireShared = PhfAcquireQueuedLockExclusive;
873  testContext.ReleaseExclusive = PhfReleaseQueuedLockExclusive;
874  testContext.ReleaseShared = PhfReleaseQueuedLockExclusive;
875  testContext.Parameter = &queuedLock;
876  PhInitializeQueuedLock(&queuedLock);
877  PhpTestRwLock(&testContext);
878  }
879  else if (PhEqualStringZ(command, L"stats", TRUE))
880  {
881 #ifdef DEBUG
882  wprintf(L"Object small free list count: %u\n", PhObjectSmallFreeList.Count);
883  wprintf(L"Statistics:\n");
884 #define PRINT_STATISTIC(Name) wprintf(L#Name L": %u\n", PhLibStatisticsBlock.Name);
885 
886  PRINT_STATISTIC(BaseThreadsCreated);
887  PRINT_STATISTIC(BaseThreadsCreateFailed);
888  PRINT_STATISTIC(BaseStringBuildersCreated);
889  PRINT_STATISTIC(BaseStringBuildersResized);
890  PRINT_STATISTIC(RefObjectsCreated);
891  PRINT_STATISTIC(RefObjectsDestroyed);
892  PRINT_STATISTIC(RefObjectsAllocated);
893  PRINT_STATISTIC(RefObjectsFreed);
894  PRINT_STATISTIC(RefObjectsAllocatedFromSmallFreeList);
895  PRINT_STATISTIC(RefObjectsFreedToSmallFreeList);
896  PRINT_STATISTIC(RefObjectsAllocatedFromTypeFreeList);
897  PRINT_STATISTIC(RefObjectsFreedToTypeFreeList);
898  PRINT_STATISTIC(RefObjectsDeleteDeferred);
899  PRINT_STATISTIC(RefAutoPoolsCreated);
900  PRINT_STATISTIC(RefAutoPoolsDestroyed);
901  PRINT_STATISTIC(RefAutoPoolsDynamicAllocated);
902  PRINT_STATISTIC(RefAutoPoolsDynamicResized);
903  PRINT_STATISTIC(QlBlockSpins);
904  PRINT_STATISTIC(QlBlockWaits);
905  PRINT_STATISTIC(QlAcquireExclusiveBlocks);
906  PRINT_STATISTIC(QlAcquireSharedBlocks);
907  PRINT_STATISTIC(WqWorkQueueThreadsCreated);
908  PRINT_STATISTIC(WqWorkQueueThreadsCreateFailed);
909  PRINT_STATISTIC(WqWorkItemsQueued);
910 
911 #else
912  wprintf(commandDebugOnly);
913 #endif
914  }
915  else if (PhEqualStringZ(command, L"objects", TRUE))
916  {
917 #ifdef DEBUG
918  PWSTR typeFilter = wcstok_s(NULL, delims, &context);
919  PLIST_ENTRY currentEntry;
920  ULONG totalNumberOfObjects = 0;
921  //SIZE_T totalNumberOfBytes = 0;
922 
923  if (typeFilter)
924  wcslwr(typeFilter);
925 
926  PhAcquireQueuedLockShared(&PhDbgObjectListLock);
927 
928  currentEntry = PhDbgObjectListHead.Flink;
929 
930  while (currentEntry != &PhDbgObjectListHead)
931  {
932  PPH_OBJECT_HEADER objectHeader;
933  WCHAR typeName[32];
934 
935  objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry);
936 
937  // Make sure the object isn't being destroyed.
938  if (!PhReferenceObjectSafe(PhObjectHeaderToObject(objectHeader)))
939  {
940  currentEntry = currentEntry->Flink;
941  continue;
942  }
943 
944  totalNumberOfObjects++;
945  //totalNumberOfBytes += objectHeader->Size;
946 
947  if (typeFilter)
948  {
949  wcscpy_s(typeName, sizeof(typeName) / 2, PhGetObjectType(PhObjectHeaderToObject(objectHeader))->Name);
950  wcslwr(typeName);
951  }
952 
953  if (
954  !typeFilter ||
955  (typeFilter && wcsstr(typeName, typeFilter))
956  )
957  {
958  PhpPrintObjectInfo(objectHeader, 1);
959  }
960 
961  currentEntry = currentEntry->Flink;
963  }
964 
965  PhReleaseQueuedLockShared(&PhDbgObjectListLock);
966 
967  wprintf(L"\n");
968  wprintf(L"Total number: %lu\n", totalNumberOfObjects);
969  /*wprintf(L"Total size (excl. header): %s\n",
970  ((PPH_STRING)PhAutoDereferenceObject(PhFormatSize(totalNumberOfBytes, 1)))->Buffer);*/
971  wprintf(L"Total overhead (header): %s\n",
973  PhFormatSize(PhAddObjectHeaderSize(0) * totalNumberOfObjects, 1)
974  ))->Buffer);
975 #else
976  wprintf(commandDebugOnly);
977 #endif
978  }
979  else if (PhEqualStringZ(command, L"objtrace", TRUE))
980  {
981 #ifdef DEBUG
982  PWSTR objectAddress = wcstok_s(NULL, delims, &context);
983  PH_STRINGREF objectAddressString;
984  ULONG64 address;
985 
986  if (!objectAddress)
987  {
988  wprintf(L"Missing object address.\n");
989  goto EndCommand;
990  }
991 
992  PhInitializeStringRef(&objectAddressString, objectAddress);
993 
994  if (PhStringToInteger64(&objectAddressString, 16, &address))
995  {
996  PPH_OBJECT_HEADER objectHeader = (PPH_OBJECT_HEADER)PhObjectToObjectHeader((PVOID)address);
997  PVOID stackBackTrace[16];
998  ULONG i;
999 
1000  // The address may not be valid.
1001  __try
1002  {
1003  memcpy(stackBackTrace, objectHeader->StackBackTrace, 16 * sizeof(PVOID));
1004  }
1005  __except (EXCEPTION_EXECUTE_HANDLER)
1006  {
1007  PPH_STRING message;
1008 
1009  message = PhGetNtMessage(GetExceptionCode());
1010  PhAutoDereferenceObject(message);
1011  wprintf(L"Error: %s\n", PhGetString(message));
1012 
1013  goto EndCommand;
1014  }
1015 
1016  for (i = 0; i < 16; i++)
1017  {
1018  if (!stackBackTrace[i])
1019  break;
1020 
1021  wprintf(L"%s\n", PhpGetSymbolForAddress(stackBackTrace[i]));
1022  }
1023  }
1024  else
1025  {
1026  wprintf(L"Invalid object address.\n");
1027  }
1028 #else
1029  wprintf(commandDebugOnly);
1030 #endif
1031  }
1032  else if (PhEqualStringZ(command, L"objmksnap", TRUE))
1033  {
1034 #ifdef DEBUG
1035  PLIST_ENTRY currentEntry;
1036 
1037  if (ObjectListSnapshot)
1038  {
1039  PhDereferenceObject(ObjectListSnapshot);
1040  ObjectListSnapshot = NULL;
1041  }
1042 
1043  ObjectListSnapshot = PhCreateSimpleHashtable(100);
1044 
1045  PhAcquireQueuedLockShared(&PhDbgObjectListLock);
1046 
1047  currentEntry = PhDbgObjectListHead.Flink;
1048 
1049  while (currentEntry != &PhDbgObjectListHead)
1050  {
1051  PPH_OBJECT_HEADER objectHeader;
1052 
1053  objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry);
1054  currentEntry = currentEntry->Flink;
1055 
1056  if (PhObjectHeaderToObject(objectHeader) != ObjectListSnapshot)
1057  PhAddItemSimpleHashtable(ObjectListSnapshot, objectHeader, NULL);
1058  }
1059 
1060  PhReleaseQueuedLockShared(&PhDbgObjectListLock);
1061 #else
1062  wprintf(commandDebugOnly);
1063 #endif
1064  }
1065  else if (PhEqualStringZ(command, L"objcmpsnap", TRUE))
1066  {
1067 #ifdef DEBUG
1068  PLIST_ENTRY currentEntry;
1069  PPH_LIST newObjects;
1070  ULONG i;
1071 
1072  if (!ObjectListSnapshot)
1073  {
1074  wprintf(L"No snapshot.\n");
1075  goto EndCommand;
1076  }
1077 
1078  newObjects = PhCreateList(10);
1079 
1080  PhAcquireQueuedLockShared(&PhDbgObjectListLock);
1081 
1082  currentEntry = PhDbgObjectListHead.Flink;
1083 
1084  while (currentEntry != &PhDbgObjectListHead)
1085  {
1086  PPH_OBJECT_HEADER objectHeader;
1087 
1088  objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry);
1089  currentEntry = currentEntry->Flink;
1090 
1091  if (
1092  PhObjectHeaderToObject(objectHeader) != ObjectListSnapshot &&
1093  PhObjectHeaderToObject(objectHeader) != newObjects
1094  )
1095  {
1096  if (!PhFindItemSimpleHashtable(ObjectListSnapshot, objectHeader))
1097  {
1098  if (PhReferenceObjectSafe(PhObjectHeaderToObject(objectHeader)))
1099  PhAddItemList(newObjects, objectHeader);
1100  }
1101  }
1102  }
1103 
1104  PhReleaseQueuedLockShared(&PhDbgObjectListLock);
1105 
1106  for (i = 0; i < newObjects->Count; i++)
1107  {
1108  PPH_OBJECT_HEADER objectHeader = newObjects->Items[i];
1109 
1110  PhpPrintObjectInfo(objectHeader, 1);
1111 
1113  }
1114 
1115  PhDereferenceObject(newObjects);
1116 #else
1117  wprintf(commandDebugOnly);
1118 #endif
1119  }
1120  else if (PhEqualStringZ(command, L"objmknew", TRUE))
1121  {
1122 #ifdef DEBUG
1123  PhAcquireQueuedLockExclusive(&NewObjectListLock);
1124  PhpDeleteNewObjectList();
1125  PhReleaseQueuedLockExclusive(&NewObjectListLock);
1126 
1127  // Creation needs to be done outside of the lock,
1128  // otherwise a deadlock will occur.
1129  NewObjectList = PhCreateList(100);
1130 #else
1131  wprintf(commandDebugOnly);
1132 #endif
1133  }
1134  else if (PhEqualStringZ(command, L"objdelnew", TRUE))
1135  {
1136 #ifdef DEBUG
1137  PhAcquireQueuedLockExclusive(&NewObjectListLock);
1138  PhpDeleteNewObjectList();
1139  PhReleaseQueuedLockExclusive(&NewObjectListLock);
1140 #else
1141  wprintf(commandDebugOnly);
1142 #endif
1143  }
1144  else if (PhEqualStringZ(command, L"objviewnew", TRUE))
1145  {
1146 #ifdef DEBUG
1147  ULONG i;
1148 
1149  PhAcquireQueuedLockExclusive(&NewObjectListLock);
1150 
1151  if (!NewObjectList)
1152  {
1153  wprintf(L"Object creation hooking not active.\n");
1154  PhReleaseQueuedLockExclusive(&NewObjectListLock);
1155  goto EndCommand;
1156  }
1157 
1158  for (i = 0; i < NewObjectList->Count; i++)
1159  {
1160  PhpPrintObjectInfo(PhObjectToObjectHeader(NewObjectList->Items[i]), 1);
1161  }
1162 
1163  PhReleaseQueuedLockExclusive(&NewObjectListLock);
1164 #else
1165  wprintf(commandDebugOnly);
1166 #endif
1167  }
1168  else if (PhEqualStringZ(command, L"dumpobj", TRUE))
1169  {
1170  PWSTR addressString = wcstok_s(NULL, delims, &context);
1171  PH_STRINGREF addressStringRef;
1172  ULONG64 address;
1173 
1174  if (!addressString)
1175  goto EndCommand;
1176 
1177  PhInitializeStringRef(&addressStringRef, addressString);
1178 
1179  if (PhStringToInteger64(&addressStringRef, 16, &address))
1180  {
1181  PhpDumpObjectInfo((PPH_OBJECT_HEADER)PhObjectToObjectHeader((PVOID)address));
1182  }
1183  }
1184  else if (PhEqualStringZ(command, L"dumpautopool", TRUE))
1185  {
1186  PWSTR addressString = wcstok_s(NULL, delims, &context);
1187  PH_STRINGREF addressStringRef;
1188  ULONG64 address;
1189 
1190  if (!addressString)
1191  goto EndCommand;
1192 
1193  PhInitializeStringRef(&addressStringRef, addressString);
1194 
1195  if (PhStringToInteger64(&addressStringRef, 16, &address))
1196  {
1197  PPH_AUTO_POOL userAutoPool = (PPH_AUTO_POOL)address;
1198  ULONG i;
1199 
1200  __try
1201  {
1202  wprintf(L"Static count: %u\n", userAutoPool->StaticCount);
1203  wprintf(L"Dynamic count: %u\n", userAutoPool->DynamicCount);
1204  wprintf(L"Dynamic allocated: %u\n", userAutoPool->DynamicAllocated);
1205 
1206  wprintf(L"Static objects:\n");
1207 
1208  for (i = 0; i < userAutoPool->StaticCount; i++)
1209  PhpPrintObjectInfo(PhObjectToObjectHeader(userAutoPool->StaticObjects[i]), 0);
1210 
1211  wprintf(L"Dynamic objects:\n");
1212 
1213  for (i = 0; i < userAutoPool->DynamicCount; i++)
1214  PhpPrintObjectInfo(PhObjectToObjectHeader(userAutoPool->DynamicObjects[i]), 0);
1215  }
1216  __except (EXCEPTION_EXECUTE_HANDLER)
1217  {
1218  goto EndCommand;
1219  }
1220  }
1221  }
1222  else if (PhEqualStringZ(command, L"threads", TRUE))
1223  {
1224 #ifdef DEBUG
1225  PLIST_ENTRY currentEntry;
1226 
1227  PhAcquireQueuedLockShared(&PhDbgThreadListLock);
1228 
1229  currentEntry = PhDbgThreadListHead.Flink;
1230 
1231  while (currentEntry != &PhDbgThreadListHead)
1232  {
1233  PPHP_BASE_THREAD_DBG dbg;
1234 
1235  dbg = CONTAINING_RECORD(currentEntry, PHP_BASE_THREAD_DBG, ListEntry);
1236 
1237  wprintf(L"Thread %u\n", (ULONG)dbg->ClientId.UniqueThread);
1238  wprintf(L"\tStart Address: %s\n", PhpGetSymbolForAddress(dbg->StartAddress));
1239  wprintf(L"\tParameter: %Ix\n", dbg->Parameter);
1240  wprintf(L"\tCurrent auto-pool: %Ix\n", dbg->CurrentAutoPool);
1241 
1242  currentEntry = currentEntry->Flink;
1243  }
1244 
1245  PhReleaseQueuedLockShared(&PhDbgThreadListLock);
1246 #else
1247  wprintf(commandDebugOnly);
1248 #endif
1249  }
1250  else if (PhEqualStringZ(command, L"provthreads", TRUE))
1251  {
1252 #ifdef DEBUG
1253  ULONG i;
1254 
1255  if (PhDbgProviderList)
1256  {
1257  PhAcquireQueuedLockShared(&PhDbgProviderListLock);
1258 
1259  for (i = 0; i < PhDbgProviderList->Count; i++)
1260  {
1261  PPH_PROVIDER_THREAD providerThread = PhDbgProviderList->Items[i];
1262  THREAD_BASIC_INFORMATION basicInfo;
1263  PLIST_ENTRY providerEntry;
1264 
1265  if (providerThread->ThreadHandle)
1266  {
1267  PhGetThreadBasicInformation(providerThread->ThreadHandle, &basicInfo);
1268  wprintf(L"Thread %u\n", (ULONG)basicInfo.ClientId.UniqueThread);
1269  }
1270  else
1271  {
1272  wprintf(L"Thread not running\n");
1273  }
1274 
1275  PhAcquireQueuedLockExclusive(&providerThread->Lock);
1276 
1277  providerEntry = providerThread->ListHead.Flink;
1278 
1279  while (providerEntry != &providerThread->ListHead)
1280  {
1281  PPH_PROVIDER_REGISTRATION registration;
1282 
1283  registration = CONTAINING_RECORD(providerEntry, PH_PROVIDER_REGISTRATION, ListEntry);
1284 
1285  wprintf(L"\tProvider registration at %Ix\n", registration);
1286  wprintf(L"\t\tEnabled: %s\n", registration->Enabled ? L"Yes" : L"No");
1287  wprintf(L"\t\tFunction: %s\n", PhpGetSymbolForAddress(registration->Function));
1288 
1289  if (registration->Object)
1290  {
1291  wprintf(L"\t\tObject:\n");
1292  PhpPrintObjectInfo(PhObjectToObjectHeader(registration->Object), 0);
1293  }
1294 
1295  providerEntry = providerEntry->Flink;
1296  }
1297 
1298  PhReleaseQueuedLockExclusive(&providerThread->Lock);
1299 
1300  wprintf(L"\n");
1301  }
1302 
1303  PhReleaseQueuedLockShared(&PhDbgProviderListLock);
1304  }
1305 #else
1306  wprintf(commandDebugOnly);
1307 #endif
1308  }
1309  else if (PhEqualStringZ(command, L"workqueues", TRUE))
1310  {
1311 #ifdef DEBUG
1312  ULONG i;
1313 
1314  if (PhDbgWorkQueueList)
1315  {
1316  PhAcquireQueuedLockShared(&PhDbgWorkQueueListLock);
1317 
1318  for (i = 0; i < PhDbgWorkQueueList->Count; i++)
1319  {
1320  PPH_WORK_QUEUE workQueue = PhDbgWorkQueueList->Items[i];
1321  PLIST_ENTRY workQueueItemEntry;
1322 
1323  wprintf(L"Work queue at %s\n", PhpGetSymbolForAddress(workQueue));
1324  wprintf(L"Maximum threads: %u\n", workQueue->MaximumThreads);
1325  wprintf(L"Minimum threads: %u\n", workQueue->MinimumThreads);
1326  wprintf(L"No work timeout: %d\n", workQueue->NoWorkTimeout);
1327 
1328  wprintf(L"Current threads: %u\n", workQueue->CurrentThreads);
1329  wprintf(L"Busy count: %u\n", workQueue->BusyCount);
1330 
1332 
1333  // List the items backwards.
1334  workQueueItemEntry = workQueue->QueueListHead.Blink;
1335 
1336  while (workQueueItemEntry != &workQueue->QueueListHead)
1337  {
1338  PPH_WORK_QUEUE_ITEM workQueueItem;
1339 
1340  workQueueItem = CONTAINING_RECORD(workQueueItemEntry, PH_WORK_QUEUE_ITEM, ListEntry);
1341 
1342  wprintf(L"\tWork queue item at %Ix\n", workQueueItem);
1343  wprintf(L"\t\tFunction: %s\n", PhpGetSymbolForAddress(workQueueItem->Function));
1344  wprintf(L"\t\tContext: %Ix\n", workQueueItem->Context);
1345 
1346  workQueueItemEntry = workQueueItemEntry->Blink;
1347  }
1348 
1350 
1351  wprintf(L"\n");
1352  }
1353 
1354  PhReleaseQueuedLockShared(&PhDbgWorkQueueListLock);
1355  }
1356 #else
1357  wprintf(commandDebugOnly);
1358 #endif
1359  }
1360  else if (PhEqualStringZ(command, L"procrecords", TRUE))
1361  {
1362  PPH_PROCESS_RECORD record;
1363  ULONG i;
1364  SYSTEMTIME systemTime;
1365  PPH_PROCESS_RECORD startRecord;
1366 
1368 
1369  for (i = 0; i < PhProcessRecordList->Count; i++)
1370  {
1372 
1373  PhLargeIntegerToLocalSystemTime(&systemTime, &record->CreateTime);
1374  wprintf(L"Records for %s %s:\n",
1375  ((PPH_STRING)PhAutoDereferenceObject(PhFormatDate(&systemTime, NULL)))->Buffer,
1376  ((PPH_STRING)PhAutoDereferenceObject(PhFormatTime(&systemTime, NULL)))->Buffer
1377  );
1378 
1379  startRecord = record;
1380 
1381  do
1382  {
1383  wprintf(L"\tRecord at %Ix: %s (%u) (refs: %d)\n", record, record->ProcessName->Buffer, (ULONG)record->ProcessId, record->RefCount);
1384 
1385  if (record->FileName)
1386  wprintf(L"\t\t%s\n", record->FileName->Buffer);
1387 
1388  record = CONTAINING_RECORD(record->ListEntry.Flink, PH_PROCESS_RECORD, ListEntry);
1389  } while (record != startRecord);
1390  }
1391 
1393  }
1394  else if (PhEqualStringZ(command, L"procitem", TRUE))
1395  {
1396  PWSTR filterString;
1397  PH_STRINGREF filterRef;
1398  ULONG64 filter64;
1399  LONG_PTR processIdFilter = -9; // can't use -2, -1 or 0 because they're all used for process IDs
1400  ULONG_PTR processAddressFilter = 0;
1401  PWSTR imageNameFilter = NULL;
1402  BOOLEAN showAll = FALSE;
1403  PPH_PROCESS_ITEM *processes;
1404  ULONG numberOfProcesses;
1405  ULONG i;
1406 
1407  filterString = wcstok_s(NULL, delims, &context);
1408 
1409  if (filterString)
1410  {
1411  PhInitializeStringRef(&filterRef, filterString);
1412 
1413  if (PhStringToInteger64(&filterRef, 10, &filter64))
1414  processIdFilter = (LONG_PTR)filter64;
1415  if (PhStringToInteger64(&filterRef, 16, &filter64))
1416  processAddressFilter = (ULONG_PTR)filter64;
1417 
1418  imageNameFilter = filterString;
1419  }
1420  else
1421  {
1422  showAll = TRUE;
1423  }
1424 
1425  PhEnumProcessItems(&processes, &numberOfProcesses);
1426 
1427  for (i = 0; i < numberOfProcesses; i++)
1428  {
1429  PPH_PROCESS_ITEM process = processes[i];
1430 
1431  if (
1432  showAll ||
1433  (processIdFilter != -9 && (LONG_PTR)process->ProcessId == processIdFilter) ||
1434  (processAddressFilter != 0 && (ULONG_PTR)process == processAddressFilter) ||
1435  (imageNameFilter && PhMatchWildcards(imageNameFilter, process->ProcessName->Buffer, TRUE))
1436  )
1437  {
1438  wprintf(L"Process item at %Ix: %s (%u)\n", process, process->ProcessName->Buffer, (ULONG)process->ProcessId);
1439  wprintf(L"\tRecord at %Ix\n", process->Record);
1440  wprintf(L"\tQuery handle %Ix\n", process->QueryHandle);
1441  wprintf(L"\tFile name at %Ix: %s\n", process->FileName, PhGetStringOrDefault(process->FileName, L"(null)"));
1442  wprintf(L"\tCommand line at %Ix: %s\n", process->CommandLine, PhGetStringOrDefault(process->CommandLine, L"(null)"));
1443  wprintf(L"\tFlags: %u\n", process->Flags);
1444  wprintf(L"\n");
1445  }
1446  }
1447 
1448  PhDereferenceObjects(processes, numberOfProcesses);
1449  }
1450  else if (PhEqualStringZ(command, L"uniquestr", TRUE))
1451  {
1452 #ifdef DEBUG
1453  PLIST_ENTRY currentEntry;
1454  PPH_HASHTABLE hashtable;
1455  PPH_LIST list;
1456  PSTRING_TABLE_ENTRY stringEntry;
1457  ULONG enumerationKey;
1458  ULONG i;
1459 
1460  hashtable = PhCreateHashtable(
1461  sizeof(STRING_TABLE_ENTRY),
1462  PhpStringHashtableCompareFunction,
1463  PhpStringHashtableHashFunction,
1464  1024
1465  );
1466 
1467  PhAcquireQueuedLockShared(&PhDbgObjectListLock);
1468 
1469  currentEntry = PhDbgObjectListHead.Flink;
1470 
1471  while (currentEntry != &PhDbgObjectListHead)
1472  {
1473  PPH_OBJECT_HEADER objectHeader;
1474  PPH_STRING string;
1475  STRING_TABLE_ENTRY localStringEntry;
1476  BOOLEAN added;
1477 
1478  objectHeader = CONTAINING_RECORD(currentEntry, PH_OBJECT_HEADER, ObjectListEntry);
1479  currentEntry = currentEntry->Flink;
1480  string = PhObjectHeaderToObject(objectHeader);
1481 
1482  // Make sure this is a string.
1483  if (PhGetObjectType(string) != PhStringType)
1484  continue;
1485 
1486  // Make sure the object isn't being destroyed.
1487  if (!PhReferenceObjectSafe(string))
1488  continue;
1489 
1490  localStringEntry.String = string;
1491  stringEntry = PhAddEntryHashtableEx(hashtable, &localStringEntry, &added);
1492 
1493  if (added)
1494  {
1495  stringEntry->Count = 1;
1496  PhReferenceObject(string);
1497  }
1498  else
1499  {
1500  stringEntry->Count++;
1501  }
1502 
1504  }
1505 
1506  PhReleaseQueuedLockShared(&PhDbgObjectListLock);
1507 
1508  // Sort the string entries by count.
1509 
1510  list = PhCreateList(hashtable->Count);
1511 
1512  enumerationKey = 0;
1513 
1514  while (PhEnumHashtable(hashtable, &stringEntry, &enumerationKey))
1515  {
1516  PhAddItemList(list, stringEntry);
1517  }
1518 
1519  qsort(list->Items, list->Count, sizeof(PSTRING_TABLE_ENTRY), PhpStringEntryCompareByCount);
1520 
1521  // Display...
1522 
1523  for (i = 0; i < 40 && i < list->Count; i++)
1524  {
1525  stringEntry = list->Items[i];
1526  wprintf(L"%Iu\t%.64s\n", stringEntry->Count, stringEntry->String->Buffer);
1527  }
1528 
1529  wprintf(L"\nTotal unique strings: %u\n", list->Count);
1530 
1531  // Cleanup
1532 
1533  for (i = 0; i < list->Count; i++)
1534  {
1535  stringEntry = list->Items[i];
1536  PhDereferenceObject(stringEntry->String);
1537  }
1538 
1539  PhDereferenceObject(list);
1540  PhDereferenceObject(hashtable);
1541 #else
1542  wprintf(commandDebugOnly);
1543 #endif
1544  }
1545  else if (PhEqualStringZ(command, L"enableleakdetect", TRUE))
1546  {
1547  HEAP_DEBUGGING_INFORMATION debuggingInfo;
1548 
1549  memset(&debuggingInfo, 0, sizeof(HEAP_DEBUGGING_INFORMATION));
1550  debuggingInfo.StackTraceDepth = 32;
1551  debuggingInfo.HeapLeakEnumerationRoutine = PhpLeakEnumerationRoutine;
1552 
1554  {
1555  wprintf(L"Unable to initialize heap debugging. Make sure that you are using Windows 7 or above.");
1556  }
1557  }
1558  else if (PhEqualStringZ(command, L"leakdetect", TRUE))
1559  {
1560  VOID (NTAPI *rtlDetectHeapLeaks)(VOID);
1561  PWSTR options = wcstok_s(NULL, delims, &context);
1562 
1563  rtlDetectHeapLeaks = PhGetModuleProcAddress(L"ntdll.dll", "RtlDetectHeapLeaks");
1564 
1565  if (!(NtCurrentPeb()->NtGlobalFlag & FLG_USER_STACK_TRACE_DB))
1566  {
1567  wprintf(L"Warning: user-mode stack trace database is not enabled. Stack traces will not be displayed.\n");
1568  }
1569 
1570  ShowAllLeaks = FALSE;
1571 
1572  if (options)
1573  {
1574  if (PhEqualStringZ(options, L"all", TRUE))
1575  ShowAllLeaks = TRUE;
1576  }
1577 
1578  if (rtlDetectHeapLeaks)
1579  {
1580  InLeakDetection = TRUE;
1581  NumberOfLeaks = 0;
1582  NumberOfLeaksShown = 0;
1583  rtlDetectHeapLeaks();
1584  InLeakDetection = FALSE;
1585 
1586  wprintf(L"\nNumber of leaks: %lu (%lu displayed)\n", NumberOfLeaks, NumberOfLeaksShown);
1587  }
1588  }
1589  else if (PhEqualStringZ(command, L"mem", TRUE))
1590  {
1591  PWSTR addressString;
1592  PWSTR bytesString;
1593  PH_STRINGREF addressStringRef;
1594  PH_STRINGREF bytesStringRef;
1595  ULONG64 address64;
1596  ULONG64 numberOfBytes64;
1597  PUCHAR address;
1598  ULONG numberOfBytes;
1599  ULONG blockSize;
1600  UCHAR buffer[16];
1601  ULONG i;
1602 
1603  addressString = wcstok_s(NULL, delims, &context);
1604 
1605  if (!addressString)
1606  goto PrintMemUsage;
1607 
1608  bytesString = wcstok_s(NULL, delims, &context);
1609 
1610  if (!bytesString)
1611  {
1612  bytesString = L"16";
1613  }
1614 
1615  PhInitializeStringRef(&addressStringRef, addressString);
1616  PhInitializeStringRef(&bytesStringRef, bytesString);
1617 
1618  if (PhStringToInteger64(&addressStringRef, 16, &address64) && PhStringToInteger64(&bytesStringRef, 10, &numberOfBytes64))
1619  {
1620  address = (PUCHAR)address64;
1621  numberOfBytes = (ULONG)numberOfBytes64;
1622 
1623  if (numberOfBytes > 256)
1624  {
1625  wprintf(L"Number of bytes must be 256 or smaller.\n");
1626  goto EndCommand;
1627  }
1628 
1629  blockSize = sizeof(buffer);
1630 
1631  while (numberOfBytes != 0)
1632  {
1633  if (blockSize > numberOfBytes)
1634  blockSize = numberOfBytes;
1635 
1636  __try
1637  {
1638  memcpy(buffer, address, blockSize);
1639  }
1640  __except (EXCEPTION_EXECUTE_HANDLER)
1641  {
1642  wprintf(L"Error reading address near %Ix.\n", address);
1643  goto EndCommand;
1644  }
1645 
1646  // Print hex dump
1647  for (i = 0; i < blockSize; i++)
1648  wprintf(L"%02x ", buffer[i]);
1649 
1650  // Fill remaining space (for last, possibly partial block)
1651  for (; i < sizeof(buffer); i++)
1652  wprintf(L" ");
1653 
1654  wprintf(L" ");
1655 
1656  // Print ASCII dump
1657  for (i = 0; i < blockSize; i++)
1658  putwchar((ULONG)(buffer[i] - ' ') <= (ULONG)('~' - ' ') ? buffer[i] : '.');
1659 
1660  wprintf(L"\n");
1661 
1662  address += blockSize;
1663  numberOfBytes -= blockSize;
1664  }
1665  }
1666 
1667  goto EndCommand;
1668 PrintMemUsage:
1669  wprintf(L"Usage: mem address [numberOfBytes]\n");
1670  wprintf(L"Example: mem 12345678 16\n");
1671  }
1672  else
1673  {
1674  wprintf(L"Unrecognized command.\n");
1675  goto EndCommand; // get rid of the compiler warning about the label being unreferenced
1676  }
1677 
1678 EndCommand:
1679  PhDrainAutoPool(&autoPool);
1680  }
1681 
1682  PhDereferenceObject(DebugConsoleSymbolProvider);
1683 
1684  PhDeleteAutoPool(&autoPool);
1685 
1686  return STATUS_SUCCESS;
1687 }