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.

817 lines
24 KiB

  1. #include "stdinc.h"
  2. #include "fusionheap.h"
  3. #include "fusionbuffer.h"
  4. #include "debmacro.h"
  5. #define NUMBER_OF(x) (sizeof(x)/sizeof((x)[0]))
  6. #define MAX_VTBL_ENTRIES 256
  7. FUSION_HEAP_HANDLE g_hHeap = NULL;
  8. #if FUSION_DEBUG_HEAP
  9. FUSION_HEAP_HANDLE g_hDebugInfoHeap = NULL;
  10. LONG g_FusionHeapAllocationCount = 0;
  11. LONG g_FusionHeapAllocationToBreakOn = 0;
  12. PVOID g_FusionHeapDeallocationPtrToBreakOn = NULL;
  13. PVOID g_FusionHeapAllocationPtrToBreakOn = NULL;
  14. WCHAR g_FusionModuleNameUnknown[] = L"(Unknown module)";
  15. //
  16. // g_FusionHeapOperationCount is used to keep track of the total number of
  17. // allocations and deallocations; the heap is verified each
  18. // g_FusionHeapCheckFrequency operations. Set it to zero to disable any
  19. // non-explicit checks.
  20. //
  21. LONG g_FusionHeapOperationCount = 0;
  22. LONG g_FusionHeapCheckFrequency = 1;
  23. // Set g_FusionUsePrivateHeap to TRUE in your DllMain prior to
  24. // calling FusionpInitializeHeap() to get a private heap for this DLL.
  25. BOOL g_FusionUsePrivateHeap = FALSE;
  26. //
  27. // Setting this boolean enables stack-back-tracing for allocations. This
  28. // will make your life infinitely easier when trying to debug leaks. However,
  29. // it will eat piles more debug heap. Set it via a breakin or in your DllMain.
  30. //
  31. // ABSOLUTELY DO NOT CHECK THIS IN WITH STACK TRACKING ENABLED!!!!!
  32. //
  33. BOOL g_FusionHeapTrackStackTraces = FALSE;
  34. // g_FusionHeapPostAllocationBytes is the number of extra bytes
  35. // to allocate and write a pattern on to watch for people overwriting
  36. // their allocations.
  37. LONG g_FusionHeapPostAllocationBytes = 8;
  38. UCHAR g_FusionHeapPostAllocationChar = 0xf0;
  39. UCHAR g_FusionHeapAllocationPoisonChar = 0xfa;
  40. UCHAR g_FusionHeapDeallocationPoisonChar = 0xfd;
  41. // HINSTANCE used when initializing the heap; we use it later to report the
  42. // dll name.
  43. HINSTANCE g_FusionHeapHInstance;
  44. #endif // FUSION_DEBUG_HEAP
  45. BOOL
  46. FusionpInitializeHeap(
  47. HINSTANCE hInstance
  48. )
  49. {
  50. #if FUSION_DEBUG_HEAP
  51. BOOL fSuccess = FALSE;
  52. #if FUSION_PRIVATE_HEAP
  53. g_hHeap = (FUSION_HEAP_HANDLE) ::HeapCreate(0, 0, 0);
  54. if (g_hHeap == NULL)
  55. {
  56. ::FusionpDbgPrintEx(
  57. FUSION_DBG_LEVEL_ERROR,
  58. "SXS: Failed to create private heap. FusionpGetLastWin32Error() = %d\n", ::FusionpGetLastWin32Error());
  59. goto Exit;
  60. }
  61. #else
  62. if (g_FusionUsePrivateHeap)
  63. {
  64. g_hHeap = (FUSION_HEAP_HANDLE) ::HeapCreate(0, 0, 0);
  65. if (g_hHeap == NULL)
  66. {
  67. ::FusionpDbgPrintEx(
  68. FUSION_DBG_LEVEL_ERROR,
  69. "SXS: Failed to create private heap. FusionpGetLastWin32Error() = %d\n", ::FusionpGetLastWin32Error());
  70. goto Exit;
  71. }
  72. }
  73. else
  74. {
  75. g_hHeap = (FUSION_HEAP_HANDLE) ::GetProcessHeap();
  76. if (g_hHeap == NULL)
  77. {
  78. ::FusionpDbgPrintEx(
  79. FUSION_DBG_LEVEL_ERROR,
  80. "SXS: Failed to get process default heap. FusionpGetLastWin32Error() = %d\n", ::FusionpGetLastWin32Error());
  81. goto Exit;
  82. }
  83. }
  84. #endif
  85. g_hDebugInfoHeap = (FUSION_HEAP_HANDLE) ::HeapCreate(0, 0, 0);
  86. if (g_hDebugInfoHeap == NULL)
  87. {
  88. goto Exit;
  89. }
  90. g_FusionHeapHInstance = hInstance;
  91. fSuccess = TRUE;
  92. Exit:
  93. return fSuccess;
  94. #else
  95. g_hHeap = (FUSION_HEAP_HANDLE) ::GetProcessHeap();
  96. return TRUE;
  97. #endif
  98. }
  99. VOID
  100. FusionpUninitializeHeap()
  101. {
  102. #if FUSION_DEBUG_HEAP
  103. BOOL fHeapLocked = FALSE;
  104. BOOL fDebugHeapLocked = FALSE;
  105. PROCESS_HEAP_ENTRY phe;
  106. WCHAR DllName[MAX_PATH / sizeof(WCHAR)]; // keep stack frame to ~MAX_PATH bytes
  107. if (g_hHeap == NULL || g_hDebugInfoHeap == NULL)
  108. goto Exit;
  109. DllName[0] = 0;
  110. if (g_FusionHeapHInstance != NULL)
  111. ::GetModuleFileNameW(g_FusionHeapHInstance, DllName, NUMBER_OF(DllName));
  112. if (!::HeapLock(g_hHeap))
  113. goto ReportError;
  114. fHeapLocked = TRUE;
  115. if (!::HeapLock(g_hDebugInfoHeap))
  116. goto ReportError;
  117. fDebugHeapLocked = TRUE;
  118. // Walk the debug heap looking for allocations...
  119. phe.lpData = NULL;
  120. while (::HeapWalk(g_hDebugInfoHeap, &phe))
  121. {
  122. if (!(phe.wFlags & PROCESS_HEAP_ENTRY_BUSY))
  123. continue;
  124. PFUSION_HEAP_ALLOCATION_TRACKER pTracker = (PFUSION_HEAP_ALLOCATION_TRACKER) phe.lpData;
  125. if (pTracker == NULL)
  126. continue;
  127. if (pTracker->Prefix == NULL)
  128. continue;
  129. // Stop the prefix from pointing at the debug info; we're doing to destroy the debug heap.
  130. pTracker->Prefix->Tracker = NULL;
  131. }
  132. //
  133. // On invalid function, meaning HeapWalk is not defined, just exit.
  134. // Same for no-more-items, meaning the end of the list is nigh. We
  135. // make the assumption that none of the other functions in the loop
  136. // can fail with E_N_M_I or E_I_F - this may be a fallacy for later.
  137. //
  138. switch (::FusionpGetLastWin32Error())
  139. {
  140. case ERROR_INVALID_FUNCTION:
  141. case ERROR_NO_MORE_ITEMS:
  142. goto Exit;
  143. default:
  144. goto ReportError;
  145. }
  146. // Original code:
  147. //
  148. // if (::FusionpGetLastWin32Error() != ERROR_NO_MORE_ITEMS)
  149. // goto ReportError;
  150. //
  151. goto Exit;
  152. ReportError:
  153. FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "%s: FusionpUninitializeHeap() encountered an error; FusionpGetLastWin32Error() = %d\n", DllName, ::FusionpGetLastWin32Error());
  154. Exit:
  155. if (fDebugHeapLocked)
  156. ::HeapUnlock(g_hDebugInfoHeap);
  157. if (fHeapLocked)
  158. ::HeapUnlock(g_hHeap);
  159. if (g_hDebugInfoHeap != NULL)
  160. ::HeapDestroy(g_hDebugInfoHeap);
  161. #if 0 // should do this, but need to test it, and no diff due to #ifdefs
  162. if (g_hHeap != NULL && g_Heap != ::GetProcessHeap())
  163. ::HeapDestroy(g_hHeap);
  164. #endif
  165. g_hHeap = NULL;
  166. g_hDebugInfoHeap = NULL;
  167. #endif
  168. }
  169. #if FUSION_DEBUG_HEAP
  170. VOID
  171. FusionpDumpHeap(
  172. PCWSTR PerLinePrefixWithoutSpaces
  173. )
  174. {
  175. BOOL fHeapLocked = FALSE;
  176. BOOL fDebugHeapLocked = FALSE;
  177. PROCESS_HEAP_ENTRY phe;
  178. WCHAR DllName[MAX_PATH / sizeof(WCHAR) / 2];
  179. WCHAR PerLinePrefix[MAX_PATH / sizeof(WCHAR) / 2]; // only MAX_PATH bytes for prev two variables
  180. const static WCHAR PerLineSpacesPrefix[] = L" ";
  181. if (g_hHeap == NULL || g_hDebugInfoHeap == NULL)
  182. goto Exit;
  183. // sprintf is overkill, but convenient, and it lets us reuse the buffer size support..
  184. ::_snwprintf(PerLinePrefix, NUMBER_OF(PerLinePrefix), L"%S%S", PerLinePrefixWithoutSpaces, PerLineSpacesPrefix);
  185. PerLinePrefix[NUMBER_OF(PerLinePrefix) - 1] = L'\0';
  186. DllName[0] = 0;
  187. ::GetModuleFileNameW(g_FusionHeapHInstance, DllName, NUMBER_OF(DllName));
  188. try
  189. {
  190. if (!::HeapLock(g_hHeap))
  191. goto ReportError;
  192. fHeapLocked = TRUE;
  193. if (!::HeapLock(g_hDebugInfoHeap))
  194. goto ReportError;
  195. fDebugHeapLocked = TRUE;
  196. // Walk the debug heap looking for allocations...
  197. phe.lpData = NULL;
  198. while (::HeapWalk(g_hDebugInfoHeap, &phe))
  199. {
  200. PCSTR HeapString;
  201. SIZE_T cbToShow;
  202. if (!(phe.wFlags & PROCESS_HEAP_ENTRY_BUSY))
  203. continue;
  204. PFUSION_HEAP_ALLOCATION_TRACKER pTracker = (PFUSION_HEAP_ALLOCATION_TRACKER) phe.lpData;
  205. if (pTracker == NULL)
  206. continue;
  207. if (pTracker->Prefix == NULL)
  208. continue;
  209. #if 0 // as long as the buffer isn't heap allocated, this check isn't needed
  210. // Skip the buffer we're using for formatting the output...
  211. if (((PCWSTR) (((ULONG_PTR) pTracker->Prefix) + sizeof(FUSION_HEAP_PREFIX))) == PerLinePrefix)
  212. continue;
  213. #endif
  214. // If the caller wanted us to not report this allocation as being leaky, don't.
  215. if (pTracker->Flags & FUSION_HEAP_DO_NOT_REPORT_LEAKED_ALLOCATION)
  216. continue;
  217. if (pTracker->Heap == g_hHeap)
  218. HeapString = "default heap";
  219. else
  220. HeapString = "custom heap";
  221. cbToShow = pTracker->RequestedSize;
  222. if (cbToShow > 64)
  223. cbToShow = 64;
  224. FusionpDbgPrintEx(
  225. FUSION_DBG_LEVEL_ERROR,
  226. "%s(%u): Memory allocation leaked!\n", pTracker->FileName, pTracker->Line);
  227. FusionpDbgPrintEx(
  228. FUSION_DBG_LEVEL_ERROR,
  229. "%SLeaked %S allocation #%u (0x%lx) at %p \"%s\" (tracked by %p; allocated from heap %p - %s)\n"
  230. "%S Requested bytes/Allocated bytes: %Iu / %Iu (dumping %Iu bytes)\n",
  231. PerLinePrefix, DllName, pTracker->SequenceNumber, pTracker->SequenceNumber, pTracker->Prefix, pTracker->Expression, pTracker, pTracker->Heap, HeapString,
  232. PerLinePrefix, pTracker->RequestedSize, pTracker->AllocationSize, cbToShow);
  233. FusionpDbgPrintEx(
  234. FUSION_DBG_LEVEL_ERROR,
  235. "%s Allocated at line %u of %s\n",
  236. PerLinePrefix, pTracker->Line, pTracker->FileName);
  237. #if FUSION_ENABLE_FROZEN_STACK
  238. if (pTracker->pvFrozenStack)
  239. ::FusionpOutputFrozenStack(
  240. FUSION_DBG_LEVEL_ERROR,
  241. "SXS",
  242. (PFROZEN_STACK)pTracker->pvFrozenStack);
  243. #endif
  244. FusionpDbgPrintBlob(
  245. FUSION_DBG_LEVEL_ERROR,
  246. pTracker->Prefix + 1,
  247. cbToShow,
  248. PerLinePrefix);
  249. }
  250. //
  251. // On invalid function, meaning HeapWalk is not defined, just exit.
  252. // Same for no-more-items, meaning the end of the list is nigh. We
  253. // make the assumption that none of the other functions in the loop
  254. // can fail with E_N_M_I or E_I_F - this may be a fallacy for later.
  255. //
  256. switch (::FusionpGetLastWin32Error())
  257. {
  258. case ERROR_INVALID_FUNCTION:
  259. case ERROR_NO_MORE_ITEMS:
  260. goto Exit;
  261. default:
  262. goto ReportError;
  263. }
  264. // Original code:
  265. //
  266. // if (::FusionpGetLastWin32Error() != ERROR_NO_MORE_ITEMS)
  267. // goto ReportError;
  268. //
  269. }
  270. catch(...)
  271. {
  272. }
  273. goto Exit;
  274. ReportError:
  275. FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "%S: FusionpDumpHeap() encountered an error; FusionpGetLastWin32Error() = %d\n", DllName, ::FusionpGetLastWin32Error());
  276. Exit:
  277. if (fDebugHeapLocked)
  278. ::HeapUnlock(g_hDebugInfoHeap);
  279. if (fHeapLocked)
  280. ::HeapUnlock(g_hHeap);
  281. }
  282. VOID
  283. FusionpValidateHeap(
  284. FUSION_HEAP_HANDLE hFusionHeap
  285. )
  286. {
  287. FN_TRACE();
  288. BOOL fHeapLocked = FALSE;
  289. BOOL fDebugHeapLocked = FALSE;
  290. PROCESS_HEAP_ENTRY phe;
  291. SIZE_T i;
  292. WCHAR DllName[MAX_PATH / sizeof(WCHAR)]; // keep stack frame to ~MAX_PATH bytes
  293. PCWSTR DllNamePointer = DllName;
  294. DWORD dwCallStatus;
  295. HANDLE hHeap = (HANDLE) hFusionHeap;
  296. DllName[0] = 0;
  297. if (g_hDebugInfoHeap == NULL)
  298. goto Exit;
  299. //
  300. // Get the current module's name, but don't print garbage if it fails.
  301. //
  302. dwCallStatus = ::GetModuleFileNameW(g_FusionHeapHInstance, DllName, NUMBER_OF(DllName));
  303. if (!dwCallStatus)
  304. {
  305. ::FusionpDbgPrintEx(
  306. FUSION_DBG_LEVEL_ERROR,
  307. "FusionpValidateHeap() was unable to get the current module name, code = %d\n",
  308. ::FusionpGetLastWin32Error());
  309. //
  310. // Blank the name, insert something relevant.
  311. //
  312. DllNamePointer = g_FusionModuleNameUnknown;
  313. }
  314. try
  315. {
  316. if (hHeap != NULL)
  317. {
  318. if (!::HeapLock(hHeap))
  319. goto ReportError;
  320. fHeapLocked = TRUE;
  321. }
  322. if (!::HeapLock(g_hDebugInfoHeap))
  323. goto ReportError;
  324. fDebugHeapLocked = TRUE;
  325. // Walk the debug heap looking for allocations...
  326. phe.lpData = NULL;
  327. while (::HeapWalk(g_hDebugInfoHeap, &phe))
  328. {
  329. PCSTR HeapString;
  330. SIZE_T cbToShow;
  331. if (!(phe.wFlags & PROCESS_HEAP_ENTRY_BUSY))
  332. continue;
  333. PFUSION_HEAP_ALLOCATION_TRACKER pTracker = (PFUSION_HEAP_ALLOCATION_TRACKER) phe.lpData;
  334. if (pTracker == NULL)
  335. continue;
  336. if (pTracker->Prefix == NULL)
  337. continue;
  338. // If we're checking only a particular heap, skip...
  339. if ((hHeap != NULL) && (pTracker->Heap != hHeap))
  340. continue;
  341. if (pTracker->PostAllocPoisonArea == NULL)
  342. continue;
  343. // The area should have been NULL if the count of bytes was nonzero...
  344. ASSERT(pTracker->PostAllocPoisonBytes != 0);
  345. PUCHAR PostAllocPoisonArea = pTracker->PostAllocPoisonArea;
  346. const UCHAR PostAllocPoisonChar = pTracker->PostAllocPoisonChar;
  347. const ULONG PostAllocPoisonBytes = pTracker->PostAllocPoisonBytes;
  348. for (i=0; i<PostAllocPoisonBytes; i++)
  349. {
  350. if (PostAllocPoisonArea[i] != PostAllocPoisonChar)
  351. break;
  352. }
  353. // The poison area looks good; skip...
  354. if (i == PostAllocPoisonBytes)
  355. continue;
  356. if (pTracker->Heap == g_hHeap)
  357. HeapString = "default heap";
  358. else
  359. HeapString = "custom heap";
  360. cbToShow = pTracker->RequestedSize;
  361. if (cbToShow > 64)
  362. cbToShow = 64;
  363. FusionpDbgPrintEx(
  364. FUSION_DBG_LEVEL_ERROR,
  365. "Wrote past end of %S allocation #%u (0x%lx) at %p \"%s\" (tracked by %p; allocated from heap %p - %s)\n"
  366. " Requested bytes/Allocated bytes: %Iu / %Iu (dumping %Iu bytes)\n",
  367. DllNamePointer, pTracker->SequenceNumber, pTracker->SequenceNumber, pTracker->Prefix, pTracker->Expression, pTracker, pTracker->Heap, HeapString,
  368. pTracker->RequestedSize, pTracker->AllocationSize, cbToShow);
  369. FusionpDbgPrintEx(
  370. FUSION_DBG_LEVEL_ERROR,
  371. " Allocated at line %u of %s\n",
  372. pTracker->Line, pTracker->FileName);
  373. FusionpDbgPrintBlob(
  374. FUSION_DBG_LEVEL_ERROR,
  375. pTracker->Prefix + 1,
  376. cbToShow,
  377. L"");
  378. FusionpDbgPrintBlob(
  379. FUSION_DBG_LEVEL_ERROR,
  380. pTracker->PostAllocPoisonArea,
  381. pTracker->PostAllocPoisonBytes,
  382. L"");
  383. }
  384. //
  385. // On invalid function, meaning HeapWalk is not defined, just exit.
  386. // Same for no-more-items, meaning the end of the list is nigh. We
  387. // make the assumption that none of the other functions in the loop
  388. // can fail with E_N_M_I or E_I_F - this may be a fallacy for later.
  389. //
  390. switch (::FusionpGetLastWin32Error())
  391. {
  392. case ERROR_INVALID_FUNCTION:
  393. case ERROR_NO_MORE_ITEMS:
  394. goto Exit;
  395. default:
  396. goto ReportError;
  397. }
  398. // Original code:
  399. //
  400. // if (::FusionpGetLastWin32Error() != ERROR_NO_MORE_ITEMS)
  401. // goto ReportError;
  402. //
  403. }
  404. catch(...)
  405. {
  406. FusionpDbgPrintEx(
  407. FUSION_DBG_LEVEL_ERROR,
  408. "%S: Exception while validating heap.\n", DllNamePointer);
  409. }
  410. goto Exit;
  411. ReportError:
  412. FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "%S: FusionpValidateHeap() encountered an error; FusionpGetLastWin32Error() = %d\n", DllNamePointer, ::FusionpGetLastWin32Error());
  413. Exit:
  414. if (fDebugHeapLocked)
  415. ::HeapUnlock(g_hDebugInfoHeap);
  416. if (fHeapLocked)
  417. ::HeapUnlock(hHeap);
  418. }
  419. PVOID
  420. FusionpDbgHeapAlloc(
  421. FUSION_HEAP_HANDLE hHeap,
  422. DWORD dwHeapAllocFlags,
  423. SIZE_T cb,
  424. PCSTR pszFile,
  425. INT nLine,
  426. PCSTR pszExpression,
  427. DWORD dwFusionFlags
  428. )
  429. {
  430. FN_TRACE();
  431. BOOL fSuccess = FALSE;
  432. BOOL fDebugHeapLocked = FALSE;
  433. SIZE_T cbAdditionalBytes = 0;
  434. #if FUSION_ENABLE_FROZEN_STACK
  435. // BOOL bShouldTraceStack = (g_FusionHeapTrackStackTraces && (::TlsGetValue(g_FusionHeapTrackingDisabledDepthTLSIndex) == 0));
  436. BOOL bShouldTraceStack = g_FusionHeapTrackStackTraces;
  437. FROZEN_STACK Prober = { 0 };
  438. #endif
  439. ASSERT(hHeap != NULL);
  440. LONG lAllocationSequenceNumber = ::InterlockedIncrement(&g_FusionHeapAllocationCount);
  441. if ((g_FusionHeapAllocationToBreakOn != 0) &&
  442. (lAllocationSequenceNumber == g_FusionHeapAllocationToBreakOn))
  443. {
  444. // Break in to the debugger, even if we're not in a checked build.
  445. FUSION_DEBUG_BREAK_IN_FREE_BUILD();
  446. }
  447. LONG lOperationSequenceNumber = ::InterlockedIncrement(&g_FusionHeapOperationCount);
  448. if ((g_FusionHeapCheckFrequency != 0) && ((lOperationSequenceNumber % g_FusionHeapCheckFrequency) == 0))
  449. {
  450. // Check the active heap allocations for correct post-block signatures...
  451. // ::FusionpValidateHeap(NULL);
  452. }
  453. PSTR psz = NULL;
  454. SIZE_T cbFile = (pszFile == NULL) ? 0 : ::strlen(pszFile) + 1;
  455. SIZE_T cbExpression = (pszExpression == NULL) ? 0 : ::strlen(pszExpression) + 1;
  456. PFUSION_HEAP_ALLOCATION_TRACKER pTracker = NULL;
  457. // Make a copy of the global variable so that if someone breaks in in the debugger
  458. // and changes it while we're in the middle of this code we don't die horribly.
  459. const ULONG cbPostAllocationBytes = g_FusionHeapPostAllocationBytes;
  460. const UCHAR chPostAllocationChar = g_FusionHeapPostAllocationChar;
  461. const SIZE_T cbToAllocate = (sizeof(FUSION_HEAP_PREFIX) + cb + cbPostAllocationBytes);
  462. const PFUSION_HEAP_PREFIX pPrefix = reinterpret_cast<PFUSION_HEAP_PREFIX>(::HeapAlloc(hHeap, dwHeapAllocFlags, cbToAllocate));
  463. if (pPrefix == NULL)
  464. {
  465. ::FusionpDbgPrintEx(
  466. FUSION_DBG_LEVEL_ERROR,
  467. "%s(%d): [SXS.DLL] Heap allocation failure allocating %Iu (really %Iu) bytes\n", pszFile, nLine, cb, cbToAllocate);
  468. ::SetLastError(ERROR_OUTOFMEMORY);
  469. return NULL;
  470. }
  471. // lock the debug info heap to allocate memory for pTracker
  472. if (!::HeapLock(g_hDebugInfoHeap))
  473. goto Exit;
  474. fDebugHeapLocked = TRUE;
  475. //
  476. // Are we tracing the stack? If so, then we need to allocate some extra bytes
  477. // on the end of this tracker to store the context.
  478. //
  479. #if FIXBEFORECHECKIN
  480. if (bShouldTraceStack)
  481. {
  482. BOOL bSuccess = ::FusionpFreezeStack(NULL, 0, &Prober);
  483. if (!bSuccess && (::FusionpGetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)) {
  484. cbAdditionalBytes = sizeof(FROZEN_STACK) + (sizeof(TRACECONTEXT) * Prober.ulMaxDepth);
  485. } else {
  486. cbAdditionalBytes = 0;
  487. bShouldTraceStack = FALSE;
  488. }
  489. }
  490. else
  491. #endif // FIXBEFORECHECKIN
  492. cbAdditionalBytes = 0;
  493. pTracker = reinterpret_cast<PFUSION_HEAP_ALLOCATION_TRACKER>(::HeapAlloc(
  494. g_hDebugInfoHeap,
  495. 0,
  496. sizeof(FUSION_HEAP_ALLOCATION_TRACKER)
  497. + FUSION_HEAP_ROUND_SIZE(cbFile)
  498. + FUSION_HEAP_ROUND_SIZE(cbExpression)
  499. + FUSION_HEAP_ROUND_SIZE(cbAdditionalBytes)));
  500. if (pTracker == NULL)
  501. {
  502. ::FusionpDbgPrintEx(
  503. FUSION_DBG_LEVEL_ERROR,
  504. "%s(%d): [SXS.DLL] Heap allocation failure allocating tracker for %lu bytes\n", pszFile, nLine, cb);
  505. ::HeapFree(hHeap, 0, pPrefix);
  506. ::SetLastError(ERROR_OUTOFMEMORY);
  507. goto Exit;
  508. }
  509. pPrefix->Tracker = pTracker;
  510. pTracker->Prefix = pPrefix;
  511. pTracker->Heap = hHeap;
  512. pTracker->SequenceNumber = lAllocationSequenceNumber;
  513. pTracker->PostAllocPoisonBytes = cbPostAllocationBytes;
  514. if (cbPostAllocationBytes != 0)
  515. {
  516. const PUCHAR pb = (UCHAR *) (((ULONG_PTR) (pPrefix + 1)) + cb);
  517. ULONG i;
  518. pTracker->PostAllocPoisonArea = (PUCHAR) pb;
  519. pTracker->PostAllocPoisonChar = chPostAllocationChar;
  520. for (i=0; i<cbPostAllocationBytes; i++)
  521. pb[i] = chPostAllocationChar;
  522. }
  523. else
  524. {
  525. pTracker->PostAllocPoisonArea = NULL;
  526. }
  527. psz = (PSTR) (pTracker + 1);
  528. if (cbFile != 0)
  529. {
  530. pTracker->FileName = psz;
  531. memcpy(psz, pszFile, cbFile);
  532. psz += FUSION_HEAP_ROUND_SIZE(cbFile);
  533. }
  534. else
  535. pTracker->FileName = NULL;
  536. if (cbExpression != 0)
  537. {
  538. pTracker->Expression = psz;
  539. memcpy(psz, pszExpression, cbExpression);
  540. psz += FUSION_HEAP_ROUND_SIZE(cbExpression);
  541. }
  542. else
  543. pTracker->Expression = NULL;
  544. #if FUSION_ENABLE_FROZEN_STACK
  545. //
  546. // Set up our stack tracker
  547. //
  548. if (bShouldTraceStack)
  549. {
  550. PFROZEN_STACK pStack = (PFROZEN_STACK)psz;
  551. pTracker->pvFrozenStack = pStack;
  552. pStack->ulDepth = 0;
  553. pStack->ulMaxDepth = Prober.ulMaxDepth;
  554. if (!::FusionpFreezeStack(0, pStack))
  555. pTracker->pvFrozenStack = NULL;
  556. }
  557. //
  558. // Otherwise, no stack for you.
  559. //
  560. else
  561. {
  562. pTracker->pvFrozenStack = NULL;
  563. }
  564. #endif
  565. pTracker->Line = nLine;
  566. pTracker->Flags = dwFusionFlags;
  567. pTracker->RequestedSize = cb;
  568. pTracker->AllocationSize = cb + sizeof(FUSION_HEAP_PREFIX);
  569. #if 0
  570. if (::TlsGetValue(g_FusionHeapTrackingDisabledDepthTLSIndex) != 0)
  571. pTracker->Flags |= FUSION_HEAP_DO_NOT_REPORT_LEAKED_ALLOCATION;
  572. #endif
  573. // poison the allocation...
  574. memset((pPrefix + 1), g_FusionHeapAllocationPoisonChar, cb);
  575. if ((g_FusionHeapAllocationPtrToBreakOn != 0) &&
  576. ((pPrefix + 1) == g_FusionHeapAllocationPtrToBreakOn))
  577. {
  578. // Break in to the debugger, even if we're not in a checked build.
  579. FUSION_DEBUG_BREAK_IN_FREE_BUILD();
  580. }
  581. fSuccess = TRUE;
  582. Exit:
  583. if (fDebugHeapLocked){
  584. DWORD dwLastError = ::FusionpGetLastWin32Error();
  585. ::HeapUnlock(g_hDebugInfoHeap);
  586. ::SetLastError(dwLastError);
  587. }
  588. if (fSuccess)
  589. return (PVOID) (pPrefix + 1);
  590. else
  591. return NULL;
  592. }
  593. BOOL
  594. FusionpDbgHeapFree(
  595. FUSION_HEAP_HANDLE hHeap,
  596. DWORD dwHeapFreeFlags,
  597. PVOID pv
  598. )
  599. {
  600. FN_TRACE();
  601. PFUSION_HEAP_ALLOCATION_TRACKER pTracker;
  602. BOOL fResult = FALSE;
  603. ASSERT(hHeap != NULL);
  604. if (pv == NULL)
  605. return FALSE;
  606. if ((g_FusionHeapDeallocationPtrToBreakOn != NULL) &&
  607. (pv == g_FusionHeapDeallocationPtrToBreakOn))
  608. {
  609. // Break in to the debugger, even if we're not in a checked build.
  610. FUSION_DEBUG_BREAK_IN_FREE_BUILD();
  611. }
  612. // Let's see if its one of our funky ones...
  613. PFUSION_HEAP_PREFIX p = (PFUSION_HEAP_PREFIX) (((ULONG_PTR) pv) - sizeof(FUSION_HEAP_PREFIX));
  614. if (!::HeapValidate(hHeap, 0, p)) {
  615. // HeapValidate failed. Fatal. Just leak the memory for now...
  616. // ASSERT(0);
  617. return FALSE;
  618. }
  619. if (!::HeapValidate(g_hDebugInfoHeap, 0, p->Tracker)) {
  620. // HeapValidate failed. Fatal. Just leak the memory for now...
  621. // ASSERT(0);
  622. return FALSE;
  623. }
  624. pTracker = p->Tracker;
  625. ASSERT(pTracker->Heap == hHeap);
  626. p->Tracker->Prefix = NULL;
  627. // poison the deallocation...
  628. memset(p, g_FusionHeapDeallocationPoisonChar, pTracker->AllocationSize);
  629. ::HeapFree(g_hDebugInfoHeap, 0, pTracker);
  630. fResult = ::HeapFree(hHeap, dwHeapFreeFlags, p);
  631. return fResult;
  632. }
  633. VOID
  634. FusionpDeallocateTracker(
  635. PFUSION_HEAP_PREFIX p
  636. )
  637. {
  638. CSxsPreserveLastError ple;
  639. PFUSION_HEAP_ALLOCATION_TRACKER pTracker = p->Tracker;
  640. ::HeapFree(g_hDebugInfoHeap, 0, pTracker);
  641. p->Tracker = NULL;
  642. ple.Restore();
  643. }
  644. VOID *
  645. FusionpGetFakeVTbl()
  646. {
  647. VOID *pvHeap;
  648. // Always allocate the fake vtbl from the process heap so that it survives us nomatter what.
  649. pvHeap = HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_VTBL_ENTRIES * sizeof(void *));
  650. return pvHeap;
  651. }
  652. VOID
  653. FusionpDontTrackBlk(
  654. VOID *pv
  655. )
  656. {
  657. PFUSION_HEAP_PREFIX p;
  658. p = (PFUSION_HEAP_PREFIX) (((ULONG_PTR)pv) - sizeof(FUSION_HEAP_PREFIX));
  659. FusionpDeallocateTracker(p);
  660. p->Tracker = NULL;
  661. }
  662. #endif // FUSION_DEBUG_HEAP