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.

967 lines
29 KiB

  1. //=--------------------------------------------------------------------------=
  2. // Macros.Cpp
  3. //=--------------------------------------------------------------------------=
  4. // Copyright 1997 Microsoft Corporation. All Rights Reserved.
  5. //
  6. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  7. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  8. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  9. // PARTICULAR PURPOSE.
  10. //=--------------------------------------------------------------------------=
  11. // Handy macros like the ones we use in the VB code base.
  12. //=--------------------------------------------------------------------------=
  13. #include "pch.h"
  14. #ifdef DEBUG
  15. #include <winuser.h>
  16. // for ASSERT and FAIL
  17. //
  18. SZTHISFILE
  19. //=--------------------------------------------------------------------------=
  20. // Debug control switches
  21. //=--------------------------------------------------------------------------=
  22. DEFINE_SWITCH(fTraceCtlAllocs); // Trace all Heap allocations and frees
  23. // fOutputFile should also be on with this switch
  24. DEFINE_SWITCH(fOutputFile); // Logs all debug info in file:
  25. // %CurrentDir%\ctldebug.log
  26. DEFINE_SWITCH(fNoLeakAsserts); // No Heap memory leak asserts are displayed
  27. // when turned on.
  28. //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  29. // !DEBUGGING HEAP MEMORY LEAKS!
  30. // To debug a leak you need to figure out where and when the allocation was made.
  31. // The top of the assert dialog will give you the OCX/DLL causing the leak.
  32. // Goto Project/Build...Settings.
  33. // On the Debug tab, select "additional DLLs"
  34. // Locate and select the OCX/DLL causing the leak.
  35. // Put a breakpoint on the noted line below.
  36. // Goto Edit...Breakpoints.
  37. // Select the new breakpoint.
  38. // Press 'Condition'
  39. // In the 'Enter number of times to skip before breaking' put the value of nAlloc-1.
  40. // (if the leak was nAlloc=267 then you want to skip the breapoint 266 times, enter 266)
  41. //
  42. // WARNING: Each control (OCX/DLL) will have its own instance of the framewrk, and thus
  43. // its own instance of the memory leak implementaion. Adding a breakpoint
  44. // anywhere in the framewrk will actually add multiple breakpoints - one for
  45. // each control.
  46. // Go back to Edit...Breakpoints.
  47. // Deselect or remove the breakpoints for the OCX's/DLL's not causing leaks
  48. //
  49. // Run your scenario.
  50. // When you hit this breakpoint verify that pvAddress and nByteCount are correct and then
  51. // look down the callstack to see where the allocation was made.
  52. //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  53. void PutBreakPointHere(void * pvAddress, ULONG nByteCount, ULONG nAlloc, char * szFile, ULONG uLine)
  54. {
  55. pvAddress=pvAddress; nAlloc=nAlloc; nByteCount=nByteCount;
  56. szFile=szFile;
  57. uLine=uLine;
  58. HINSTANCE hInstance = g_hInstance; // hInstance of the OCX/DLL calling this breakpoint
  59. int PutBreakPointOnThisLine = 1; // <--- breakpoint here.
  60. } // PutBreakPointHere
  61. //=--------------------------------------------------------------------------=
  62. //
  63. // Debug Heap Memory Leak implementations
  64. //
  65. class CAddressNode
  66. {
  67. public:
  68. void * m_pv; // Address of memory block allocated
  69. ULONG m_cb; // Size of allocation in BYTES
  70. ULONG m_cAlloc; // Allocation pass count.
  71. LPSZ m_szFile; // Source file where the allocation was made
  72. ULONG m_uLine; // Source line number where the allocation was made
  73. CAddressNode * m_pnNext; // Nodes are stored in a linked list
  74. void * operator new(size_t cb);
  75. void operator delete(void * pv);
  76. // We maintain a freelist to speed up allocation of AddressNodes.
  77. static CAddressNode * m_pnFreeList;
  78. };
  79. CAddressNode * m_rInstTable[NUM_INST_TABLE_ENTRIES]; // Hashing table of all instances of
  80. // mem alloc
  81. CAddressNode * m_pnEnumNode; // Next node for enumerator to return
  82. UINT m_uEnumIndex; // Current index into m_rInstTable for enumerator
  83. static ULONG m_cGlobalPassCount; // Pass count of allocation. Common to all heaps
  84. ULONG m_cCurNumAllocs; // Current number of allocations
  85. ULONG m_cNumAllocs; // Total number of allocations ever done.
  86. ULONG m_cCurNumBytesAllocated; // Current number of bytes allocated.
  87. ULONG m_cNumBytesAllocated; // Total bytes allocated.
  88. ULONG m_HWAllocs; // High water allocations.
  89. ULONG m_HWBytes; // High water bytes.
  90. static ULONG m_OverallCurAlloc; // These are overall statistics to since we
  91. static ULONG m_OverallCurBytes; // wouldn't mind the overall high water.
  92. static ULONG m_OverallHWAlloc;
  93. static ULONG m_OverallHWBytes;
  94. // Forward declarations
  95. VOID AddInst(VOID * pv, DWORD dwBytes, LPSZ szFile, UINT uLine);
  96. VOID DebugInst(ULONG cb);
  97. VOID AnalyzeInst(LPVOID pv);
  98. VOID DumpInst(CAddressNode * pn, LPTSTR lpTypeofAlloc);
  99. LPSTR DumpInstTable(LPSTR lpLeak);
  100. VOID DeleteInst(LPVOID pv);
  101. VOID VerifyHeaderTrailer(CAddressNode * pn);
  102. VOID CheckForLeaks(VOID);
  103. VOID HeapCheck(VOID);
  104. VOID OutputToFile(LPSTR szOutput);
  105. CAddressNode * FindInst(LPVOID pv);
  106. CAddressNode * EnumReset();
  107. CAddressNode * EnumNext();
  108. // Initialize a header and trailer for all memory to be allocated.
  109. // Use 8 bytes so it is also compatible with RISC machines.
  110. char * g_szHeader = "HEADHEAD";
  111. char * g_szTrailer = "END!END!";
  112. #define HEADERSIZE 8 // # of bytes of block header
  113. // 0 ==> no block header signature
  114. #define TRAILERSIZE 8 // # of bytes of block trailer
  115. // 0 ==> no block trailer signature
  116. //=--------------------------------------------------------------------------=
  117. // CtlHeapAllocImpl:
  118. // Debug wrapper for HeapAlloc to track memory leaks:
  119. //=--------------------------------------------------------------------------=
  120. LPVOID CtlHeapAllocImpl(
  121. HANDLE g_hHeap,
  122. DWORD dwFlags,
  123. DWORD dwBytesRequested,
  124. LPSTR lpszFile,
  125. UINT line
  126. )
  127. {
  128. LPVOID lpvRet;
  129. DWORD dwBytes;
  130. LPTSTR lpTypeofAlloc = "HeapAlloc ";
  131. // If someone tries to allocate memory before PROCCESS_ATTATCH (such as in a
  132. // global constructor), do not track it because neither our heap nor our
  133. // hInstance have been initialized yet.
  134. //
  135. if (!g_fInitCrit)
  136. {
  137. g_flagConstructorAlloc = TRUE;
  138. return HeapAlloc(g_hHeap, dwFlags, dwBytesRequested);
  139. }
  140. // Increase size to make space for header and trailer signatures
  141. dwBytes = dwBytesRequested + HEADERSIZE + TRAILERSIZE;
  142. // Allocate memory
  143. lpvRet = HeapAlloc(g_hHeap, dwFlags, dwBytes);
  144. if (lpvRet)
  145. {
  146. // Initialize memory (non-zero)
  147. if (!(dwFlags & HEAP_ZERO_MEMORY))
  148. memset(lpvRet, 0xAF, dwBytes);
  149. // Add instance to hash table
  150. AddInst(lpvRet, dwBytesRequested, lpszFile, line);
  151. // Trace allocations if switch is on
  152. if (FSWITCH(fTraceCtlAllocs))
  153. {
  154. CAddressNode *pn = FindInst(lpvRet);
  155. DumpInst(pn, lpTypeofAlloc);
  156. }
  157. // Advance pointer past header signature.
  158. lpvRet = (LPVOID) ((char *)lpvRet + HEADERSIZE);
  159. }
  160. return lpvRet;
  161. } // CtlHeapAllocImpl
  162. //=--------------------------------------------------------------------------=
  163. // CtlHeapReAllocImpl:
  164. //
  165. //=--------------------------------------------------------------------------=
  166. LPVOID CtlHeapReAllocImpl(
  167. HANDLE g_hHeap,
  168. DWORD dwFlags,
  169. LPVOID lpvMem,
  170. DWORD dwBytesRequested,
  171. LPSTR lpszFile,
  172. UINT line
  173. )
  174. {
  175. LPVOID lpvRet;
  176. CAddressNode * pn;
  177. int byte;
  178. DWORD cbOffset, dwBytes;
  179. LPTSTR lpTypeofAlloc = "HeapReAlloc ";
  180. // Move pointer to beginning of header
  181. lpvMem = (LPVOID)((char *)lpvMem - HEADERSIZE);
  182. // Find instance in hash table
  183. pn = FindInst(lpvMem);
  184. if (!pn)
  185. {
  186. FAIL("CtlHeapReAllocImpl - could not find lpvMem in the instance table. See debug \
  187. output for more info.");
  188. AnalyzeInst(lpvMem);
  189. return 0;
  190. }
  191. // Increase size to make space for header and trailer signatures
  192. dwBytes = dwBytesRequested + HEADERSIZE + TRAILERSIZE;
  193. lpvRet = HeapReAlloc(g_hHeap, dwFlags, lpvMem, dwBytes);
  194. if (lpvRet)
  195. {
  196. // If the reallocation grew, we must intialize new memory
  197. if (dwBytesRequested > pn->m_cb)
  198. {
  199. if (dwFlags & HEAP_ZERO_MEMORY)
  200. byte = 0x0;
  201. else
  202. byte = 0xAF;
  203. // Get the byte offset of trailer in the old allocation
  204. cbOffset = pn->m_cb + HEADERSIZE;
  205. memset((char *)lpvRet + cbOffset, byte, dwBytes - cbOffset);
  206. }
  207. // Update hash table
  208. EnterCriticalSection(&g_csHeap);
  209. DeleteInst(lpvMem);
  210. AddInst(lpvRet, dwBytesRequested, lpszFile, line);
  211. LeaveCriticalSection(&g_csHeap);
  212. // Trace Allocations if switch is on
  213. if (FSWITCH(fTraceCtlAllocs))
  214. {
  215. CAddressNode *pn = FindInst(lpvRet);
  216. DumpInst(pn, lpTypeofAlloc);
  217. }
  218. // Advance pointer past header signature.
  219. lpvRet = (LPVOID)((char *)lpvRet + HEADERSIZE);
  220. }
  221. return lpvRet;
  222. } // CtlHeapReAllocImpl
  223. //=--------------------------------------------------------------------------=
  224. // CtlHeapFreeImpl:
  225. // Debug wrapper for HeapFree
  226. //=--------------------------------------------------------------------------=
  227. BOOL CtlHeapFreeImpl(
  228. HANDLE g_hHeap,
  229. DWORD dwFlags,
  230. LPVOID lpvMem
  231. )
  232. {
  233. BOOL fRet = FALSE;
  234. CAddressNode * pn;
  235. LPTSTR lpTypeofAlloc = "HeapFree ";
  236. // If someone tries to de-allocate memory after PROCCESS_DETATCH (such as in a
  237. // global destructor), Re-initialize critical section and free memory.
  238. //
  239. if (!g_fInitCrit)
  240. InitializeCriticalSection(&g_csHeap);
  241. // Move pointer to beginning of header
  242. lpvMem = (LPVOID) ((char *)lpvMem - HEADERSIZE);
  243. // Find the instance in the hash table
  244. pn = FindInst(lpvMem);
  245. if (pn)
  246. {
  247. // Verify the memory has not been overwritten
  248. VerifyHeaderTrailer(pn);
  249. // Trace allocations if switch is on
  250. if (FSWITCH(fTraceCtlAllocs))
  251. {
  252. CAddressNode *pn = FindInst(lpvMem);
  253. DumpInst(pn, lpTypeofAlloc);
  254. }
  255. // Free memory -- NOTE: WinNT will set free memory to 0xEEFEEEFE which is "����"
  256. fRet = HeapFree(g_hHeap, 0, lpvMem);
  257. if (!fRet)
  258. FAIL("CtlHeapFreeImpl - lpvMem was found to be allocated in the heap passed in \
  259. but HeapFree() failed. Maybe the pointer was already freed.");
  260. }
  261. // Remove instance from hash table
  262. if (fRet)
  263. DeleteInst(lpvMem);
  264. // Make sure this memory wasn't allocated in a global constructor
  265. else if (!g_flagConstructorAlloc)
  266. {
  267. FAIL("CtlHeapFreeImpl - could not find lpvMem in the instance table. See debug \
  268. output for more info.");
  269. AnalyzeInst(lpvMem);
  270. }
  271. else
  272. fRet = TRUE;
  273. // If called after PROCESS_DETATCH delete critical section and Check for leaks again
  274. // NOTE: Only the LAST Assert will have the exact leak information. All previous
  275. // Asserts will not take into account a HeapFree which occurs after PROCESS_DETACH.
  276. // This only occurs in controls using global static destructors.
  277. if (!g_fInitCrit)
  278. {
  279. CheckForLeaks();
  280. DeleteCriticalSection(&g_csHeap);
  281. }
  282. return fRet;
  283. } // CtlHeapFreeImpl
  284. //=--------------------------------------------------------------------------=
  285. // CheckForLeaks:
  286. // We are calling PROCESS_DETATCH so check if hash table is empty. If not
  287. // dump info on memory that has been leaked.
  288. //=--------------------------------------------------------------------------=
  289. VOID CheckForLeaks(VOID)
  290. {
  291. CAddressNode * pn = EnumReset();
  292. BOOL IsEmpty = (pn == NULL); // FALSE if there are leaks
  293. // First check for memory trashing of any leaked memory
  294. HeapCheck();
  295. if (!IsEmpty)
  296. {
  297. // First find out which OCX/DLL is leaking
  298. TCHAR lpCtlName[128];
  299. DWORD nSize = 128;
  300. DWORD fValidPath;
  301. fValidPath = GetModuleFileName(g_hInstance, (LPTSTR)lpCtlName, nSize);
  302. LPSTR lpLeaks;
  303. // Allocate some memory to hold the data but use GlobalAlloc since we
  304. // don't want to use the vb memory stuff since it will muck things up.
  305. lpLeaks = (LPSTR)GlobalLock(GlobalAlloc(GMEM_MOVEABLE,128));
  306. lstrcpy(lpLeaks, lpCtlName);
  307. lstrcat(lpLeaks, " has leaked memory.\nUse PutBreakPointHere() in macros.cpp to debug.\r\n");
  308. // Collect all leak info
  309. lpLeaks = DumpInstTable(lpLeaks);
  310. // Dump output to file if "fOutputFile" switch is on
  311. if (FSWITCH(fOutputFile))
  312. OutputToFile(lpLeaks);
  313. // Dump output to an assert as long as "fNoLeakAsserts" is off
  314. else if (!FSWITCH(fNoLeakAsserts))
  315. {
  316. // Truncate output so it fits into DisplayAssert (512 Max)
  317. if (lstrlen(lpLeaks) > 500)
  318. {
  319. lstrcpyn(lpLeaks, lpLeaks, 500);
  320. lstrcat(lpLeaks, "\nMore...");
  321. }
  322. DisplayAssert(lpLeaks, "FAIL", NULL, 0);
  323. }
  324. // Release memory used to store leak info
  325. GlobalUnlock((HGLOBAL)GlobalHandle(lpLeaks)),
  326. (BOOL)GlobalFree((HGLOBAL)GlobalHandle(lpLeaks));
  327. }
  328. return;
  329. } // CheckForLeaks
  330. //=--------------------------------------------------------------------------=
  331. // AddInst:
  332. // A heap allocation occured so here we add the allocation information to
  333. // the instance table. To debug memory leaks where you need to use pass
  334. // counts, set a passcount breakpoint in this function using the passcount
  335. // value given in the debug output.
  336. //=--------------------------------------------------------------------------=
  337. VOID AddInst(
  338. VOID * pv,
  339. DWORD dwBytes,
  340. LPSZ szFile,
  341. UINT uLine
  342. )
  343. {
  344. UINT uHash;
  345. CAddressNode * pn = new CAddressNode();
  346. ASSERT(pn,"");
  347. EnterCriticalSection(&g_csHeap);
  348. m_cGlobalPassCount++;
  349. pn->m_pv = pv; // Memory address of allocation
  350. pn->m_cb = dwBytes; // Bytes requested to be allocated
  351. pn->m_cAlloc = m_cGlobalPassCount; // This is the pass count value in debug output.
  352. pn->m_szFile = szFile; // Source file the allocation call was made
  353. pn->m_uLine = uLine; // Line number in source file.
  354. PutBreakPointHere(pv, dwBytes, m_cGlobalPassCount, szFile, uLine);
  355. // Add instance to proper position in table
  356. uHash = HashInst(pv);
  357. pn->m_pnNext = m_rInstTable[uHash];
  358. m_rInstTable[uHash] = pn;
  359. // Copy header and trailer signatures.
  360. memcpy((char *)pv, g_szHeader, HEADERSIZE);
  361. memcpy((char *)pv + HEADERSIZE + dwBytes, g_szTrailer, TRAILERSIZE);
  362. LeaveCriticalSection(&g_csHeap);
  363. // Track extra memory debug info
  364. DebugInst( dwBytes );
  365. } // AddInst
  366. //=--------------------------------------------------------------------------=
  367. // DebugInst:
  368. // Updates the memory debug information
  369. //=--------------------------------------------------------------------------=
  370. VOID DebugInst(
  371. ULONG cb
  372. )
  373. {
  374. EnterCriticalSection(&g_csHeap);
  375. ++m_cCurNumAllocs;
  376. ++m_cNumAllocs;
  377. ++m_OverallCurAlloc;
  378. m_cCurNumBytesAllocated+=cb;
  379. m_cNumBytesAllocated+=cb;
  380. m_OverallCurBytes+=cb;
  381. m_HWAllocs = (m_HWAllocs < m_cCurNumAllocs) ? m_cCurNumAllocs : m_HWAllocs;
  382. m_HWBytes = (m_HWBytes < m_cCurNumBytesAllocated) ? m_cCurNumBytesAllocated : m_HWBytes;
  383. m_OverallHWAlloc = (m_OverallHWAlloc < m_OverallCurAlloc)
  384. ? m_OverallCurAlloc : m_OverallHWAlloc;
  385. m_OverallHWBytes = (m_OverallHWBytes < m_OverallCurBytes)
  386. ? m_OverallCurBytes : m_OverallHWBytes;
  387. LeaveCriticalSection(&g_csHeap);
  388. } // DebugInst
  389. //=--------------------------------------------------------------------------=
  390. // FindInst:
  391. // Give a pointer to an allocation, return a pointer to the debug
  392. // allocation information.
  393. //=--------------------------------------------------------------------------=
  394. CAddressNode * FindInst(
  395. LPVOID pv
  396. )
  397. {
  398. CAddressNode * pn;
  399. EnterCriticalSection(&g_csHeap);
  400. pn = m_rInstTable[HashInst(pv)];
  401. while (pn && pn->m_pv != pv)
  402. pn = pn->m_pnNext;
  403. LeaveCriticalSection(&g_csHeap);
  404. return pn;
  405. } // FindInst
  406. //=--------------------------------------------------------------------------=
  407. // AnalyzeInst:
  408. // Given a pointer try determine if it is a valid Read and Write pointer
  409. // and if it was allocated.
  410. //=--------------------------------------------------------------------------=
  411. VOID AnalyzeInst(
  412. LPVOID pv
  413. )
  414. {
  415. LPTSTR lpTypeofAlloc = "Bad lpvMem ";
  416. CAddressNode * pn = NULL;
  417. // Either we have a bad pointer or the pointer does not point to any
  418. // known heap allocations. Here we check if it points to readable or
  419. // writable memory.
  420. BOOL fBadPointer = (IsBadReadPtr(pv, 4) || IsBadWritePtr(pv, 4));
  421. // Report what we know about the memory address
  422. if (fBadPointer)
  423. DebugPrintf("AnalyzeInst found that pointer pv=0x%lX is not writable\n\r" \
  424. "or readable. The allocation is either outside the addressable range\n\r" \
  425. "for this operating system or the allocation was already freed.\n\r",pv);
  426. else
  427. DebugPrintf("AnalyzeInst found that pointer pv=0x%lX is readable and writable,\n\r" \
  428. "so the allocation was made without being added to instance table\n\r" \
  429. "(prior to PROCESS_ATTATCH), or the memory was already freed.\n\r",pv);
  430. } // AnanlyzeInst
  431. //=--------------------------------------------------------------------------=
  432. // DumpInst:
  433. // Dump instance information out to an assert window.
  434. //=--------------------------------------------------------------------------=
  435. VOID DumpInst(
  436. CAddressNode * pn,
  437. LPTSTR lpTypeofAlloc
  438. )
  439. {
  440. char szOutput[255];
  441. // Format output
  442. wsprintf(szOutput, "%s: %s(%u) Address=0x%lx nAlloc=%ld Bytes=%ld\r\n", lpTypeofAlloc,
  443. pn->m_szFile, pn->m_uLine, (ULONG)pn->m_pv, (ULONG)pn->m_cAlloc, (ULONG)pn->m_cb);
  444. // Dump output to file if switch is turned on
  445. if (FSWITCH(fOutputFile))
  446. OutputToFile(szOutput);
  447. else if (FSWITCH(fNoLeakAsserts))
  448. DebugPrintf(szOutput);
  449. // Else display output in assert
  450. else
  451. DisplayAssert(szOutput, "FAIL", _szThisFile, __LINE__);;
  452. } // DumpInst
  453. //=--------------------------------------------------------------------------=
  454. // DumpInstTable:
  455. // Memory leak has been detected so dump the entire instance table.
  456. //=--------------------------------------------------------------------------=
  457. LPSTR DumpInstTable(
  458. LPSTR lpLeak
  459. )
  460. {
  461. CAddressNode * pn = EnumReset();
  462. DWORD sizeoflpLeak;
  463. LPSTR lpTemp;
  464. EnterCriticalSection(&g_csHeap);
  465. DebugPrintf(lpLeak);
  466. while (pn)
  467. {
  468. // Format the leak info
  469. char szOut[250] = {NULL};
  470. wsprintf(szOut, "\t%s(%u) Address=0x%lx nAlloc=%ld Bytes=%ld\r\n", pn->m_szFile,
  471. pn->m_uLine, (ULONG)pn->m_pv, (ULONG)pn->m_cAlloc, (ULONG)pn->m_cb);
  472. DebugPrintf(szOut);
  473. // Convert lpLeak to a handle and get its current allocation size
  474. sizeoflpLeak = GlobalSize(GlobalHandle(lpLeak));
  475. // Reallocate memory to make space for more leak info
  476. lpTemp = (LPSTR) (GlobalUnlock((HGLOBAL)GlobalHandle(lpLeak)),
  477. GlobalLock(GlobalReAlloc((HGLOBAL)GlobalHandle(lpLeak),
  478. sizeoflpLeak + lstrlen(szOut) + 1, GMEM_MOVEABLE)));
  479. // Add new leak info to lpLeak
  480. if(lpTemp)
  481. {
  482. lpLeak = lpTemp;
  483. lstrcat(lpLeak, szOut);
  484. }
  485. // Get the next leak
  486. pn = EnumNext();
  487. }
  488. LeaveCriticalSection(&g_csHeap);
  489. return lpLeak;
  490. } // DumpInstTable
  491. //=--------------------------------------------------------------------------=
  492. // DeleteInst:
  493. // A heap allocation got free or was reallocated so remove the
  494. // information from the instance table and check for memory trashing.
  495. //=--------------------------------------------------------------------------=
  496. VOID DeleteInst(
  497. LPVOID pv
  498. )
  499. {
  500. CAddressNode ** ppn, * pnDead;
  501. ppn = &m_rInstTable[HashInst(pv)];
  502. EnterCriticalSection(&g_csHeap);
  503. // Find allocation instance
  504. while (*ppn != NULL)
  505. {
  506. if ((*ppn)->m_pv == pv)
  507. {
  508. pnDead = *ppn;
  509. *ppn = (*ppn)->m_pnNext;
  510. // Correct memory debug info
  511. --m_cCurNumAllocs;
  512. m_cCurNumBytesAllocated -= pnDead->m_cb;
  513. --m_OverallCurAlloc;
  514. m_OverallCurBytes -= pnDead->m_cb;
  515. // Remove instance
  516. delete pnDead;
  517. LeaveCriticalSection(&g_csHeap);
  518. return;
  519. } // if
  520. ppn = &((*ppn)->m_pnNext);
  521. } // while
  522. FAIL("DeleteInst - memory instance not found");
  523. } // DeleteInst
  524. //=--------------------------------------------------------------------------=
  525. // VerifyHeaderTrailer:
  526. // Inspect allocation for header and trailer signature overwrites
  527. //=--------------------------------------------------------------------------=
  528. VOID VerifyHeaderTrailer(
  529. CAddressNode * pn
  530. )
  531. {
  532. LPTSTR lpTypeofAlloc = "Memory trashed ";
  533. //Verify the header
  534. if (memcmp((char *)pn->m_pv, g_szHeader, HEADERSIZE) != 0)
  535. {
  536. FAIL("Heap block header has been trashed.");
  537. DebugPrintf("Heap block header trashed.");
  538. DebugPrintf("\r\n");
  539. DumpInst(pn, lpTypeofAlloc);
  540. }
  541. //Verify the trailer
  542. if (memcmp((char *)pn->m_pv + pn->m_cb + HEADERSIZE, g_szTrailer, TRAILERSIZE) != 0)
  543. {
  544. FAIL("Heap block trailer has been trashed.");
  545. DebugPrintf("Heap block trailer trashed.");
  546. DebugPrintf("\r\n");
  547. DumpInst(pn, lpTypeofAlloc);
  548. }
  549. return;
  550. } // VerifyHeaderTrailer
  551. //=--------------------------------------------------------------------------=
  552. // HeapCheck:
  553. // Inspect all of the allocations for header and trailer signature
  554. // overwrites.
  555. //=--------------------------------------------------------------------------=
  556. VOID HeapCheck(VOID)
  557. {
  558. ASSERT(HeapValidate(g_hHeap, 0, NULL) != 0, "OS Says heap is corrupt");
  559. CAddressNode * pn = EnumReset();
  560. while (pn)
  561. {
  562. VerifyHeaderTrailer(pn);
  563. pn = EnumNext();
  564. }
  565. return;
  566. } // HeapCheck
  567. //=-------------------------------------------------------------------------=
  568. // For use with CAddresssNode
  569. //=-------------------------------------------------------------------------=
  570. #define MEM_cAddressNodes 128 // Nodes are block allocated
  571. #define UNUSED(var) ((var) = (var)) // Used to avoid warnings
  572. // The free list is common
  573. CAddressNode * CAddressNode::m_pnFreeList = NULL;
  574. //=--------------------------------------------------------------------------=
  575. // CAddressNode::operator new:
  576. // Returns a pointer to an allocated address node. If there are none on
  577. // the free list then we allocate a block of address nodes, chain them
  578. // together and add them to the free list. These nodes are never
  579. // actually freed so it is ok to allocate them in blocks.
  580. //=--------------------------------------------------------------------------=
  581. void * CAddressNode::operator new(
  582. size_t cb
  583. )
  584. {
  585. CAddressNode * pn;
  586. UNUSED(cb);
  587. EnterCriticalSection(&g_csHeap); // needed for static m_pnFreeList
  588. if (m_pnFreeList == NULL)
  589. {
  590. UINT cbSize = sizeof(CAddressNode) * MEM_cAddressNodes; //allocate a block
  591. pn = (CAddressNode *) HeapAlloc(g_hHeap, 0, cbSize);
  592. //chain all except the first node together. the first node
  593. //is the one returned
  594. for (int i = 1; i < MEM_cAddressNodes - 1; ++i)
  595. pn[i].m_pnNext = &pn[i+1];
  596. pn[MEM_cAddressNodes - 1].m_pnNext = NULL;
  597. m_pnFreeList = &pn[1];
  598. }
  599. else
  600. {
  601. pn = m_pnFreeList;
  602. m_pnFreeList = pn->m_pnNext;
  603. }
  604. LeaveCriticalSection(&g_csHeap);
  605. return pn;
  606. } // CAddressNode::operator new
  607. //=--------------------------------------------------------------------------=
  608. // CAddressNode::operator delete
  609. // Return the address node to the free list. We never actually free
  610. // the node since nodes are allocated in blocks.
  611. //=--------------------------------------------------------------------------=
  612. void CAddressNode::operator delete(
  613. void * pv
  614. )
  615. {
  616. EnterCriticalSection(&g_csHeap); // needed for static m_pnFreeList
  617. CAddressNode * pn = (CAddressNode *) pv;
  618. pn->m_pnNext = m_pnFreeList;
  619. m_pnFreeList = pn;
  620. LeaveCriticalSection(&g_csHeap);
  621. } // CAddressNode::operator delete
  622. //=--------------------------------------------------------------------------=
  623. // EnumReset:
  624. // Reset the enumerator and return the first node. NULL if empty.
  625. //=--------------------------------------------------------------------------=
  626. CAddressNode * EnumReset()
  627. {
  628. m_pnEnumNode = NULL;
  629. for (m_uEnumIndex = 0; m_uEnumIndex < NUM_INST_TABLE_ENTRIES; ++m_uEnumIndex)
  630. {
  631. m_pnEnumNode = m_rInstTable[m_uEnumIndex];
  632. if (m_pnEnumNode != NULL)
  633. return m_pnEnumNode;
  634. }
  635. return NULL; //Instance table is empty
  636. } // EnumReset
  637. //=--------------------------------------------------------------------------=
  638. // EnumNext:
  639. // Return the next node in the enumeration. m_pnEnumNode points to the last
  640. // node returned. It is NULL if no more left.
  641. //=--------------------------------------------------------------------------=
  642. CAddressNode * EnumNext()
  643. {
  644. ASSERT(m_uEnumIndex <= NUM_INST_TABLE_ENTRIES, "");
  645. if (m_pnEnumNode == NULL)
  646. return NULL; //end of enumeration
  647. m_pnEnumNode = m_pnEnumNode->m_pnNext;
  648. if (m_pnEnumNode == NULL)
  649. {
  650. //at end of this linked list so search for next list
  651. m_uEnumIndex++;
  652. while (m_uEnumIndex < NUM_INST_TABLE_ENTRIES && m_rInstTable[m_uEnumIndex] == NULL)
  653. m_uEnumIndex++;
  654. if (m_uEnumIndex < NUM_INST_TABLE_ENTRIES)
  655. m_pnEnumNode = m_rInstTable[m_uEnumIndex];
  656. }
  657. return m_pnEnumNode;
  658. } // EnumNext
  659. //=---------------------------------------------------------------------------=
  660. // OutputToFile:
  661. // Dumps output to file "ctldebug.log"
  662. //=---------------------------------------------------------------------------=
  663. VOID OutputToFile
  664. (
  665. LPSTR szOutput
  666. )
  667. {
  668. DWORD nPathSize;
  669. DWORD nDirPathSize = 128;
  670. TCHAR lpFilePath[128];
  671. LPCTSTR lpFileName = "\\CtlDebug.log";
  672. HANDLE hFile;
  673. BOOL fWritten, fClosed = FALSE;
  674. DWORD nBytesWritten;
  675. // Create path to output file
  676. nPathSize = GetCurrentDirectory(nDirPathSize, (LPTSTR)lpFilePath);
  677. if (nPathSize == 0)
  678. FAIL("Unable to get current directory...");
  679. lstrcat(lpFilePath, lpFileName);
  680. // Open and write to file
  681. hFile = CreateFile((LPCTSTR)lpFilePath, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
  682. FILE_ATTRIBUTE_NORMAL, NULL);
  683. DWORD SetPtr = SetFilePointer(hFile, NULL, NULL, FILE_END);
  684. fWritten = WriteFile(hFile, (LPCVOID)szOutput, (DWORD)strlen(szOutput),
  685. &nBytesWritten, NULL);
  686. if (!fWritten)
  687. FAIL("Unable to write output to file...");
  688. // Close file handle
  689. fClosed = CloseHandle(hFile);
  690. if (!fClosed)
  691. FAIL("Unable to close output file...");
  692. } // OutputToFile
  693. //
  694. // End of Debug Memory Leak implemntation
  695. //
  696. //=--------------------------------------------------------------------------=
  697. //=--------------------------------------------------------------------------=
  698. // This routine outputs through DebugPrintf some information if the
  699. // given hr fails to succeed. This is used by RRETURN to output where
  700. // a function that returns a failing error code.
  701. //=--------------------------------------------------------------------------=
  702. HRESULT HrDebugTraceReturn
  703. (
  704. HRESULT hr,
  705. char *pszFile,
  706. int iLine
  707. )
  708. {
  709. // We only output information if the hr fails.
  710. if (FAILED(hr))
  711. {
  712. char szMessageError[128];
  713. szMessageError[0] = '\0';
  714. BOOL fMessage;
  715. #if RBY_MAC
  716. fMessage = FALSE; // FormatMessage not available on the mac
  717. #else
  718. // Get the message from the system
  719. // CONSIDER, t-tshort 10/95: Getting some messages from us instead
  720. // of the system?
  721. fMessage = FormatMessage(FORMAT_MESSAGE_MAX_WIDTH_MASK
  722. | FORMAT_MESSAGE_FROM_SYSTEM,
  723. NULL, hr,
  724. MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
  725. szMessageError, sizeof(szMessageError), NULL);
  726. #endif
  727. // Erps didn't get a message.
  728. if(!fMessage)
  729. lstrcpy(szMessageError,"Unknown Hresult");
  730. // Output the information that we want.
  731. DebugPrintf("FAILED RETURN: %s(%d) : 0x%08lx, %s\n",
  732. pszFile, iLine, hr, szMessageError);
  733. }
  734. return hr;
  735. }
  736. //---------------------------------------------------------------------
  737. // The following is a common output formatting buffer shared by several
  738. // of the following debug routines.
  739. //---------------------------------------------------------------------
  740. char s_rgchOutput[2048]; // pretty big...
  741. //=--------------------------------------------------------------------------=
  742. // Emit debugging information
  743. //=--------------------------------------------------------------------------=
  744. void _DebugOutput(char* pszOutput)
  745. {
  746. OutputDebugString(pszOutput);
  747. }
  748. //=--------------------------------------------------------------------------=
  749. // Emit a formatted debugging string to the location specified in
  750. // the debug options dialog.
  751. //=--------------------------------------------------------------------------=
  752. void _DebugPrintf(char* pszFmt, ...)
  753. {
  754. va_list args;
  755. va_start(args, pszFmt);
  756. wvsprintf(s_rgchOutput, pszFmt, args);
  757. va_end(args);
  758. // sqwak if we overrun the formatting buffer!
  759. ASSERT(strlen(s_rgchOutput) < sizeof(s_rgchOutput), "");
  760. _DebugOutput(s_rgchOutput);
  761. }
  762. //=--------------------------------------------------------------------------=
  763. // Conditional form of DebugPrintf
  764. //=--------------------------------------------------------------------------=
  765. void _DebugPrintIf(BOOL fPrint, char* pszFmt, ...)
  766. {
  767. va_list args;
  768. if (!fPrint)
  769. return;
  770. va_start(args, pszFmt);
  771. wvsprintf(s_rgchOutput, pszFmt, args);
  772. va_end(args);
  773. // sqwak if we overrun the formatting buffer!
  774. ASSERT(strlen(s_rgchOutput) < sizeof(s_rgchOutput), "");
  775. _DebugOutput(s_rgchOutput);
  776. }
  777. #endif // DEBUG