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.

396 lines
10 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. #ifdef DPNBUILD_FIXEDMEMORYMODEL
  57. DNASSERT(dwpMaxMemUsage != 0);
  58. #else // ! DPNBUILD_FIXEDMEMORYMODEL
  59. DNASSERT(dwpMaxMemUsage == 0);
  60. #endif // ! DPNBUILD_FIXEDMEMORYMODEL
  61. DPFX(DPFPREP, 5, "Initializing Memory Tracking");
  62. // In debug we always maintain a separate heap and track allocations. In retail,
  63. // we don't track allocations, and will use the process heap except for
  64. // DPNBUILD_FIXEDMEMORYMODEL builds, where we use a separate heap so we
  65. // can cap the total allocation size.
  66. #ifdef DPNBUILD_ONLYONETHREAD
  67. g_hMemoryHeap = HeapCreate(HEAP_NO_SERIALIZE, // flags
  68. #else // ! DPNBUILD_ONLYONETHREAD
  69. g_hMemoryHeap = HeapCreate(0, // flags
  70. #endif // ! DPNBUILD_ONLYONETHREAD
  71. dwpMaxMemUsage, // initial size
  72. dwpMaxMemUsage // maximum heap size (if 0, it can grow)
  73. );
  74. if (g_hMemoryHeap == NULL)
  75. {
  76. DPFX(DPFPREP, 0, "Failed to create memory heap!");
  77. return FALSE;
  78. }
  79. #ifdef DBG
  80. #pragma TODO(masonb, "Handle possibility of failure")
  81. InitializeCriticalSection(&g_AllocatedMemoryLock);
  82. g_blAllocatedMemory.Initialize();
  83. g_dwpCurrentNumMemAllocations = 0;
  84. g_dwpCurrentMemAllocated = 0;
  85. g_dwpTotalNumMemAllocations = 0;
  86. g_dwpTotalMemAllocated = 0;
  87. g_dwpPeakNumMemAllocations = 0;
  88. g_dwpPeakMemAllocated = 0;
  89. #endif // DBG
  90. return TRUE;
  91. }
  92. #undef DPF_MODNAME
  93. #define DPF_MODNAME "DNMemoryTrackDeinitialize"
  94. void DNMemoryTrackDeinitialize()
  95. {
  96. // Validate the heap if we're on NT and a debug build, and then destroy the heap.
  97. if (g_hMemoryHeap != NULL)
  98. {
  99. BOOL fResult;
  100. #ifdef DBG
  101. DWORD dwError;
  102. DPFX(DPFPREP, 5, "Deinitializing Memory Tracking");
  103. DPFX(DPFPREP, 5, "Total num mem allocations = %u", g_dwpTotalNumMemAllocations);
  104. DPFX(DPFPREP, 5, "Total mem allocated = %u", g_dwpTotalMemAllocated);
  105. DPFX(DPFPREP, 5, "Peak num mem allocations = %u", g_dwpPeakNumMemAllocations);
  106. DPFX(DPFPREP, 5, "Peak mem allocated = %u", g_dwpPeakMemAllocated);
  107. DeleteCriticalSection(&g_AllocatedMemoryLock);
  108. #ifdef WINNT
  109. // Validate heap contents before shutdown. This code only works on NT.
  110. fResult = HeapValidate(g_hMemoryHeap, 0, NULL);
  111. if (! fResult)
  112. {
  113. dwError = GetLastError();
  114. DPFX(DPFPREP, 0, "Problem validating heap on destroy %d!", dwError );
  115. DNASSERT(! "Problem validating heap on destroy!");
  116. }
  117. #endif // WINNT
  118. #endif // DBG
  119. fResult = HeapDestroy(g_hMemoryHeap);
  120. if (! fResult)
  121. {
  122. #ifdef DBG
  123. dwError = GetLastError();
  124. DPFX(DPFPREP, 0, "Problem destroying heap %d!", dwError );
  125. DNASSERT(! "Problem destroying heap!");
  126. #endif // DBG
  127. }
  128. g_hMemoryHeap = NULL;
  129. }
  130. }
  131. #endif // DBG or DPNBUILD_FIXEDMEMORYMODEL
  132. #ifdef DBG
  133. #undef DPF_MODNAME
  134. #define DPF_MODNAME "DNMemoryTrackHeapAlloc"
  135. void* DNMemoryTrackHeapAlloc(DWORD_PTR dwpSize)
  136. {
  137. MEMORY_HEADER* pMemory;
  138. void* pReturn;
  139. DNASSERT(g_hMemoryHeap != NULL);
  140. // Voice and lobby currently try allocating 0 byte buffers, can't enable this check yet.
  141. //DNASSERT( Size > 0 );
  142. DNMemoryTrackValidateMemory();
  143. if (DNMemoryTrackAreAllocationsAllowed())
  144. {
  145. // We need enough room for our header plus what the user wants plus the guard signature at the end
  146. pMemory = (MEMORY_HEADER*)HeapAlloc(g_hMemoryHeap, 0, sizeof(MEMORY_HEADER) + dwpSize + sizeof(DWORD_PTR));
  147. if (pMemory != NULL)
  148. {
  149. pMemory->blLinkage.Initialize();
  150. pMemory->dwpSize = dwpSize;
  151. pMemory->AllocCallStack.NoteCurrentCallStack();
  152. pMemory->dwpPreGuard = GUARD_SIGNATURE;
  153. *(UNALIGNED DWORD_PTR*)((BYTE*)(pMemory + 1) + dwpSize) = GUARD_SIGNATURE;
  154. EnterCriticalSection(&g_AllocatedMemoryLock);
  155. pMemory->blLinkage.InsertAfter(&g_blAllocatedMemory);
  156. g_dwpCurrentNumMemAllocations++;
  157. g_dwpCurrentMemAllocated += dwpSize;
  158. g_dwpTotalNumMemAllocations++;
  159. g_dwpTotalMemAllocated += dwpSize;
  160. if (g_dwpCurrentNumMemAllocations > g_dwpPeakNumMemAllocations)
  161. {
  162. g_dwpPeakNumMemAllocations = g_dwpCurrentNumMemAllocations;
  163. }
  164. if (g_dwpCurrentMemAllocated > g_dwpPeakMemAllocated)
  165. {
  166. g_dwpPeakMemAllocated = g_dwpCurrentMemAllocated;
  167. }
  168. LeaveCriticalSection(&g_AllocatedMemoryLock);
  169. pReturn = pMemory + 1;
  170. // We require that the pointers we pass back are heap aligned
  171. DNASSERT(((DWORD_PTR)pReturn & 0xF) == 0 || // IA64
  172. (((DWORD_PTR)pReturn & 0x7) == 0 && ((DWORD_PTR)pMemory & 0xF) == 0x8) || // NT32
  173. (((DWORD_PTR)pReturn & 0x3) == 0 && ((DWORD_PTR)pMemory & 0xF) == 0x4) || // WIN9X
  174. (((DWORD_PTR)pReturn & 0x3) == 0 && ((DWORD_PTR)pMemory & 0xF) == 0xC) // WIN9X
  175. );
  176. DPFX(DPFPREP, 5, "Memory Allocated, pData[%p], Size[%d]", pReturn, dwpSize);
  177. }
  178. else
  179. {
  180. DPFX(DPFPREP, 0, "Failed allocating memory.");
  181. pReturn = NULL;
  182. }
  183. }
  184. else
  185. {
  186. DPFX(DPFPREP, 0, "Memory allocations are not currently allowed!");
  187. DNASSERT(! "Memory allocations are not currently allowed!");
  188. pReturn = NULL;
  189. }
  190. return pReturn;
  191. }
  192. #undef DPF_MODNAME
  193. #define DPF_MODNAME "DNMemoryTrackHeapFree"
  194. void DNMemoryTrackHeapFree(void* pvData)
  195. {
  196. CBilink* pbl;
  197. MEMORY_HEADER* pMemory;
  198. DNASSERT(g_hMemoryHeap != NULL);
  199. DNMemoryTrackValidateMemory();
  200. if (pvData == NULL)
  201. {
  202. return;
  203. }
  204. EnterCriticalSection( &g_AllocatedMemoryLock );
  205. // Verify that we know of this pointer
  206. pbl = g_blAllocatedMemory.GetNext();
  207. while (pbl != &g_blAllocatedMemory)
  208. {
  209. pMemory = CONTAINING_RECORD(pbl, MEMORY_HEADER, blLinkage);
  210. if ((pMemory + 1) == pvData)
  211. {
  212. break;
  213. }
  214. pbl = pbl->GetNext();
  215. }
  216. DNASSERT(pbl != &g_blAllocatedMemory);
  217. pMemory->blLinkage.RemoveFromList();
  218. g_dwpCurrentNumMemAllocations--;
  219. g_dwpCurrentMemAllocated -= pMemory->dwpSize;
  220. LeaveCriticalSection(&g_AllocatedMemoryLock);
  221. DPFX(DPFPREP, 5, "Memory Freed, pData[%p], Size[%d]", pMemory + 1, pMemory->dwpSize);
  222. // Zero it in case someone is still trying to use it
  223. memset(pMemory, 0, sizeof(MEMORY_HEADER) + pMemory->dwpSize + sizeof(DWORD_PTR));
  224. HeapFree(g_hMemoryHeap, 0, pMemory);
  225. }
  226. #undef DPF_MODNAME
  227. #define DPF_MODNAME "DNMemoryTrackValidateMemory"
  228. void DNMemoryTrackValidateMemory()
  229. {
  230. CBilink* pbl;
  231. MEMORY_HEADER* pMemory;
  232. LPCTSTR pszCause;
  233. DWORD_PTR dwpNumAllocations = 0;
  234. DWORD_PTR dwpTotalAllocated = 0;
  235. TCHAR CallStackBuffer[CALLSTACK_BUFFER_SIZE];
  236. DNASSERT(g_hMemoryHeap != NULL);
  237. // validate all of the allocated memory
  238. EnterCriticalSection( &g_AllocatedMemoryLock );
  239. pbl = g_blAllocatedMemory.GetNext();
  240. while (pbl != &g_blAllocatedMemory)
  241. {
  242. pMemory = CONTAINING_RECORD(pbl, MEMORY_HEADER, blLinkage);
  243. if (pMemory->dwpPreGuard != GUARD_SIGNATURE)
  244. {
  245. pszCause = _T("UNDERRUN DETECTED");
  246. }
  247. else if (*(UNALIGNED DWORD_PTR*)((BYTE*)(pMemory + 1) + pMemory->dwpSize) != GUARD_SIGNATURE)
  248. {
  249. pszCause = _T("OVERRUN DETECTED");
  250. }
  251. else
  252. {
  253. pszCause = NULL;
  254. dwpNumAllocations++;
  255. dwpTotalAllocated += pMemory->dwpSize;
  256. }
  257. if (pszCause)
  258. {
  259. pMemory->AllocCallStack.GetCallStackString(CallStackBuffer);
  260. DPFX(DPFPREP, 0, "Memory corruption[%s], pData[%p], Size[%d]\n%s", pszCause, pMemory + 1, pMemory->dwpSize, CallStackBuffer);
  261. DNASSERT(FALSE);
  262. }
  263. pbl = pbl->GetNext();
  264. }
  265. DNASSERT(dwpNumAllocations == g_dwpCurrentNumMemAllocations);
  266. DNASSERT(dwpTotalAllocated == g_dwpCurrentMemAllocated);
  267. LeaveCriticalSection(&g_AllocatedMemoryLock);
  268. #ifdef WINNT
  269. // Ask the OS to validate the heap
  270. if (HeapValidate(g_hMemoryHeap, 0, NULL) == FALSE)
  271. {
  272. DNASSERT(FALSE);
  273. }
  274. #endif // WINNT
  275. }
  276. #undef DPF_MODNAME
  277. #define DPF_MODNAME "DNMemoryTrackDumpLeaks"
  278. BOOL DNMemoryTrackDumpLeaks()
  279. {
  280. MEMORY_HEADER* pMemory;
  281. TCHAR CallStackBuffer[CALLSTACK_BUFFER_SIZE];
  282. BOOL fLeaked = FALSE;
  283. DNASSERT(g_hMemoryHeap != NULL);
  284. EnterCriticalSection( &g_AllocatedMemoryLock );
  285. while (!g_blAllocatedMemory.IsEmpty())
  286. {
  287. pMemory = CONTAINING_RECORD(g_blAllocatedMemory.GetNext(), MEMORY_HEADER, blLinkage);
  288. pMemory->AllocCallStack.GetCallStackString(CallStackBuffer);
  289. DPFX(DPFPREP, 0, "Memory leaked, pData[%p], Size[%d]\n%s", pMemory + 1, pMemory->dwpSize, CallStackBuffer);
  290. pMemory->blLinkage.RemoveFromList();
  291. HeapFree(g_hMemoryHeap, 0, pMemory);
  292. fLeaked = TRUE;
  293. }
  294. LeaveCriticalSection(&g_AllocatedMemoryLock);
  295. return fLeaked;
  296. }
  297. #endif // DBG
  298. #ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
  299. #undef DPF_MODNAME
  300. #define DPF_MODNAME "DNMemoryTrackAllowAllocations"
  301. void DNMemoryTrackAllowAllocations(BOOL fAllow)
  302. {
  303. DPFX(DPFPREP, 1, "Memory allocations allowed = %i.", fAllow);
  304. DNInterlockedExchange((LONG*) (&g_fAllocationsAllowed), fAllow);
  305. }
  306. #endif // DPNBUILD_PREALLOCATEDMEMORYMODEL