Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1126 lines
33 KiB

  1. /*++
  2. Copyright (c) 1994-2000 Microsoft Corporation
  3. Module Name:
  4. heappagx.c
  5. Abstract:
  6. This module contains the page heap manager debug extensions.
  7. Author:
  8. Tom McGuire (TomMcg) 06-Jan-1995
  9. Silviu Calinoiu (SilviuC) 22-Feb-2000
  10. Revision History:
  11. --*/
  12. #define DEBUG_PAGE_HEAP 1
  13. #include "precomp.h"
  14. #include "heap.h"
  15. __inline
  16. BOOLEAN
  17. CheckInterrupted(
  18. VOID
  19. )
  20. {
  21. if (CheckControlC()) {
  22. dprintf( "\nInterrupted\n\n" );
  23. return TRUE;
  24. }
  25. return FALSE;
  26. }
  27. __inline
  28. ULONG64
  29. FetchRemotePVOID (
  30. ULONG64 Address
  31. )
  32. {
  33. ULONG64 RemoteValue = 0;
  34. ReadPointer( Address, &RemoteValue);
  35. return RemoteValue;
  36. }
  37. __inline
  38. ULONG
  39. FetchRemoteULONG(
  40. ULONG64 Address
  41. )
  42. {
  43. ULONG RemoteValue = 0;
  44. ReadMemory( Address, &RemoteValue, sizeof( ULONG ), NULL );
  45. return RemoteValue;
  46. }
  47. ULONG
  48. ReturnFieldOffset(
  49. PCHAR TypeName,
  50. PCHAR FieldName)
  51. {
  52. ULONG off=0;
  53. GetFieldOffset(TypeName, FieldName, &off);
  54. return off;
  55. }
  56. #define FETCH_REMOTE_FIELD_PTR( StructBase, StructType, FieldName ) \
  57. FetchRemotePVOID((StructBase) + ReturnFieldOffset( #StructType, #FieldName ))
  58. #define FETCH_REMOTE_FIELD_INT( StructBase, StructType, FieldName ) \
  59. FetchRemoteULONG((StructBase) + ReturnFieldOffset( #StructType, #FieldName ))
  60. #define FETCH_REMOTE_FIELD_SIZE_T( StructBase, StructType, FieldName ) \
  61. FetchRemotePVOID((StructBase) + ReturnFieldOffset( #StructType, #FieldName ))
  62. #define DUMP_REMOTE_FIELD_INT( DumpName, StructBase, StructType, FieldName ) \
  63. dprintf( "%s%08X\n", (DumpName), FETCH_REMOTE_FIELD_INT( StructBase, StructType, FieldName ))
  64. #define DUMP_REMOTE_FIELD_PTR( DumpName, StructBase, StructType, FieldName ) \
  65. dprintf( "%s%p\n", (DumpName), FETCH_REMOTE_FIELD_PTR( StructBase, StructType, FieldName ))
  66. VOID
  67. DebugPageHeapLocateFaultAllocation(
  68. ULONG64 RemoteHeap,
  69. ULONG64 AddressOfFault
  70. );
  71. VOID
  72. DebugPageHeapReportAllocation(
  73. ULONG64 RemoteHeap,
  74. ULONG64 RemoteHeapNode,
  75. PCHAR NodeType,
  76. ULONG64 AddressOfFault
  77. );
  78. BOOLEAN
  79. DebugPageHeapExtensionShowHeapList(
  80. VOID
  81. );
  82. VOID
  83. TraceDatabaseDump (
  84. PCSTR Args,
  85. BOOLEAN SortByCountField
  86. );
  87. VOID
  88. TraceDatabaseBlockDump (
  89. ULONG64 Address
  90. );
  91. VOID
  92. FaultInjectionTracesDump (
  93. PCSTR Args
  94. );
  95. /////////////////////////////////////////////////////////////////////
  96. /////////////////////////////////////////////////////////////////////
  97. /////////////////////////////////////////////////////////////////////
  98. VOID DebugPageHeapHelp (
  99. )
  100. {
  101. dprintf ("!heap -p Dump all page heaps. \n");
  102. dprintf ("!heap -p -h ADDR Detailed dump of page heap at ADDR. \n");
  103. dprintf ("!heap -p -a ADDR Figure out what heap block is at ADDR. \n");
  104. dprintf ("!heap -p -t [N] Dump N collected traces with heavy heap users.\n");
  105. dprintf ("!heap -p -tc [N] Dump N traces sorted by count usage (eqv. with -t).\n");
  106. dprintf ("!heap -p -ts [N] Dump N traces sorted by size.\n");
  107. dprintf ("!heap -p -fi [N] Dump last N fault injection traces.\n");
  108. dprintf (" \n");
  109. dprintf (" +-----+---------------+--+ \n");
  110. dprintf (" | | | | Normal heap allocated block \n");
  111. dprintf (" +-----+---------------+--+ \n");
  112. dprintf (" ^ ^ ^ \n");
  113. dprintf (" | | 8 suffix bytes filled with 0xA0 \n");
  114. dprintf (" | user allocation (filled with E0 if zeroing not requested) \n");
  115. dprintf (" block header (starts with 0xABCDAAAA and ends with 0xDCBAAAAA).\n");
  116. dprintf (" A `dt DPH_BLOCK_INFORMATION' on header address followed by \n");
  117. dprintf (" a `dds' on the StackTrace field gives the stacktrace of allocation. \n");
  118. dprintf (" \n");
  119. dprintf (" +-----+---------------+--+ \n");
  120. dprintf (" | | | | Normal heap freed block \n");
  121. dprintf (" +-----+---------------+--+ \n");
  122. dprintf (" ^ ^ ^ \n");
  123. dprintf (" | | 8 suffix bytes filled with 0xA0 \n");
  124. dprintf (" | user allocation (filled with F0 bytes) \n");
  125. dprintf (" block header (starts with 0xABCDAAA9 and ends with 0xDCBAAA9). \n");
  126. dprintf (" A `dt DPH_BLOCK_INFORMATION' on header address followed by \n");
  127. dprintf (" a `dds' on the StackTrace field gives the stacktrace of allocation. \n");
  128. dprintf (" \n");
  129. dprintf (" +-----+---------+--+------ \n");
  130. dprintf (" | | | | ... N/A page Page heap \n");
  131. dprintf (" +-----+---------+--+------ allocated block \n");
  132. dprintf (" ^ ^ ^ \n");
  133. dprintf (" | | 0-7 suffix bytes filled with 0xD0 \n");
  134. dprintf (" | user allocation (filled with C0 if zeroing not requested) \n");
  135. dprintf (" block header (starts with 0xABCDBBBB and ends with 0xDCBABBBB).\n");
  136. dprintf (" A `dt DPH_BLOCK_INFORMATION' on header address followed by \n");
  137. dprintf (" a `dds' on the StackTrace field gives the stacktrace of allocation. \n");
  138. dprintf (" \n");
  139. dprintf (" +-----+---------+--+------ \n");
  140. dprintf (" | | | | ... N/A page Page heap \n");
  141. dprintf (" +-----+---------+--+------ freed block \n");
  142. dprintf (" ^ ^ ^ \n");
  143. dprintf (" | | 0-7 suffix bytes filled with 0xD0 \n");
  144. dprintf (" | user allocation (filled with F0 bytes) \n");
  145. dprintf (" block header (starts with 0xABCDBBA and ends with 0xDCBABBBA).\n");
  146. dprintf (" A `dt DPH_BLOCK_INFORMATION' on header address followed by \n");
  147. dprintf (" a `dds' on the StackTrace field gives the stacktrace of allocation. \n");
  148. dprintf (" \n");
  149. }
  150. VOID
  151. DebugPageHeapExtensionFind(
  152. PCSTR ArgumentString
  153. )
  154. {
  155. ULONG64 RemoteHeapList;
  156. ULONG64 RemoteHeap;
  157. ULONG64 RemoteVirtualNode;
  158. ULONG64 RemoteVirtualBase;
  159. ULONG64 RemoteVirtualSize;
  160. ULONG64 AddressOfFault;
  161. BOOL Result;
  162. Result = GetExpressionEx (ArgumentString,
  163. &AddressOfFault,
  164. &ArgumentString);
  165. if (Result == FALSE) {
  166. dprintf ("\nFailed to convert `%s' to an address.\n",
  167. ArgumentString);
  168. return;
  169. }
  170. RemoteHeapList = (ULONG64) GetExpression( "NTDLL!RtlpDphHeapListHead" );
  171. RemoteHeap = FetchRemotePVOID( RemoteHeapList );
  172. if (RemoteHeap == 0) {
  173. dprintf( "\nNo page heaps active in process (or bad symbols)\n\n" );
  174. AddressOfFault = 0;
  175. }
  176. if (( AddressOfFault == 0 ) || ( strchr( ArgumentString, '?' ))) {
  177. DebugPageHeapHelp();
  178. return;
  179. }
  180. //
  181. // Find the heap that contains the range of virtual addresses that
  182. // contain the AddressOfFault.
  183. //
  184. for (;;) {
  185. //
  186. // The heap header contains a linked list of virtual memory
  187. // allocations.
  188. //
  189. RemoteVirtualNode = FETCH_REMOTE_FIELD_PTR( RemoteHeap, NTDLL!_DPH_HEAP_ROOT, pVirtualStorageListHead );
  190. while (RemoteVirtualNode != 0) {
  191. RemoteVirtualBase = FETCH_REMOTE_FIELD_PTR( RemoteVirtualNode, NTDLL!_DPH_HEAP_BLOCK, pVirtualBlock );
  192. RemoteVirtualSize = FETCH_REMOTE_FIELD_SIZE_T( RemoteVirtualNode, NTDLL!_DPH_HEAP_BLOCK, nVirtualBlockSize );
  193. if (( RemoteVirtualBase == 0 ) || ( RemoteVirtualSize == 0 )) {
  194. dprintf( "\nPAGEHEAP: Heap 0x%p appears to have an invalid\n"
  195. " virtual allocation list\n\n",
  196. RemoteHeap
  197. );
  198. }
  199. if ((AddressOfFault >= RemoteVirtualBase) &&
  200. (AddressOfFault <= RemoteVirtualBase + RemoteVirtualSize )) {
  201. //
  202. // The fault appears to have occurred in the range of this
  203. // heap, so we'll search the busy and free lists for the
  204. // closest match and report it. Then exit.
  205. //
  206. DebugPageHeapLocateFaultAllocation( RemoteHeap, AddressOfFault );
  207. return;
  208. }
  209. if (CheckInterrupted()) {
  210. return;
  211. }
  212. RemoteVirtualNode = FETCH_REMOTE_FIELD_PTR( RemoteVirtualNode, NTDLL!_DPH_HEAP_BLOCK, pNextAlloc );
  213. }
  214. //
  215. // Not found in this heap. Continue with next heap or end
  216. // of heap list.
  217. //
  218. if (CheckInterrupted()) {
  219. return;
  220. }
  221. RemoteHeap = FETCH_REMOTE_FIELD_PTR( RemoteHeap, NTDLL!_DPH_HEAP_ROOT, pNextHeapRoot );
  222. if (RemoteHeap == 0) {
  223. dprintf( "\nPAGEHEAP: Could not find a page heap containing\n"
  224. " the virtual address 0x%p\n\n",
  225. AddressOfFault
  226. );
  227. return;
  228. }
  229. }
  230. }
  231. VOID
  232. DebugPageHeapLocateFaultAllocation(
  233. ULONG64 RemoteHeap,
  234. ULONG64 AddressOfFault
  235. )
  236. {
  237. ULONG64 ClosestHeapNode;
  238. ULONG64 ClosestDifference;
  239. ULONG64 RemoteHeapNode;
  240. ULONG64 RemoteAllocBase;
  241. ULONG64 RemoteAllocSize;
  242. ULONG RemoteFreeListSize;
  243. ClosestHeapNode = 0;
  244. //
  245. // First search the busy list for the containing allocation, if any.
  246. //
  247. RemoteHeapNode = FETCH_REMOTE_FIELD_PTR( RemoteHeap, NTDLL!_DPH_HEAP_ROOT, pBusyAllocationListHead );
  248. while (RemoteHeapNode != 0) {
  249. RemoteAllocBase = FETCH_REMOTE_FIELD_PTR( RemoteHeapNode, NTDLL!_DPH_HEAP_BLOCK, pVirtualBlock );
  250. RemoteAllocSize = FETCH_REMOTE_FIELD_SIZE_T( RemoteHeapNode, NTDLL!_DPH_HEAP_BLOCK, nVirtualBlockSize );
  251. if ((AddressOfFault >= RemoteAllocBase) &&
  252. (AddressOfFault < RemoteAllocBase + RemoteAllocSize)) {
  253. //
  254. // The fault appears to have occurred in this allocation's
  255. // memory (which includes the NO_ACCESS page beyond the user
  256. // portion of the allocation).
  257. //
  258. DebugPageHeapReportAllocation( RemoteHeap, RemoteHeapNode, "allocated", AddressOfFault );
  259. return;
  260. }
  261. if (CheckInterrupted()) {
  262. return;
  263. }
  264. RemoteHeapNode = FETCH_REMOTE_FIELD_PTR( RemoteHeapNode, NTDLL!_DPH_HEAP_BLOCK, pNextAlloc );
  265. }
  266. //
  267. // Failed to find containing allocation on busy list, so search free.
  268. //
  269. RemoteHeapNode = FETCH_REMOTE_FIELD_PTR( RemoteHeap, NTDLL!_DPH_HEAP_ROOT, pFreeAllocationListHead );
  270. while (RemoteHeapNode != 0) {
  271. RemoteAllocBase = FETCH_REMOTE_FIELD_PTR( RemoteHeapNode, NTDLL!_DPH_HEAP_BLOCK, pVirtualBlock );
  272. RemoteAllocSize = FETCH_REMOTE_FIELD_INT( RemoteHeapNode, NTDLL!_DPH_HEAP_BLOCK, nVirtualBlockSize );
  273. if ((AddressOfFault >= RemoteAllocBase) &&
  274. (AddressOfFault < RemoteAllocBase + RemoteAllocSize)) {
  275. //
  276. // The fault appears to have occurred in this freed alloc's
  277. // memory.
  278. //
  279. DebugPageHeapReportAllocation( RemoteHeap, RemoteHeapNode, "freed", AddressOfFault );
  280. return;
  281. }
  282. if (CheckInterrupted()) {
  283. return;
  284. }
  285. RemoteHeapNode = FETCH_REMOTE_FIELD_PTR( RemoteHeapNode, NTDLL!_DPH_HEAP_BLOCK, pNextAlloc );
  286. }
  287. //
  288. // Failed to find containing allocation in free list, but we wouldn't
  289. // have gotten this far if the debug heap did not contain the virtual
  290. // address range of the fault. So, report it as a wild pointer that
  291. // could have been freed memory.
  292. //
  293. RemoteFreeListSize = FETCH_REMOTE_FIELD_INT( RemoteHeap, NTDLL!_DPH_HEAP_ROOT, nFreeAllocations );
  294. dprintf( "\nPAGEHEAP: %p references memory contained in the heap %p,\n"
  295. " but does not reference an existing allocated or\n"
  296. " recently freed heap block. It is possible that\n"
  297. " the memory at %p could previously have been\n"
  298. " allocated and freed, but it must have been freed\n"
  299. " prior to the most recent %d frees.\n\n",
  300. AddressOfFault,
  301. RemoteHeap,
  302. AddressOfFault,
  303. RemoteFreeListSize
  304. );
  305. }
  306. VOID
  307. DebugPageHeapReportAllocation(
  308. ULONG64 RemoteHeap,
  309. ULONG64 RemoteHeapNode,
  310. PCHAR NodeType,
  311. ULONG64 AddressOfFault
  312. )
  313. {
  314. ULONG64 RemoteUserBase;
  315. ULONG64 RemoteUserSize;
  316. ULONG64 EndOfBlock;
  317. ULONG64 PastTheBlock;
  318. ULONG64 BeforeTheBlock;
  319. RemoteUserBase = FETCH_REMOTE_FIELD_PTR( RemoteHeapNode, NTDLL!_DPH_HEAP_BLOCK, pUserAllocation );
  320. RemoteUserSize = FETCH_REMOTE_FIELD_SIZE_T( RemoteHeapNode, NTDLL!_DPH_HEAP_BLOCK, nUserRequestedSize );
  321. EndOfBlock = RemoteUserBase + RemoteUserSize - 1;
  322. if (AddressOfFault > EndOfBlock) {
  323. PastTheBlock = AddressOfFault - EndOfBlock;
  324. dprintf( "\nPAGEHEAP: %p is %p bytes beyond the end of %s heap block at\n"
  325. " %p of 0x%x bytes",
  326. AddressOfFault,
  327. PastTheBlock,
  328. NodeType,
  329. RemoteUserBase,
  330. RemoteUserSize
  331. );
  332. }
  333. else if (AddressOfFault >= RemoteUserBase) {
  334. dprintf( "\nPAGEHEAP: %p references %s heap block at\n"
  335. " %p of 0x%x bytes",
  336. AddressOfFault,
  337. NodeType,
  338. RemoteUserBase,
  339. RemoteUserSize
  340. );
  341. }
  342. else {
  343. BeforeTheBlock = (PCHAR) RemoteUserBase - (PCHAR) AddressOfFault;
  344. dprintf( "\nPAGEHEAP: %p is %p bytes before the %s heap block at\n"
  345. " %p of 0x%x bytes",
  346. AddressOfFault,
  347. BeforeTheBlock,
  348. NodeType,
  349. RemoteUserBase,
  350. RemoteUserSize
  351. );
  352. }
  353. {
  354. ULONG64 Trace;
  355. Trace = FETCH_REMOTE_FIELD_PTR (RemoteHeapNode, NTDLL!_DPH_HEAP_BLOCK, StackTrace);
  356. dprintf ("\n\n");
  357. TraceDatabaseBlockDump (Trace);
  358. }
  359. }
  360. #define FORMAT_TYPE_BUSY_LIST 0
  361. #define FORMAT_TYPE_FREE_LIST 1
  362. #define FORMAT_TYPE_VIRT_LIST 2
  363. BOOLEAN
  364. DebugPageHeapDumpThisList(
  365. ULONG64 RemoteList,
  366. PCH ListName,
  367. ULONG FormatType
  368. )
  369. {
  370. ULONG64 RemoteNode;
  371. ULONG64 RemoteBase;
  372. ULONG64 RemoteSize;
  373. ULONG64 RemoteUser;
  374. ULONG64 RemoteUsiz;
  375. ULONG RemoteFlag;
  376. ULONG64 RemoteValu;
  377. RemoteNode = RemoteList;
  378. dprintf( "\n%s:\n", ListName );
  379. switch (FormatType) {
  380. case FORMAT_TYPE_BUSY_LIST:
  381. dprintf( "UserAddr UserSize VirtAddr VirtSize UserFlag UserValu\n" );
  382. break;
  383. case FORMAT_TYPE_FREE_LIST:
  384. dprintf( "UserAddr UserSize VirtAddr VirtSize\n" );
  385. break;
  386. }
  387. while (RemoteNode) {
  388. RemoteBase = FETCH_REMOTE_FIELD_PTR( RemoteNode, NTDLL!_DPH_HEAP_BLOCK, pVirtualBlock );
  389. RemoteSize = FETCH_REMOTE_FIELD_SIZE_T( RemoteNode, NTDLL!_DPH_HEAP_BLOCK, nVirtualBlockSize );
  390. RemoteUser = FETCH_REMOTE_FIELD_PTR( RemoteNode, NTDLL!_DPH_HEAP_BLOCK, pUserAllocation );
  391. RemoteUsiz = FETCH_REMOTE_FIELD_SIZE_T( RemoteNode, NTDLL!_DPH_HEAP_BLOCK, nUserRequestedSize );
  392. RemoteFlag = FETCH_REMOTE_FIELD_INT( RemoteNode, NTDLL!_DPH_HEAP_BLOCK, UserFlags );
  393. RemoteValu = FETCH_REMOTE_FIELD_PTR( RemoteNode, NTDLL!_DPH_HEAP_BLOCK, UserValue );
  394. RemoteNode = FETCH_REMOTE_FIELD_PTR( RemoteNode, NTDLL!_DPH_HEAP_BLOCK, pNextAlloc );
  395. switch (FormatType) {
  396. case FORMAT_TYPE_BUSY_LIST:
  397. dprintf(( RemoteFlag || RemoteValu ) ?
  398. "%p %08X %p %08X %08X %p\n" :
  399. "%p %08X %p %08X\n",
  400. RemoteUser,
  401. RemoteUsiz,
  402. RemoteBase,
  403. RemoteSize,
  404. RemoteFlag,
  405. RemoteValu
  406. );
  407. break;
  408. case FORMAT_TYPE_FREE_LIST:
  409. dprintf( "%p %08X %p %08X\n",
  410. RemoteUser,
  411. RemoteUsiz,
  412. RemoteBase,
  413. RemoteSize
  414. );
  415. break;
  416. case FORMAT_TYPE_VIRT_LIST:
  417. dprintf( "%p - %p (%08X)\n",
  418. RemoteBase,
  419. (PCH)RemoteBase + RemoteSize,
  420. RemoteSize
  421. );
  422. break;
  423. }
  424. if (CheckInterrupted()) {
  425. return FALSE;
  426. }
  427. }
  428. return TRUE;
  429. }
  430. BOOLEAN
  431. DebugPageHeapDumpThisHeap(
  432. ULONG64 RemoteHeap
  433. )
  434. {
  435. ULONG64 RemoteNode;
  436. dprintf( "\nDPH Heap at %p:\n\n", RemoteHeap );
  437. DUMP_REMOTE_FIELD_INT( "Signature: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, Signature );
  438. DUMP_REMOTE_FIELD_INT( "HeapFlags: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, HeapFlags );
  439. DUMP_REMOTE_FIELD_INT( "ExtraFlags: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, ExtraFlags );
  440. DUMP_REMOTE_FIELD_INT( "NormalHeap: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, NormalHeap );
  441. DUMP_REMOTE_FIELD_INT( "VirtualRanges: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, nVirtualStorageRanges );
  442. DUMP_REMOTE_FIELD_PTR( "VirtualCommit: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, nVirtualStorageBytes );
  443. DUMP_REMOTE_FIELD_INT( "BusyAllocs: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, nBusyAllocations );
  444. DUMP_REMOTE_FIELD_PTR( "BusyVirtual: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, nBusyAllocationBytesCommitted );
  445. DUMP_REMOTE_FIELD_PTR( "BusyReadWrite: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, nBusyAllocationBytesAccessible );
  446. DUMP_REMOTE_FIELD_INT( "FreeAllocs: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, nFreeAllocations );
  447. DUMP_REMOTE_FIELD_PTR( "FreeVirtual: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, nFreeAllocationBytesCommitted );
  448. DUMP_REMOTE_FIELD_INT( "AvailAllocs: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, nAvailableAllocations );
  449. DUMP_REMOTE_FIELD_PTR( "AvailVirtual: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, nAvailableAllocationBytesCommitted );
  450. DUMP_REMOTE_FIELD_INT( "NodePools: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, nNodePools );
  451. DUMP_REMOTE_FIELD_PTR( "NodeVirtual: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, nNodePoolBytes );
  452. DUMP_REMOTE_FIELD_INT( "AvailNodes: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, nUnusedNodes );
  453. DUMP_REMOTE_FIELD_INT( "Seed: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, Seed );
  454. dprintf (" --- Counters --- \n");
  455. DUMP_REMOTE_FIELD_INT( "Size < 1K: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, Counter[0] );
  456. DUMP_REMOTE_FIELD_INT( "Size < 4K: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, Counter[1] );
  457. DUMP_REMOTE_FIELD_INT( "Size >= 4K: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, Counter[2] );
  458. DUMP_REMOTE_FIELD_INT( "W/o alloc info: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, Counter[3] );
  459. DUMP_REMOTE_FIELD_INT( "Total allocs: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, Counter[4] );
  460. DUMP_REMOTE_FIELD_INT( "Total reallocs: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, Counter[5] );
  461. DUMP_REMOTE_FIELD_INT( "Total frees: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, Counter[6] );
  462. DUMP_REMOTE_FIELD_INT( "Normal allocs: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, Counter[7] );
  463. DUMP_REMOTE_FIELD_INT( "Normal reallocs: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, Counter[8] );
  464. DUMP_REMOTE_FIELD_INT( "Normal frees: ", RemoteHeap, NTDLL!_DPH_HEAP_ROOT, Counter[9] );
  465. {
  466. ULONG64 Trace;
  467. dprintf ("\n");
  468. Trace = FETCH_REMOTE_FIELD_PTR (RemoteHeap, NTDLL!_DPH_HEAP_ROOT, CreateStackTrace);
  469. TraceDatabaseBlockDump (Trace);
  470. }
  471. if (! DebugPageHeapDumpThisList(
  472. FETCH_REMOTE_FIELD_PTR( RemoteHeap, NTDLL!_DPH_HEAP_ROOT, pVirtualStorageListHead ),
  473. "VirtualList",
  474. FORMAT_TYPE_VIRT_LIST )) {
  475. return FALSE;
  476. }
  477. if (! DebugPageHeapDumpThisList(
  478. FETCH_REMOTE_FIELD_PTR( RemoteHeap, NTDLL!_DPH_HEAP_ROOT, pNodePoolListHead ),
  479. "NodePoolList",
  480. FORMAT_TYPE_VIRT_LIST )) {
  481. return FALSE;
  482. }
  483. if (! DebugPageHeapDumpThisList(
  484. FETCH_REMOTE_FIELD_PTR( RemoteHeap, NTDLL!_DPH_HEAP_ROOT, pAvailableAllocationListHead ),
  485. "AvailableList",
  486. FORMAT_TYPE_VIRT_LIST )) {
  487. return FALSE;
  488. }
  489. if (! DebugPageHeapDumpThisList(
  490. FETCH_REMOTE_FIELD_PTR( RemoteHeap, NTDLL!_DPH_HEAP_ROOT, pFreeAllocationListHead ),
  491. "FreeList",
  492. FORMAT_TYPE_FREE_LIST )) {
  493. return FALSE;
  494. }
  495. if (! DebugPageHeapDumpThisList(
  496. FETCH_REMOTE_FIELD_PTR( RemoteHeap, NTDLL!_DPH_HEAP_ROOT, pBusyAllocationListHead ),
  497. "BusyList",
  498. FORMAT_TYPE_BUSY_LIST )) {
  499. return FALSE;
  500. }
  501. dprintf( "\n" );
  502. return TRUE;
  503. }
  504. VOID
  505. DebugPageHeapExtensionDump(
  506. PCSTR ArgumentString
  507. )
  508. {
  509. ULONG64 RemoteHeapList;
  510. ULONG64 RemoteHeap;
  511. ULONG64 RemoteHeapToDump;
  512. BOOLEAN AnyDumps = FALSE;
  513. BOOL Result;
  514. Result = GetExpressionEx (ArgumentString,
  515. &RemoteHeapToDump,
  516. &ArgumentString);
  517. if (Result == FALSE) {
  518. dprintf ("\nFailed to convert `%s' to an address.\n",
  519. ArgumentString);
  520. return;
  521. }
  522. RemoteHeapList = (ULONG64) GetExpression( "NTDLL!RtlpDphHeapListHead" );
  523. RemoteHeap = FetchRemotePVOID( RemoteHeapList );
  524. if (( RemoteHeap == 0 ) ||
  525. ( RemoteHeapToDump == 0 ) ||
  526. ( strchr( ArgumentString, '?' ))) {
  527. DebugPageHeapHelp();
  528. DebugPageHeapExtensionShowHeapList();
  529. return;
  530. }
  531. while (RemoteHeap != 0) {
  532. if ((((LONG_PTR)RemoteHeapToDump & 0xFFFF0000 ) == ((LONG_PTR)RemoteHeap & 0xFFFF0000 )) ||
  533. ((LONG_PTR)RemoteHeapToDump == -1 )) {
  534. AnyDumps = TRUE;
  535. if (! DebugPageHeapDumpThisHeap( RemoteHeap ))
  536. return;
  537. }
  538. if (CheckInterrupted()) {
  539. return;
  540. }
  541. RemoteHeap = FETCH_REMOTE_FIELD_PTR( RemoteHeap, NTDLL!_DPH_HEAP_ROOT, pNextHeapRoot );
  542. }
  543. if (! AnyDumps) {
  544. dprintf( "\nPage heap \"0x%p\" not found in process\n\n", RemoteHeapToDump );
  545. DebugPageHeapExtensionShowHeapList();
  546. }
  547. }
  548. BOOLEAN
  549. DebugPageHeapExtensionShowHeapList(
  550. VOID
  551. )
  552. {
  553. ULONG64 RemoteHeapList = (ULONG64)GetExpression( "NTDLL!RtlpDphHeapListHead" );
  554. ULONG64 RemoteHeap = FetchRemotePVOID( RemoteHeapList );
  555. ULONG64 NormalHeap;
  556. ULONG HeapFlags;
  557. if (RemoteHeap == 0) {
  558. dprintf( "\nNo page heaps active in process (or bad symbols)\n" );
  559. return FALSE;
  560. }
  561. else {
  562. dprintf( "\nPage heaps active in process:\n\n" );
  563. do {
  564. NormalHeap = FETCH_REMOTE_FIELD_PTR (RemoteHeap, NTDLL!_DPH_HEAP_ROOT, NormalHeap);
  565. HeapFlags = (ULONG) FETCH_REMOTE_FIELD_INT (RemoteHeap, NTDLL!_DPH_HEAP_ROOT, ExtraFlags);
  566. dprintf (" %p (%p, flags %X)\n", RemoteHeap, NormalHeap, HeapFlags);
  567. RemoteHeap = FETCH_REMOTE_FIELD_PTR( RemoteHeap, NTDLL!_DPH_HEAP_ROOT, pNextHeapRoot );
  568. } while (RemoteHeap);
  569. dprintf( "\n" );
  570. return TRUE;
  571. }
  572. }
  573. BOOLEAN
  574. DebugPageHeapIsActive(
  575. VOID
  576. )
  577. {
  578. ULONG64 RemoteHeapList = (ULONG64)GetExpression( "NTDLL!RtlpDphHeapListHead" );
  579. ULONG64 RemoteHeap = FetchRemotePVOID( RemoteHeapList );
  580. if (RemoteHeap == 0) {
  581. return FALSE;
  582. }
  583. else {
  584. return TRUE;
  585. }
  586. }
  587. VOID
  588. DebugPageHeapExtension(
  589. PCSTR ArgumentString
  590. )
  591. {
  592. PCSTR Current;
  593. //
  594. // Is help requested?
  595. //
  596. if (strstr (ArgumentString, "?") != NULL) {
  597. DebugPageHeapHelp ();
  598. }
  599. //
  600. // If page heap not active then return immediately.
  601. //
  602. if (! DebugPageHeapIsActive()) {
  603. dprintf ("Page heap is not active for this process. \n");
  604. return;
  605. }
  606. //
  607. // Parse command line
  608. //
  609. if ((Current = strstr (ArgumentString, "-h")) != NULL) {
  610. DebugPageHeapExtensionDump (Current + strlen("-h"));
  611. }
  612. else if ((Current = strstr (ArgumentString, "-a")) != NULL) {
  613. DebugPageHeapExtensionFind (Current + strlen("-a"));
  614. }
  615. else if ((Current = strstr (ArgumentString, "-tc")) != NULL) {
  616. TraceDatabaseDump (Current + strlen("-tc"), TRUE);
  617. }
  618. else if ((Current = strstr (ArgumentString, "-ts")) != NULL) {
  619. TraceDatabaseDump (Current + strlen("-ts"), FALSE);
  620. }
  621. else if ((Current = strstr (ArgumentString, "-t")) != NULL) {
  622. TraceDatabaseDump (Current + strlen("-t"), TRUE);
  623. }
  624. else if ((Current = strstr (ArgumentString, "-fi")) != NULL) {
  625. FaultInjectionTracesDump (Current + strlen("-fi"));
  626. }
  627. else {
  628. DebugPageHeapExtensionShowHeapList ();
  629. }
  630. return;
  631. }
  632. /////////////////////////////////////////////////////////////////////
  633. ////////////////////////////////////////////////////// Trace database
  634. /////////////////////////////////////////////////////////////////////
  635. typedef struct {
  636. ULONG64 Address;
  637. ULONG64 Count;
  638. ULONG64 Size;
  639. } TRACE, *PTRACE;
  640. VOID
  641. TraceDatabaseDump (
  642. PCSTR Args,
  643. BOOLEAN SortByCountField
  644. )
  645. {
  646. ULONG64 Database;
  647. ULONG I, J, Min, TraceIndex;
  648. PTRACE Trace;
  649. ULONG64 TracesToDisplay = 0;
  650. ULONG64 MaximumSize;
  651. ULONG64 CurrentSize;
  652. ULONG64 NoOfTraces;
  653. ULONG64 NoOfHits;
  654. ULONG NoOfBuckets;
  655. ULONG PvoidSize;
  656. if (Args) {
  657. sscanf (Args, "%I64u", &TracesToDisplay);
  658. if (TracesToDisplay == 0) {
  659. TracesToDisplay = 4;
  660. }
  661. }
  662. Database = (ULONG64) GetExpression ("NTDLL!RtlpDphTraceDatabase" );
  663. Database = FetchRemotePVOID (Database);
  664. MaximumSize = FETCH_REMOTE_FIELD_PTR(Database, NTDLL!_RTL_TRACE_DATABASE, MaximumSize);
  665. CurrentSize = FETCH_REMOTE_FIELD_PTR(Database, NTDLL!_RTL_TRACE_DATABASE, CurrentSize);
  666. NoOfBuckets = FETCH_REMOTE_FIELD_INT(Database, NTDLL!_RTL_TRACE_DATABASE, NoOfBuckets);
  667. NoOfTraces = FETCH_REMOTE_FIELD_PTR(Database, NTDLL!_RTL_TRACE_DATABASE, NoOfTraces);
  668. NoOfHits = FETCH_REMOTE_FIELD_PTR(Database, NTDLL!_RTL_TRACE_DATABASE, NoOfHits);
  669. PvoidSize = IsPtr64() ? 8 : 4;
  670. dprintf ("MaximumSize: %p \n", MaximumSize);
  671. dprintf ("CurentSize: %p \n", CurrentSize);
  672. dprintf ("NoOfBuckets: %u \n", NoOfBuckets);
  673. dprintf ("NoOfTraces: %p \n", NoOfTraces);
  674. dprintf ("NoOfHits: %p \n", NoOfHits);
  675. //
  676. // Dump hash counters.
  677. //
  678. dprintf ("HashCounters:");
  679. for (I = 0; I < 16; I += 1) {
  680. CHAR FieldName[16];
  681. sprintf (FieldName, "HashCounter[%u]", I);
  682. dprintf (" %u", FetchRemoteULONG (
  683. Database + ReturnFieldOffset("NTDLL!_RTL_TRACE_DATABASE", FieldName)));
  684. }
  685. dprintf ("\n");
  686. if (NoOfTraces < TracesToDisplay) {
  687. TracesToDisplay = NoOfTraces;
  688. }
  689. Trace = (PTRACE) malloc (sizeof(TRACE) * (ULONG)NoOfTraces);
  690. if (Trace == NULL) {
  691. dprintf ("Error: cannot allocate trace database debug structure.\n");
  692. return;
  693. }
  694. //
  695. // Read all the traces from the hash table.
  696. //
  697. for (I = 0, TraceIndex = 0; I < NoOfBuckets; I += 1) {
  698. ULONG64 Current;
  699. Current = FETCH_REMOTE_FIELD_PTR(Database, NTDLL!_RTL_TRACE_DATABASE, Buckets);
  700. Current += I * PvoidSize;
  701. Current = FetchRemotePVOID (Current);
  702. while (Current != 0) {
  703. if (TraceIndex >= NoOfTraces) {
  704. dprintf ("Internal error: TraceIndex >= NoOfTraces \n");
  705. return;
  706. }
  707. Trace[TraceIndex].Address = Current;
  708. Trace[TraceIndex].Count = FETCH_REMOTE_FIELD_PTR (Current, NTDLL!_RTL_TRACE_BLOCK, UserCount);
  709. Trace[TraceIndex].Size = FETCH_REMOTE_FIELD_PTR (Current, NTDLL!_RTL_TRACE_BLOCK, UserSize);
  710. TraceIndex += 1;
  711. Current = FETCH_REMOTE_FIELD_PTR (Current, NTDLL!_RTL_TRACE_BLOCK, Next);
  712. }
  713. }
  714. //
  715. // Sort the traces just read based on Count field.
  716. //
  717. for (I = 0; I < NoOfTraces; I += 1) {
  718. for (J = I, Min = I; J < NoOfTraces; J += 1) {
  719. if (SortByCountField) {
  720. if (Trace[J].Count > Trace[Min].Count) {
  721. Min = J;
  722. }
  723. }
  724. else {
  725. if (Trace[J].Size > Trace[Min].Size) {
  726. Min = J;
  727. }
  728. }
  729. }
  730. if (Min != I) {
  731. ULONG64 Address;
  732. ULONG64 Count;
  733. ULONG64 Size;
  734. Address = Trace[I].Address;
  735. Count = Trace[I].Count;
  736. Size = Trace[I].Size;
  737. Trace[I].Address = Trace[Min].Address;
  738. Trace[I].Count = Trace[Min].Count;
  739. Trace[I].Size = Trace[Min].Size;
  740. Trace[Min].Address = Address;
  741. Trace[Min].Count = Count;
  742. Trace[Min].Size = Size;
  743. }
  744. }
  745. #if 0
  746. for (I = 1; I < NoOfTraces; I += 1) {
  747. if (Trace[I].Size > Trace[I-1].Size) {
  748. dprintf (".");
  749. }
  750. }
  751. #endif
  752. dprintf ("\n");
  753. //
  754. // Print first N
  755. //
  756. for (I = 0; I < TracesToDisplay; I += 1) {
  757. dprintf ("\n");
  758. TraceDatabaseBlockDump (Trace[I].Address);
  759. if (CheckControlC()) {
  760. dprintf ("Interrupted \n");
  761. break;
  762. }
  763. }
  764. dprintf ("\n");
  765. free (Trace);
  766. }
  767. VOID
  768. TraceDatabaseBlockDump (
  769. ULONG64 Address
  770. )
  771. {
  772. ULONG64 TraceAddress;
  773. ULONG64 ReturnAddress;
  774. CHAR SymbolName[ 1024 ];
  775. ULONG64 Displacement;
  776. ULONG I;
  777. ULONG BlockSize;
  778. ULONG PvoidSize;
  779. if (Address == 0) {
  780. dprintf (" No trace\n");
  781. return;
  782. }
  783. PvoidSize = IsPtr64() ? 8 : 4;
  784. BlockSize = FETCH_REMOTE_FIELD_INT(Address, NTDLL!_RTL_TRACE_BLOCK, Size);
  785. dprintf (" Trace @ %p: %p bytes, %u blocks (heap @ %p) \n",
  786. Address,
  787. FETCH_REMOTE_FIELD_PTR(Address, NTDLL!_RTL_TRACE_BLOCK, UserSize),
  788. FETCH_REMOTE_FIELD_PTR(Address, NTDLL!_RTL_TRACE_BLOCK, UserCount),
  789. FETCH_REMOTE_FIELD_PTR(Address, NTDLL!_RTL_TRACE_BLOCK, UserContext));
  790. dprintf (" [%x, %u, %u] \n",
  791. FETCH_REMOTE_FIELD_INT(Address, NTDLL!_RTL_TRACE_BLOCK, Magic),
  792. FETCH_REMOTE_FIELD_INT(Address, NTDLL!_RTL_TRACE_BLOCK, Count),
  793. FETCH_REMOTE_FIELD_INT(Address, NTDLL!_RTL_TRACE_BLOCK, Size));
  794. for (I = 0; I < BlockSize; I += 1) {
  795. TraceAddress = FETCH_REMOTE_FIELD_PTR (Address, NTDLL!_RTL_TRACE_BLOCK, Trace);
  796. ReturnAddress = FetchRemotePVOID (TraceAddress + I * PvoidSize);
  797. GetSymbol (ReturnAddress, SymbolName, &Displacement);
  798. dprintf (" %p %s+0x%p\n",
  799. ReturnAddress,
  800. SymbolName,
  801. Displacement);
  802. }
  803. }
  804. /////////////////////////////////////////////////////////////////////
  805. ////////////////////////////////////////////// Fault injection traces
  806. /////////////////////////////////////////////////////////////////////
  807. VOID
  808. FaultInjectionTracesDump (
  809. PCSTR Args
  810. )
  811. {
  812. ULONG64 TracesToDisplay = 0;
  813. ULONG64 TraceAddress;
  814. ULONG64 IndexAddress;
  815. ULONG Index;
  816. ULONG I;
  817. const ULONG NO_OF_FAULT_INJECTION_TRACES = 128;
  818. ULONG PvoidSize;
  819. ULONG64 TraceBlock;
  820. ULONG TracesFound = 0;
  821. BOOLEAN Interrupted = FALSE;
  822. ULONG64 FlagsAddress;
  823. ULONG Flags;
  824. if (Args) {
  825. sscanf (Args, "%I64u", &TracesToDisplay);
  826. if (TracesToDisplay == 0) {
  827. TracesToDisplay = 4;
  828. }
  829. }
  830. PvoidSize = IsPtr64() ? 8 : 4;
  831. TraceAddress = (ULONG64) GetExpression ("NTDLL!RtlpDphFaultStacks");
  832. IndexAddress = (ULONG64) GetExpression ("NTDLL!RtlpDphFaultStacksIndex");
  833. FlagsAddress = (ULONG64) GetExpression ("NTDLL!RtlpDphGlobalFlags");
  834. Flags = FetchRemoteULONG (FlagsAddress);
  835. if (! (Flags & PAGE_HEAP_USE_FAULT_INJECTION)) {
  836. dprintf ("Fault injection is not enabled for this process. \n");
  837. dprintf ("Use `pageheap /enable PROGRAM /fault RATE' to enable it. \n");
  838. return;
  839. }
  840. Index = FetchRemoteULONG (IndexAddress);
  841. for (I = 0; I < NO_OF_FAULT_INJECTION_TRACES; I += 1) {
  842. Index -= 1;
  843. Index &= (NO_OF_FAULT_INJECTION_TRACES - 1);
  844. TraceBlock = FetchRemotePVOID (TraceAddress + Index * PvoidSize);
  845. if (TraceBlock != 0) {
  846. TracesFound += 1;
  847. dprintf ("\n");
  848. TraceDatabaseBlockDump (TraceBlock);
  849. if (TracesFound >= TracesToDisplay) {
  850. break;
  851. }
  852. }
  853. if (CheckControlC()) {
  854. Interrupted = TRUE;
  855. dprintf ("Interrupted \n");
  856. break;
  857. }
  858. }
  859. if (Interrupted == FALSE && TracesFound == 0) {
  860. dprintf ("No fault injection traces found. \n");
  861. }
  862. }