Leaked source code of windows server 2003
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.

413 lines
11 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 2001-2002 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: MemoryTracking.cpp
  6. * Content: Debug memory tracking for detecting leaks, overruns, etc.
  7. *
  8. * History:
  9. * Date By Reason
  10. * ==== == ======
  11. * 11/14/2001 masonb Created
  12. *
  13. ***************************************************************************/
  14. #include "dncmni.h"
  15. #ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
  16. BOOL g_fAllocationsAllowed = TRUE;
  17. #endif // DPNBUILD_PREALLOCATEDMEMORYMODEL
  18. #ifdef DBG
  19. #ifdef _WIN64
  20. #define GUARD_SIGNATURE 0xABABABABABABABAB
  21. #else // !_WIN64
  22. #define GUARD_SIGNATURE 0xABABABAB
  23. #endif // _WIN64
  24. // Structure prepended to memory allocations to check for leaks.
  25. struct MEMORY_HEADER
  26. {
  27. CBilink blLinkage; // size = two pointers
  28. DWORD_PTR dwpSize; // size = pointer
  29. CCallStack AllocCallStack; // size = 12 pointers
  30. DWORD_PTR dwpPreGuard; // size = pointer
  31. // We want what follows to always be 16-byte aligned and #pragma pack doesn't seem to ensure that
  32. };
  33. CRITICAL_SECTION g_AllocatedMemoryLock;
  34. CBilink g_blAllocatedMemory;
  35. DWORD_PTR g_dwpCurrentNumMemAllocations = 0;
  36. DWORD_PTR g_dwpCurrentMemAllocated = 0;
  37. DWORD_PTR g_dwpTotalNumMemAllocations = 0;
  38. DWORD_PTR g_dwpTotalMemAllocated = 0;
  39. DWORD_PTR g_dwpPeakNumMemAllocations = 0;
  40. DWORD_PTR g_dwpPeakMemAllocated = 0;
  41. #endif // DBG
  42. #if ((defined(DBG)) || (defined(DPNBUILD_FIXEDMEMORYMODEL)))
  43. HANDLE g_hMemoryHeap = NULL;
  44. #undef DPF_MODNAME
  45. #define DPF_MODNAME "DNMemoryTrackInitialize"
  46. BOOL DNMemoryTrackInitialize(DWORD_PTR dwpMaxMemUsage)
  47. {
  48. // Ensure that we stay heap aligned for SLISTs
  49. #ifdef _WIN64
  50. DBG_CASSERT(sizeof(MEMORY_HEADER) % 16 == 0);
  51. #else // !_WIN64
  52. DBG_CASSERT(sizeof(MEMORY_HEADER) % 8 == 0);
  53. #endif // _WIN64
  54. // Check for double init
  55. DNASSERT(g_hMemoryHeap == NULL);
  56. #ifndef DPNBUILD_FIXEDMEMORYMODEL
  57. DNASSERT(dwpMaxMemUsage == 0);
  58. #endif // ! DPNBUILD_FIXEDMEMORYMODEL
  59. DPFX(DPFPREP, 5, "Initializing Memory Tracking");
  60. // In debug we always maintain a separate heap and track allocations. In retail,
  61. // we don't track allocations, and will use the process heap except for
  62. // DPNBUILD_FIXEDMEMORYMODEL builds, where we use a separate heap so we
  63. // can cap the total allocation size.
  64. #ifdef DPNBUILD_ONLYONETHREAD
  65. g_hMemoryHeap = HeapCreate(HEAP_NO_SERIALIZE, // flags
  66. #else // ! DPNBUILD_ONLYONETHREAD
  67. g_hMemoryHeap = HeapCreate(0, // flags
  68. #endif // ! DPNBUILD_ONLYONETHREAD
  69. dwpMaxMemUsage, // initial size
  70. dwpMaxMemUsage // maximum heap size (if 0, it can grow)
  71. );
  72. if (g_hMemoryHeap == NULL)
  73. {
  74. DPFX(DPFPREP, 0, "Failed to create memory heap!");
  75. return FALSE;
  76. }
  77. #ifdef DBG
  78. #pragma TODO(masonb, "Handle possibility of failure")
  79. InitializeCriticalSection(&g_AllocatedMemoryLock);
  80. g_blAllocatedMemory.Initialize();
  81. g_dwpCurrentNumMemAllocations = 0;
  82. g_dwpCurrentMemAllocated = 0;
  83. g_dwpTotalNumMemAllocations = 0;
  84. g_dwpTotalMemAllocated = 0;
  85. g_dwpPeakNumMemAllocations = 0;
  86. g_dwpPeakMemAllocated = 0;
  87. #endif // DBG
  88. return TRUE;
  89. }
  90. #undef DPF_MODNAME
  91. #define DPF_MODNAME "DNMemoryTrackDeinitialize"
  92. void DNMemoryTrackDeinitialize()
  93. {
  94. // Validate the heap if we're on NT and a debug build, and then destroy the heap.
  95. if (g_hMemoryHeap != NULL)
  96. {
  97. BOOL fResult;
  98. #ifdef DBG
  99. DWORD dwError;
  100. DPFX(DPFPREP, 5, "Deinitializing Memory Tracking");
  101. DPFX(DPFPREP, 5, "Total num mem allocations = %u", g_dwpTotalNumMemAllocations);
  102. DPFX(DPFPREP, 5, "Total mem allocated = %u", g_dwpTotalMemAllocated);
  103. DPFX(DPFPREP, 5, "Peak num mem allocations = %u", g_dwpPeakNumMemAllocations);
  104. DPFX(DPFPREP, 5, "Peak mem allocated = %u", g_dwpPeakMemAllocated);
  105. DeleteCriticalSection(&g_AllocatedMemoryLock);
  106. #ifdef WINNT
  107. // Validate heap contents before shutdown. This code only works on NT.
  108. fResult = HeapValidate(g_hMemoryHeap, 0, NULL);
  109. if (! fResult)
  110. {
  111. dwError = GetLastError();
  112. DPFX(DPFPREP, 0, "Problem validating heap on destroy %d!", dwError );
  113. DNASSERT(! "Problem validating heap on destroy!");
  114. }
  115. #endif // WINNT
  116. #endif // DBG
  117. fResult = HeapDestroy(g_hMemoryHeap);
  118. if (! fResult)
  119. {
  120. #ifdef DBG
  121. dwError = GetLastError();
  122. DPFX(DPFPREP, 0, "Problem destroying heap %d!", dwError );
  123. DNASSERT(! "Problem destroying heap!");
  124. #endif // DBG
  125. }
  126. g_hMemoryHeap = NULL;
  127. }
  128. }
  129. #endif // DBG or DPNBUILD_FIXEDMEMORYMODEL
  130. #ifdef DBG
  131. #undef DPF_MODNAME
  132. #define DPF_MODNAME "DNMemoryTrackHeapAlloc"
  133. void* DNMemoryTrackHeapAlloc(DWORD_PTR dwpSize)
  134. {
  135. MEMORY_HEADER* pMemory;
  136. void* pReturn;
  137. DNASSERT(g_hMemoryHeap != NULL);
  138. // Voice and lobby currently try allocating 0 byte buffers, can't enable this check yet.
  139. //DNASSERT( Size > 0 );
  140. DNMemoryTrackValidateMemory();
  141. if (DNMemoryTrackAreAllocationsAllowed())
  142. {
  143. // We need enough room for our header plus what the user wants plus the guard signature at the end
  144. pMemory = (MEMORY_HEADER*)HeapAlloc(g_hMemoryHeap, 0, sizeof(MEMORY_HEADER) + dwpSize + sizeof(DWORD_PTR));
  145. #if ((! defined(WINCE)) && (! defined(WIN95)))
  146. if (pMemory == NULL)
  147. {
  148. DWORD_PTR dwpLargestFreeBlock;
  149. // Compact the heap to see how much size is available, and possibly try allocating again.
  150. dwpLargestFreeBlock = HeapCompact(g_hMemoryHeap, 0);
  151. if (dwpLargestFreeBlock >= (sizeof(MEMORY_HEADER) + dwpSize + sizeof(DWORD_PTR)))
  152. {
  153. DPFX(DPFPREP, 1, "Largest free block after compacting is %u bytes, allocating %u bytes again.", dwpLargestFreeBlock, dwpSize);
  154. pMemory = (MEMORY_HEADER*)HeapAlloc(g_hMemoryHeap, 0, sizeof(MEMORY_HEADER) + dwpSize + sizeof(DWORD_PTR));
  155. }
  156. else
  157. {
  158. DPFX(DPFPREP, 1, "Largest free block after compacting is %u bytes, cannot allocate %u bytes.", dwpLargestFreeBlock, dwpSize);
  159. }
  160. }
  161. #endif // ! WINCE and ! WIN95
  162. if (pMemory != NULL)
  163. {
  164. pMemory->blLinkage.Initialize();
  165. pMemory->dwpSize = dwpSize;
  166. pMemory->AllocCallStack.NoteCurrentCallStack();
  167. pMemory->dwpPreGuard = GUARD_SIGNATURE;
  168. *(DWORD_PTR UNALIGNED *)((BYTE*)(pMemory + 1) + dwpSize) = GUARD_SIGNATURE;
  169. EnterCriticalSection(&g_AllocatedMemoryLock);
  170. pMemory->blLinkage.InsertAfter(&g_blAllocatedMemory);
  171. g_dwpCurrentNumMemAllocations++;
  172. g_dwpCurrentMemAllocated += dwpSize;
  173. g_dwpTotalNumMemAllocations++;
  174. g_dwpTotalMemAllocated += dwpSize;
  175. if (g_dwpCurrentNumMemAllocations > g_dwpPeakNumMemAllocations)
  176. {
  177. g_dwpPeakNumMemAllocations = g_dwpCurrentNumMemAllocations;
  178. }
  179. if (g_dwpCurrentMemAllocated > g_dwpPeakMemAllocated)
  180. {
  181. g_dwpPeakMemAllocated = g_dwpCurrentMemAllocated;
  182. }
  183. LeaveCriticalSection(&g_AllocatedMemoryLock);
  184. pReturn = pMemory + 1;
  185. // We require that the pointers we pass back are heap aligned
  186. DNASSERT(((DWORD_PTR)pReturn & 0xF) == 0 || // IA64
  187. (((DWORD_PTR)pReturn & 0x7) == 0 && ((DWORD_PTR)pMemory & 0xF) == 0x8) || // NT32
  188. (((DWORD_PTR)pReturn & 0x3) == 0 && ((DWORD_PTR)pMemory & 0xF) == 0x4) || // WIN9X
  189. (((DWORD_PTR)pReturn & 0x3) == 0 && ((DWORD_PTR)pMemory & 0xF) == 0xC) // WIN9X
  190. );
  191. DPFX(DPFPREP, 5, "Memory Allocated, pData[%p], Size[%d]", pReturn, dwpSize);
  192. }
  193. else
  194. {
  195. DPFX(DPFPREP, 0, "Failed allocating %u bytes of memory.", dwpSize);
  196. pReturn = NULL;
  197. }
  198. }
  199. else
  200. {
  201. DPFX(DPFPREP, 0, "Memory allocations are not currently allowed!");
  202. DNASSERT(! "Memory allocations are not currently allowed!");
  203. pReturn = NULL;
  204. }
  205. return pReturn;
  206. }
  207. #undef DPF_MODNAME
  208. #define DPF_MODNAME "DNMemoryTrackHeapFree"
  209. void DNMemoryTrackHeapFree(void* pvData)
  210. {
  211. CBilink* pbl;
  212. MEMORY_HEADER* pMemory;
  213. DNASSERT(g_hMemoryHeap != NULL);
  214. DNMemoryTrackValidateMemory();
  215. if (pvData == NULL)
  216. {
  217. return;
  218. }
  219. EnterCriticalSection( &g_AllocatedMemoryLock );
  220. // Verify that we know of this pointer
  221. pbl = g_blAllocatedMemory.GetNext();
  222. while (pbl != &g_blAllocatedMemory)
  223. {
  224. pMemory = CONTAINING_RECORD(pbl, MEMORY_HEADER, blLinkage);
  225. if ((pMemory + 1) == pvData)
  226. {
  227. break;
  228. }
  229. pbl = pbl->GetNext();
  230. }
  231. DNASSERT(pbl != &g_blAllocatedMemory);
  232. pMemory->blLinkage.RemoveFromList();
  233. g_dwpCurrentNumMemAllocations--;
  234. g_dwpCurrentMemAllocated -= pMemory->dwpSize;
  235. LeaveCriticalSection(&g_AllocatedMemoryLock);
  236. DPFX(DPFPREP, 5, "Memory Freed, pData[%p], Size[%d]", pMemory + 1, pMemory->dwpSize);
  237. // Zero it in case someone is still trying to use it
  238. memset(pMemory, 0, sizeof(MEMORY_HEADER) + pMemory->dwpSize + sizeof(DWORD_PTR));
  239. HeapFree(g_hMemoryHeap, 0, pMemory);
  240. }
  241. #undef DPF_MODNAME
  242. #define DPF_MODNAME "DNMemoryTrackValidateMemory"
  243. void DNMemoryTrackValidateMemory()
  244. {
  245. CBilink* pbl;
  246. MEMORY_HEADER* pMemory;
  247. LPCTSTR pszCause;
  248. DWORD_PTR dwpNumAllocations = 0;
  249. DWORD_PTR dwpTotalAllocated = 0;
  250. TCHAR CallStackBuffer[CALLSTACK_BUFFER_SIZE];
  251. DNASSERT(g_hMemoryHeap != NULL);
  252. // validate all of the allocated memory
  253. EnterCriticalSection( &g_AllocatedMemoryLock );
  254. pbl = g_blAllocatedMemory.GetNext();
  255. while (pbl != &g_blAllocatedMemory)
  256. {
  257. pMemory = CONTAINING_RECORD(pbl, MEMORY_HEADER, blLinkage);
  258. if (pMemory->dwpPreGuard != GUARD_SIGNATURE)
  259. {
  260. pszCause = _T("UNDERRUN DETECTED");
  261. }
  262. else if (*(DWORD_PTR UNALIGNED *)((BYTE*)(pMemory + 1) + pMemory->dwpSize) != GUARD_SIGNATURE)
  263. {
  264. pszCause = _T("OVERRUN DETECTED");
  265. }
  266. else
  267. {
  268. pszCause = NULL;
  269. dwpNumAllocations++;
  270. dwpTotalAllocated += pMemory->dwpSize;
  271. }
  272. if (pszCause)
  273. {
  274. pMemory->AllocCallStack.GetCallStackString(CallStackBuffer);
  275. DPFX(DPFPREP, 0, "Memory corruption[%s], pData[%p], Size[%d]\n%s", pszCause, pMemory + 1, pMemory->dwpSize, CallStackBuffer);
  276. DNASSERT(FALSE);
  277. }
  278. pbl = pbl->GetNext();
  279. }
  280. DNASSERT(dwpNumAllocations == g_dwpCurrentNumMemAllocations);
  281. DNASSERT(dwpTotalAllocated == g_dwpCurrentMemAllocated);
  282. LeaveCriticalSection(&g_AllocatedMemoryLock);
  283. #ifdef WINNT
  284. // Ask the OS to validate the heap
  285. if (HeapValidate(g_hMemoryHeap, 0, NULL) == FALSE)
  286. {
  287. DNASSERT(FALSE);
  288. }
  289. #endif // WINNT
  290. }
  291. #undef DPF_MODNAME
  292. #define DPF_MODNAME "DNMemoryTrackDumpLeaks"
  293. BOOL DNMemoryTrackDumpLeaks()
  294. {
  295. MEMORY_HEADER* pMemory;
  296. TCHAR CallStackBuffer[CALLSTACK_BUFFER_SIZE];
  297. BOOL fLeaked = FALSE;
  298. DNASSERT(g_hMemoryHeap != NULL);
  299. EnterCriticalSection( &g_AllocatedMemoryLock );
  300. while (!g_blAllocatedMemory.IsEmpty())
  301. {
  302. pMemory = CONTAINING_RECORD(g_blAllocatedMemory.GetNext(), MEMORY_HEADER, blLinkage);
  303. pMemory->AllocCallStack.GetCallStackString(CallStackBuffer);
  304. DPFX(DPFPREP, 0, "Memory leaked, pData[%p], Size[%d]\n%s", pMemory + 1, pMemory->dwpSize, CallStackBuffer);
  305. pMemory->blLinkage.RemoveFromList();
  306. HeapFree(g_hMemoryHeap, 0, pMemory);
  307. fLeaked = TRUE;
  308. }
  309. LeaveCriticalSection(&g_AllocatedMemoryLock);
  310. return fLeaked;
  311. }
  312. #endif // DBG
  313. #ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
  314. #undef DPF_MODNAME
  315. #define DPF_MODNAME "DNMemoryTrackAllowAllocations"
  316. void DNMemoryTrackAllowAllocations(BOOL fAllow)
  317. {
  318. DPFX(DPFPREP, 1, "Memory allocations allowed = %i.", fAllow);
  319. DNInterlockedExchange((LONG*) (&g_fAllocationsAllowed), fAllow);
  320. }
  321. #endif // DPNBUILD_PREALLOCATEDMEMORYMODEL