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.

609 lines
14 KiB

  1. //
  2. // Copyright (c) 2000 Microsoft Corporation
  3. //
  4. // Module Name
  5. //
  6. // heapwalk.c
  7. //
  8. // Abstract
  9. //
  10. // Contains functions that create/modify/update the datastructure
  11. // HEAP_ENTRY_LIST. HEAP_ENTRY_LIST maintains miminum amount of data
  12. // for a HEAP Object.
  13. //
  14. // Author
  15. //
  16. // Narayana Batchu (nbatchu) [May 11, 2001]
  17. //
  18. #include <windows.h>
  19. #include <stdlib.h>
  20. #include <stdio.h>
  21. #include <tchar.h>
  22. #include "heapwalk.h"
  23. //
  24. // Initialize
  25. //
  26. // Initializes and allocates memory for the private member
  27. // variables of the HEAP_ENTRY_LIST datastructure.
  28. //
  29. // Arguments
  30. //
  31. // pList Pointer to HEAP_ENTRY_LIST whose member variables
  32. // to be initialized.
  33. //
  34. // Return Value
  35. //
  36. VOID Initialize(LPHEAP_ENTRY_LIST pList)
  37. {
  38. if (!pList) return;
  39. pList->HeapEntryCount = 0;
  40. pList->ListSorted = TRUE;
  41. pList->PresentCapacity = INITIAL_CAPACITY;
  42. pList->pHeapEntries = (LPHEAP_ENTRY_INFO)HeapAlloc(
  43. GetProcessHeap(),
  44. HEAP_ZERO_MEMORY,
  45. sizeof(HEAP_ENTRY_INFO) * pList->PresentCapacity
  46. );
  47. if (!pList->pHeapEntries)
  48. pList->PresentCapacity = 0;
  49. }
  50. //
  51. // DestroyList
  52. //
  53. // Cleans up the datastructure HEAP_ENTRY_LIST and frees up the
  54. // memory associated with the pHeapEntries member.
  55. //
  56. // Arguments
  57. //
  58. // pList Pointer to HEAP_ENTRY_LIST whose member variables
  59. // to be cleaned up.
  60. //
  61. // Return Value
  62. //
  63. VOID DestroyList(LPHEAP_ENTRY_LIST pList)
  64. {
  65. if (!pList) return;
  66. pList->HeapEntryCount = 0;
  67. pList->ListSorted = TRUE;
  68. pList->PresentCapacity = 0;
  69. HeapFree(GetProcessHeap(), 0, pList->pHeapEntries);
  70. }
  71. //
  72. // GetMaxBlockSize
  73. //
  74. // This function searches through the HEAP_ENTRY_LIST to find out
  75. // the maximum block size whose status is defined by 'State'.
  76. //
  77. // Arguments
  78. //
  79. // pList Pointer to HEAP_ENTRY_LIST.
  80. //
  81. // State Specifies the status to search for the maximum size.
  82. // State of any block can be 0 (FREE) and 1 (BUSY).
  83. // There are other valid status values also,
  84. // but we dont maintain those entries.
  85. //
  86. // Return Value
  87. //
  88. // DWORD Returns the maximum size of the block with status 'State'.
  89. //
  90. ULONG GetMaxBlockSize(LPHEAP_ENTRY_LIST pList, BLOCK_STATE State)
  91. {
  92. ULONG MaxBlockSize = 0;
  93. UINT Index;
  94. if (!pList) goto ERROR1;
  95. if (FALSE == pList->ListSorted)
  96. {
  97. SortHeapEntries(pList);
  98. }
  99. for (Index=0; Index < pList->HeapEntryCount; Index++)
  100. {
  101. if (State == pList->pHeapEntries[Index].BlockState)
  102. {
  103. MaxBlockSize = pList->pHeapEntries[Index].BlockSize;
  104. break;
  105. }
  106. }
  107. ERROR1:
  108. return MaxBlockSize;
  109. }
  110. //
  111. // GetMaxFreeBlockSize
  112. //
  113. // This function searches through the HEAP_ENTRY_LIST to find out
  114. // the maximum free block size.
  115. //
  116. // Arguments
  117. //
  118. // pList Pointer to HEAP_ENTRY_LIST.
  119. //
  120. // Return Value
  121. //
  122. // DWORD Returns the maximum size of the available block
  123. //
  124. ULONG GetMaxFreeBlockSize(LPHEAP_ENTRY_LIST pList)
  125. {
  126. return GetMaxBlockSize(pList, HEAP_BLOCK_FREE);
  127. }
  128. //
  129. // GetMaxAllocBlockSize
  130. //
  131. // This function searches through the HEAP_ENTRY_LIST to find out
  132. // the maximum allocated block size.
  133. //
  134. // Arguments
  135. //
  136. // pList Pointer to HEAP_ENTRY_LIST.
  137. //
  138. // Return Value
  139. //
  140. // DWORD Returns the maximum size of the allocated block.
  141. //
  142. ULONG GetMaxAllocBlockSize(LPHEAP_ENTRY_LIST pList)
  143. {
  144. return GetMaxBlockSize(pList, HEAP_BLOCK_BUSY);
  145. }
  146. //
  147. // GetTopNfreeEntries
  148. //
  149. // This function scans through the entry list to find the top
  150. // n free entries in the list.
  151. //
  152. // Arguments
  153. //
  154. // pList Pointer to HEAP_ENTRY_LIST.
  155. //
  156. // pArray Array of HEAP_ENTRY_INFO structures. This holds the
  157. // top n free block sizes available for the process.
  158. //
  159. // Entries Specifies the top number of entries to be read from
  160. // the list.
  161. //
  162. // Return Value
  163. //
  164. // BOOL Returns TRUE if successful.
  165. //
  166. BOOL GetTopNfreeEntries(
  167. LPHEAP_ENTRY_LIST pList,
  168. LPHEAP_ENTRY_INFO pArray,
  169. UINT EntriesToRead)
  170. {
  171. return GetTopNentries(
  172. HEAP_BLOCK_FREE,
  173. pList,
  174. pArray,
  175. EntriesToRead
  176. );
  177. }
  178. //
  179. // GetTopNallocEntries
  180. //
  181. // This function scans through the entry list to find the top
  182. // n allocated entries in the list.
  183. //
  184. // Arguments
  185. //
  186. // pList Pointer to HEAP_ENTRY_LIST.
  187. //
  188. // pArray Array of HEAP_ENTRY_INFO structures. This holds the
  189. // top n allocated block sizes available for the process.
  190. //
  191. // Entries Specifies the top number of entries to be read from
  192. // the list.
  193. //
  194. // Return Value
  195. //
  196. // BOOL Returns TRUE if successful.
  197. //
  198. BOOL GetTopNallocEntries(
  199. LPHEAP_ENTRY_LIST pList,
  200. LPHEAP_ENTRY_INFO pArray,
  201. UINT EntriesToRead
  202. )
  203. {
  204. return GetTopNentries(
  205. HEAP_BLOCK_BUSY,
  206. pList,
  207. pArray,
  208. EntriesToRead
  209. );
  210. }
  211. //
  212. // GetTopNallocEntries
  213. //
  214. // This function scans through the entry list to find the top
  215. // n entries in the list, whose staus matches 'State'.
  216. //
  217. // Arguments
  218. //
  219. // pList Pointer to HEAP_ENTRY_LIST.
  220. //
  221. // pArray Array of HEAP_ENTRY_INFO structures. This holds the
  222. // top n block sizes available for the process, whose status
  223. // matches 'State'.
  224. //
  225. // Entries Specifies the top number of entries to be read from
  226. // the list.
  227. //
  228. // Return Value
  229. //
  230. // BOOL Returns TRUE if successful.
  231. //
  232. BOOL GetTopNentries(
  233. BLOCK_STATE State,
  234. LPHEAP_ENTRY_LIST pList,
  235. LPHEAP_ENTRY_INFO pArray,
  236. UINT EntriesToRead
  237. )
  238. {
  239. BOOL fSuccess = FALSE;
  240. UINT EntriesRead = 0;
  241. UINT Index;
  242. if (!pArray || !pList) goto ERROR2;
  243. if (FALSE == pList->ListSorted)
  244. {
  245. SortHeapEntries(pList);
  246. }
  247. for (Index=0; Index < pList->HeapEntryCount; Index++)
  248. {
  249. if (EntriesRead == EntriesToRead)
  250. break;
  251. if (State == pList->pHeapEntries[Index].BlockState)
  252. {
  253. pArray[EntriesRead].BlockSize =
  254. pList->pHeapEntries[Index].BlockSize;
  255. pArray[EntriesRead].BlockCount =
  256. pList->pHeapEntries[Index].BlockCount;
  257. pArray[EntriesRead].BlockState =
  258. pList->pHeapEntries[Index].BlockState;
  259. EntriesRead++;
  260. }
  261. }
  262. if (EntriesRead == EntriesToRead)
  263. fSuccess = TRUE;
  264. ERROR2:
  265. return fSuccess;
  266. }
  267. //
  268. // IncreaseCapacity
  269. //
  270. // Increases the array capacity by double. This function is called
  271. // when tried to insert at the end of the array which is full.
  272. //
  273. // Arguments
  274. //
  275. // pList Pointer to HEAP_ENTRY_LIST.
  276. //
  277. // Return Value
  278. //
  279. // BOOL Returns TRUE if successful in increasing the capacity.
  280. //
  281. BOOL IncreaseCapacity(LPHEAP_ENTRY_LIST pList)
  282. {
  283. BOOL fSuccess = FALSE;
  284. UINT NewCapacity;
  285. if (!pList) goto ERROR3;
  286. NewCapacity = pList->PresentCapacity * 2;
  287. if (0 == NewCapacity)
  288. NewCapacity = INITIAL_CAPACITY;
  289. __try
  290. {
  291. pList->pHeapEntries = (LPHEAP_ENTRY_INFO)HeapReAlloc(
  292. GetProcessHeap(),
  293. HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY,
  294. pList->pHeapEntries,
  295. sizeof(HEAP_ENTRY_INFO) * NewCapacity
  296. );
  297. pList->PresentCapacity = NewCapacity;
  298. fSuccess = TRUE;
  299. }
  300. __except(GetExceptionCode() == STATUS_NO_MEMORY ||
  301. GetExceptionCode() == STATUS_ACCESS_VIOLATION)
  302. {
  303. //
  304. // Ignoring the exceptions raised by HeapReAlloc().
  305. //
  306. }
  307. ERROR3:
  308. return fSuccess;
  309. }
  310. //
  311. // FindMatch
  312. //
  313. // Finds an entry in the HEAP_ENTRY_LIST that matches the size and
  314. // status of pHeapEntry.
  315. //
  316. // Arguments
  317. //
  318. // pList Pointer to HEAP_ENTRY_LIST.
  319. //
  320. // pHeapEntry Pointer to HEAP_ENTRY_INFO to be serached for in 'pList'.
  321. //
  322. // Return Value
  323. //
  324. // DWORD Index of the heap entry that matched the input heap entry
  325. // 'pHeapEntry'
  326. //
  327. //
  328. UINT FindMatch(LPHEAP_ENTRY_LIST pList, LPHEAP_ENTRY_INFO pHeapEntry)
  329. {
  330. UINT MatchedEntry = NO_MATCH;
  331. UINT Index;
  332. if (!pList || !pHeapEntry) goto ERROR4;
  333. for (Index = 0; Index < pList->HeapEntryCount; Index++)
  334. {
  335. if (pList->pHeapEntries[Index].BlockSize == pHeapEntry->BlockSize &&
  336. pList->pHeapEntries[Index].BlockState == pHeapEntry->BlockState)
  337. {
  338. MatchedEntry = Index;
  339. break;
  340. }
  341. }
  342. ERROR4:
  343. return MatchedEntry;
  344. }
  345. //
  346. // InsertHeapEntry
  347. //
  348. // Inserts a new heap entry to the list. It updates the block count if
  349. // a match is found else a new entry is made at the end of the HEAP_
  350. // ENTRY_INFO array.
  351. //
  352. // Arguments
  353. //
  354. // pList Pointer to HEAP_ENTRY_LIST.
  355. //
  356. // pHeapEntry Pointer to HEAP_ENTRY_INFO that is to be added to 'pList'.
  357. //
  358. // Return Value
  359. //
  360. // DWORD Returns the index at which it is added to the array. If
  361. // for any reason, it is not added to the list, then it
  362. // returns NO_MATCH value.
  363. //
  364. UINT InsertHeapEntry(LPHEAP_ENTRY_LIST pList, LPHEAP_ENTRY_INFO pHeapEntry)
  365. {
  366. UINT MatchedEntry = NO_MATCH;
  367. if (!pList || !pHeapEntry) goto ERROR5;
  368. MatchedEntry = FindMatch(pList, pHeapEntry);
  369. if (NO_MATCH != MatchedEntry)
  370. pList->pHeapEntries[MatchedEntry].BlockCount++;
  371. else
  372. {
  373. UINT Index = pList->HeapEntryCount;
  374. if (Index == pList->PresentCapacity && !IncreaseCapacity(pList))
  375. goto ERROR5;
  376. pList->pHeapEntries[Index].BlockSize = pHeapEntry->BlockSize;
  377. pList->pHeapEntries[Index].BlockState = pHeapEntry->BlockState;
  378. pList->pHeapEntries[Index].BlockCount = 1;
  379. MatchedEntry = Index;
  380. pList->HeapEntryCount++;
  381. pList->ListSorted = FALSE;
  382. }
  383. ERROR5:
  384. return MatchedEntry;
  385. }
  386. //
  387. // DeleteHeapEntry
  388. //
  389. // Deletes a new heap entry to the list. It decrements the block count
  390. // if a match is found.
  391. //
  392. // Its possible that the block size is zero and still the heap entry
  393. // exits. In such cases we dont decrement the block count (which would
  394. // make it negative) and return a NO_MATCH.
  395. //
  396. // Arguments
  397. //
  398. // pList Pointer to HEAP_ENTRY_LIST
  399. //
  400. // pHeapEntry Pointer to HEAP_ENTRY_INFO that is to be removed from 'pList'.
  401. //
  402. // Return Value
  403. //
  404. // DWORD Returns the index at which it is removed from the array. If for
  405. // any reason (Count==0), it is not removed to the list, then it
  406. // returns NO_MATCH value.
  407. //
  408. UINT DeleteHeapEntry(LPHEAP_ENTRY_LIST pList, LPHEAP_ENTRY_INFO pHeapEntry)
  409. {
  410. UINT MatchedEntry = NO_MATCH;
  411. if (!pList || !pHeapEntry) goto ERROR6;
  412. MatchedEntry = FindMatch(pList, pHeapEntry);
  413. if (NO_MATCH != MatchedEntry &&
  414. 0 != pList->pHeapEntries[MatchedEntry].BlockCount)
  415. {
  416. pList->pHeapEntries[MatchedEntry].BlockCount--;
  417. }
  418. else
  419. MatchedEntry = NO_MATCH;
  420. ERROR6:
  421. return MatchedEntry;
  422. }
  423. //
  424. // SortByBlockSize
  425. //
  426. // Compare function required by qsort (uses quick sort to sort
  427. // the elements in the array).
  428. //
  429. // More info about the arguments and the return values could be
  430. // found in MSDN.
  431. //
  432. int __cdecl SortByBlockSize(const void * arg1, const void *arg2)
  433. {
  434. int iCompare;
  435. LPHEAP_ENTRY_INFO hpEntry1 = (LPHEAP_ENTRY_INFO)arg1;
  436. LPHEAP_ENTRY_INFO hpEntry2 = (LPHEAP_ENTRY_INFO)arg2;
  437. iCompare = (hpEntry2->BlockSize - hpEntry1->BlockSize);
  438. return iCompare;
  439. }
  440. //
  441. // DisplayHeapFragStatistics
  442. //
  443. // Sorts and displays the fragmentation statistics. It displays
  444. // two tables one for free blocks and another for allocated blocks.
  445. //
  446. // Arguments
  447. //
  448. // File Pointer to C FILE structure, to which the heap frag-
  449. // mentation statistics have to be dumped.
  450. //
  451. // pList Pointer to HEAP_ENTRY_LIST, to be sorted and
  452. // dumped to 'File'.
  453. //
  454. // Return Value
  455. //
  456. VOID DisplayHeapFragStatistics(
  457. FILE * File,
  458. PVOID HeapAddress,
  459. LPHEAP_ENTRY_LIST pList
  460. )
  461. {
  462. if (!pList) return;
  463. fprintf(
  464. File,
  465. "\n*- - - - - - - - - - Heap %p Fragmentation Statistics - - - - - - - - - -\n\n",
  466. HeapAddress
  467. );
  468. SortHeapEntries(pList);
  469. PrintList(File, pList, HEAP_BLOCK_BUSY);
  470. PrintList(File, pList, HEAP_BLOCK_FREE);
  471. }
  472. //
  473. // SortHeapEntries
  474. //
  475. // Sorts the heap entries based on their sizes. The top most entry
  476. // would be having the maximun block size.
  477. //
  478. // Also, removes those heap entries from the array whose block count
  479. // has dropped to zero, making available more space.
  480. //
  481. // Arguments
  482. //
  483. // pList Pointer to HEAP_ENTRY_LIST, whose entries to be sorted by
  484. // their sizes.
  485. //
  486. // Return Value
  487. //
  488. VOID SortHeapEntries(LPHEAP_ENTRY_LIST pList)
  489. {
  490. UINT Index;
  491. if (!pList) return;
  492. if (FALSE == pList->ListSorted)
  493. {
  494. qsort(
  495. pList->pHeapEntries,
  496. pList->HeapEntryCount,
  497. sizeof(HEAP_ENTRY_INFO),
  498. &SortByBlockSize
  499. );
  500. for (Index = pList->HeapEntryCount-1; Index > 0; Index--)
  501. {
  502. if (0 != pList->pHeapEntries[Index].BlockCount)
  503. break;
  504. }
  505. pList->HeapEntryCount = Index + 1;
  506. pList->ListSorted = TRUE;
  507. }
  508. }
  509. //
  510. // PrintList
  511. //
  512. // Utility function that prints out the heap entries to the stdout/
  513. // file, whose status is equal to "State".
  514. //
  515. // Arguments
  516. //
  517. // File Pointer to C FILE structure, to which the heap frag-
  518. // mentation statistics have to be dumped.
  519. //
  520. // pList Pointer to HEAP_ENTRY_LIST, to be sorted and
  521. // dumped to 'File'.
  522. //
  523. // State State of the blocks to be displayed.
  524. //
  525. // Return Value
  526. //
  527. VOID PrintList(FILE * File, LPHEAP_ENTRY_LIST pList, BLOCK_STATE State)
  528. {
  529. UINT Index;
  530. if (!pList) return;
  531. if (HEAP_BLOCK_FREE == State)
  532. fprintf(File, "\nTable of Free Blocks\n\n");
  533. else if (HEAP_BLOCK_BUSY == State)
  534. fprintf(File, "\nTable of Allocated Blocks\n\n");
  535. fprintf(File, " SIZE | COUNT\n");
  536. fprintf(File, " --------------\n");
  537. for (Index = 0; Index < pList->HeapEntryCount; Index++)
  538. {
  539. if (State == pList->pHeapEntries[Index].BlockState)
  540. {
  541. fprintf(
  542. File,
  543. " 0x%04x | 0x%02x\n",
  544. pList->pHeapEntries[Index].BlockSize,
  545. pList->pHeapEntries[Index].BlockCount
  546. );
  547. }
  548. }
  549. fprintf(File, "\n");
  550. }