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.

433 lines
9.1 KiB

  1. /*++
  2. Copyright (c) 1995-1999 Microsoft Corporation
  3. Module Name:
  4. heapstat.cxx
  5. Abstract:
  6. This module contains an NTSD debugger extension for dumping various
  7. heap statistics.
  8. Author:
  9. Keith Moore (keithmo) 01-Nov-1997
  10. Revision History:
  11. --*/
  12. #include "inetdbgp.h"
  13. #define MAX_SIZE 65536
  14. // Large busy block (size exceeds MAX_SIZE)
  15. #define MAX_LBBSIZE 1024
  16. typedef struct _ENUM_CONTEXT {
  17. BOOLEAN ContinueEnum;
  18. ULONG FreeJumbo;
  19. ULONG BusyJumbo;
  20. ULONG FreeJumboBytes;
  21. ULONG BusyJumboBytes;
  22. ULONG BusyOverhead;
  23. ULONG FreeOverhead;
  24. ULONG FreeCounters[MAX_SIZE];
  25. ULONG BusyCounters[MAX_SIZE];
  26. ULONG LargeBusyBlock[MAX_LBBSIZE];
  27. } ENUM_CONTEXT, *PENUM_CONTEXT;
  28. #define BYTES_TO_K(cb) ( ( (cb) + 512 ) / 1024 )
  29. /************************************************************
  30. * Dump Heap Info
  31. ************************************************************/
  32. BOOLEAN
  33. CALLBACK
  34. HspEnumHeapSegmentEntriesProc(
  35. IN PVOID Param,
  36. IN PHEAP_ENTRY LocalHeapEntry,
  37. IN PHEAP_ENTRY RemoteHeapEntry
  38. )
  39. /*++
  40. Routine Description:
  41. Callback invoked for each heap entry within a heap segment.
  42. Arguments:
  43. Param - An uninterpreted parameter passed to the enumerator.
  44. LocalHeapEntry - Pointer to a local copy of the HEAP_ENTRY structure.
  45. RemoteHeapEntry - The remote address of the HEAP_ENTRY structure
  46. in the debugee.
  47. Return Value:
  48. BOOLEAN - TRUE if the enumeration should continue, FALSE if it
  49. should be terminated.
  50. --*/
  51. {
  52. PENUM_CONTEXT context = (PENUM_CONTEXT)Param;
  53. ULONG entryLength;
  54. ULONG allocLength;
  55. //
  56. // Calculate the total length of this entry, including the heap
  57. // header and any "slop" at the end of the block.
  58. //
  59. entryLength = (ULONG)LocalHeapEntry->Size << HEAP_GRANULARITY_SHIFT;
  60. //
  61. // From that, compute the number of bytes in use. This is the size
  62. // of the allocation request as received from the application.
  63. //
  64. allocLength = entryLength - (ULONG)LocalHeapEntry->UnusedBytes;
  65. //
  66. // Adjust the appropriate accumulators.
  67. //
  68. if( LocalHeapEntry->Flags & HEAP_ENTRY_BUSY ) {
  69. context->BusyOverhead += entryLength;
  70. if( allocLength < MAX_SIZE ) {
  71. context->BusyCounters[allocLength] += 1;
  72. } else {
  73. context->BusyJumbo += 1;
  74. context->BusyJumboBytes += allocLength;
  75. if (context->LargeBusyBlock[MAX_LBBSIZE-1] == 0) {
  76. BOOL fFound = FALSE;
  77. UINT i = 0;
  78. for (; context->LargeBusyBlock[i] != 0 && i < MAX_LBBSIZE; i++) {
  79. if( CheckControlC() ) {
  80. context->ContinueEnum = FALSE;
  81. return FALSE;
  82. }
  83. if (allocLength == context->LargeBusyBlock[i]) {
  84. fFound = TRUE;
  85. break;
  86. }
  87. }
  88. if (!fFound && i < MAX_LBBSIZE-1) {
  89. context->LargeBusyBlock[i] = allocLength;
  90. }
  91. }
  92. }
  93. } else {
  94. context->FreeOverhead += entryLength;
  95. if( allocLength < MAX_SIZE ) {
  96. context->FreeCounters[allocLength] += 1;
  97. } else {
  98. context->FreeJumbo += 1;
  99. context->FreeJumboBytes += allocLength;
  100. }
  101. }
  102. return TRUE;
  103. } // HspEnumHeapSegmentEntriesProc
  104. BOOLEAN
  105. CALLBACK
  106. HspEnumHeapSegmentsProc(
  107. IN PVOID Param,
  108. IN PHEAP_SEGMENT LocalHeapSegment,
  109. IN PHEAP_SEGMENT RemoteHeapSegment,
  110. IN ULONG HeapSegmentIndex
  111. )
  112. /*++
  113. Routine Description:
  114. Callback invoked for each heap segment within a heap.
  115. Arguments:
  116. Param - An uninterpreted parameter passed to the enumerator.
  117. LocalHeapSegment - Pointer to a local copy of the HEAP_SEGMENT
  118. structure.
  119. RemoteHeapSegment - The remote address of the HEAP_SEGMENT
  120. structure in the debugee.
  121. Return Value:
  122. BOOLEAN - TRUE if the enumeration should continue, FALSE if it
  123. should be terminated.
  124. --*/
  125. {
  126. //
  127. // Enumerate the entries for the specified segment.
  128. //
  129. if( !EnumHeapSegmentEntries(
  130. LocalHeapSegment,
  131. RemoteHeapSegment,
  132. HspEnumHeapSegmentEntriesProc,
  133. Param
  134. ) ) {
  135. dprintf( "error retrieving heap segment entries\n" );
  136. return FALSE;
  137. }
  138. return TRUE;
  139. } // HspEnumHeapSegmentsProc
  140. BOOLEAN
  141. CALLBACK
  142. HspEnumHeapsProc(
  143. IN PVOID Param,
  144. IN PHEAP LocalHeap,
  145. IN PHEAP RemoteHeap,
  146. IN ULONG HeapIndex
  147. )
  148. /*++
  149. Routine Description:
  150. Callback invoked for each heap within a process.
  151. Arguments:
  152. Param - An uninterpreted parameter passed to the enumerator.
  153. LocalHeap - Pointer to a local copy of the HEAP structure.
  154. RemoteHeap - The remote address of the HEAP structure in the debugee.
  155. Return Value:
  156. BOOLEAN - TRUE if the enumeration should continue, FALSE if it
  157. should be terminated.
  158. --*/
  159. {
  160. //
  161. // Enumerate the segments for the specified heap.
  162. //
  163. if( !EnumHeapSegments(
  164. LocalHeap,
  165. RemoteHeap,
  166. HspEnumHeapSegmentsProc,
  167. Param
  168. ) ) {
  169. dprintf( "error retrieving heap segments\n" );
  170. return FALSE;
  171. }
  172. return TRUE;
  173. } // HspEnumHeapsProc
  174. DECLARE_API( heapstat )
  175. /*++
  176. Routine Description:
  177. This function is called as an NTSD extension to format and dump
  178. heap statistics.
  179. Arguments:
  180. hCurrentProcess - Supplies a handle to the current process (at the
  181. time the extension was called).
  182. hCurrentThread - Supplies a handle to the current thread (at the
  183. time the extension was called).
  184. CurrentPc - Supplies the current pc at the time the extension is
  185. called.
  186. lpExtensionApis - Supplies the address of the functions callable
  187. by this extension.
  188. lpArgumentString - Supplies the asciiz string that describes the
  189. ansi string to be dumped.
  190. Return Value:
  191. None.
  192. --*/
  193. {
  194. PENUM_CONTEXT context;
  195. ULONG i;
  196. ULONG busyBytes;
  197. ULONG totalBusy;
  198. ULONG totalFree;
  199. ULONG totalBusyBytes;
  200. ULONG lowerNoiseBound;
  201. INIT_API();
  202. //
  203. // Setup.
  204. //
  205. context = (PENUM_CONTEXT)malloc( sizeof(*context) );
  206. if( context == NULL ) {
  207. dprintf( "out of memory\n" );
  208. return;
  209. }
  210. RtlZeroMemory(
  211. context,
  212. sizeof(*context)
  213. );
  214. context->ContinueEnum = TRUE;
  215. //
  216. // Skip leading blanks.
  217. //
  218. while( *lpArgumentString == ' ' ||
  219. *lpArgumentString == '\t' ) {
  220. lpArgumentString++;
  221. }
  222. if( *lpArgumentString == '\0' ) {
  223. lowerNoiseBound = 1;
  224. } else {
  225. lowerNoiseBound = strtoul( lpArgumentString, NULL, 16 );
  226. }
  227. //
  228. // Enumerate the heaps, which will enumerate the segments, which
  229. // will enumerate the entries, which will accumulate the statistics.
  230. //
  231. if( !EnumProcessHeaps(
  232. HspEnumHeapsProc,
  233. (PVOID)context
  234. ) ) {
  235. dprintf( "error retrieving process heaps\n" );
  236. free( context );
  237. return;
  238. }
  239. //
  240. // Dump 'em.
  241. //
  242. dprintf(
  243. " Size : NumBusy : NumFree : BusyBytes\n"
  244. );
  245. totalBusy = 0;
  246. totalFree = 0;
  247. totalBusyBytes = 0;
  248. for( i = 0 ; i < MAX_SIZE ; i++ ) {
  249. if (CheckControlC())
  250. goto cleanup;
  251. busyBytes = i * context->BusyCounters[i];
  252. if( context->BusyCounters[i] >= lowerNoiseBound ||
  253. context->FreeCounters[i] >= lowerNoiseBound ) {
  254. dprintf(
  255. " %5lx : %8lx : %8lx : %8lx (%10ldK)\n",
  256. i,
  257. context->BusyCounters[i],
  258. context->FreeCounters[i],
  259. busyBytes,
  260. BYTES_TO_K( busyBytes )
  261. );
  262. }
  263. totalBusy += context->BusyCounters[i];
  264. totalBusyBytes += busyBytes;
  265. totalFree += context->FreeCounters[i];
  266. }
  267. if( context->BusyJumbo >= lowerNoiseBound ||
  268. context->FreeJumbo >= lowerNoiseBound ) {
  269. dprintf(
  270. ">%5lx : %8lx : %8lx : %8lx (%10ldK)\n",
  271. MAX_SIZE,
  272. context->BusyJumbo,
  273. context->FreeJumbo,
  274. context->BusyJumboBytes,
  275. BYTES_TO_K( context->BusyJumboBytes )
  276. );
  277. totalBusy += context->BusyJumbo;
  278. totalFree += context->FreeJumbo;
  279. totalBusyBytes += context->BusyJumboBytes;
  280. }
  281. if (context->LargeBusyBlock[0] != 0) {
  282. for (i = 0; i < MAX_LBBSIZE && context->LargeBusyBlock[i] != 0; i++) {
  283. dprintf("%8lx : \n", context->LargeBusyBlock[i]);
  284. }
  285. }
  286. dprintf(
  287. " Total : %8lx : %8lx : %8lx (%10ldK)\n"
  288. "\n"
  289. " Total Heap Impact from Busy Blocks = %8lx (%10ldK)\n"
  290. " Total Heap Impact from Free Blocks = %8lx (%10ldK)\n",
  291. totalBusy,
  292. totalFree,
  293. totalBusyBytes,
  294. BYTES_TO_K( totalBusyBytes ),
  295. context->BusyOverhead,
  296. BYTES_TO_K( context->BusyOverhead ),
  297. context->FreeOverhead,
  298. BYTES_TO_K( context->FreeOverhead )
  299. );
  300. cleanup:
  301. free( context );
  302. } // DECLARE_API( heapstat )