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.

575 lines
10 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. dbgtrack.c
  5. Abstract:
  6. Allocation tracking implementation. From old debug.c
  7. Author:
  8. Marc R. Whitten (marcw) 09-Sept-1999
  9. Revision History:
  10. --*/
  11. //
  12. // Includes
  13. //
  14. #include "pch.h"
  15. //
  16. // NOTE: No code should appear outside the #ifdef DEBUG
  17. //
  18. #ifdef DEBUG
  19. #pragma message("DEBUG macros enabled")
  20. //
  21. // Strings
  22. //
  23. // None
  24. //
  25. // Constants
  26. //
  27. #define TRACK_BUCKETS 1501
  28. #define BUCKET_ITEMS_PER_POOL 8192
  29. //
  30. // Macros
  31. //
  32. // None
  33. //
  34. // Types
  35. //
  36. typedef UBINT ALLOCATION_ITEM_OFFSET;
  37. typedef struct _tagTRACKBUCKETITEM {
  38. struct _tagTRACKBUCKETITEM *Next;
  39. struct _tagTRACKBUCKETITEM *Prev;
  40. ALLOCTYPE Type;
  41. PVOID Ptr;
  42. ALLOCATION_ITEM_OFFSET ItemOffset;
  43. } TRACKBUCKETITEM, *PTRACKBUCKETITEM;
  44. typedef struct _tagBUCKETPOOL {
  45. UINT Count;
  46. TRACKBUCKETITEM Items[BUCKET_ITEMS_PER_POOL];
  47. } TRACKBUCKETPOOL, *PTRACKBUCKETPOOL;
  48. typedef struct {
  49. ALLOCTYPE Type;
  50. PVOID Ptr;
  51. PCSTR FileName;
  52. UINT Line;
  53. BOOL Allocated;
  54. } ALLOCATION_ITEM, *PALLOCATION_ITEM;
  55. //
  56. // Globals
  57. //
  58. PTRACKBUCKETITEM g_TrackBuckets[TRACK_BUCKETS];
  59. PTRACKBUCKETITEM g_TrackPoolDelHead;
  60. PTRACKBUCKETPOOL g_TrackPool;
  61. //
  62. // The following pointer can be used to help identify memory leak sources.
  63. // It is copied to the memory tracking log.
  64. //
  65. PCSTR g_TrackComment;
  66. PCSTR g_TrackFile;
  67. UINT g_TrackLine;
  68. BOOL g_TrackAlloc;
  69. INT g_UseCount;
  70. UINT g_DisableTrackComment = 0;
  71. GROWBUFFER g_AllocationList;
  72. PVOID g_FirstDeletedAlloc;
  73. //
  74. // Macro expansion list
  75. //
  76. // None
  77. //
  78. // Private function prototypes
  79. //
  80. //
  81. // Macro expansion definition
  82. //
  83. // None
  84. //
  85. // Code
  86. //
  87. VOID
  88. DisableTrackComment (
  89. VOID
  90. )
  91. {
  92. g_DisableTrackComment ++;
  93. }
  94. VOID
  95. EnableTrackComment (
  96. VOID
  97. )
  98. {
  99. if (g_DisableTrackComment > 0) {
  100. g_DisableTrackComment --;
  101. }
  102. }
  103. PBYTE
  104. pOurTrackedGbGrow (
  105. IN PGROWBUFFER Buffer,
  106. IN UINT Bytes
  107. )
  108. {
  109. PBYTE p;
  110. BOOL trackMsg = FALSE;
  111. //
  112. // Because grow buffers themselves cause tracking, we have to
  113. // call the untracked version. To eliminate confusion, we
  114. // give a helpful note.
  115. //
  116. if (!g_TrackFile) {
  117. trackMsg = TRUE;
  118. g_TrackFile = "<allocation tracking in dbgtrack.c, not a real leak>";
  119. g_TrackLine = __LINE__;
  120. }
  121. p = (PSTR) RealGbGrow (Buffer, Bytes);
  122. if (trackMsg) {
  123. g_TrackFile = NULL;
  124. }
  125. return p;
  126. }
  127. INT
  128. TrackPushEx (
  129. PCSTR Msg,
  130. PCSTR File,
  131. UINT Line,
  132. BOOL Alloc
  133. )
  134. {
  135. if (g_DisableTrackComment > 0) {
  136. return 0;
  137. }
  138. if (g_UseCount > 0) {
  139. g_UseCount++;
  140. return 0;
  141. }
  142. TrackPush (Msg, File, Line);
  143. g_TrackAlloc = TRUE;
  144. return 0;
  145. }
  146. INT
  147. TrackPush (
  148. PCSTR Msg,
  149. PCSTR File,
  150. UINT Line
  151. )
  152. {
  153. static CHAR Buffer[1024];
  154. if (g_DisableTrackComment > 0) {
  155. return 0;
  156. }
  157. if (g_UseCount > 0) {
  158. g_UseCount++;
  159. return 0;
  160. }
  161. if (Msg) {
  162. wsprintfA (Buffer, "%s line %u [%s]", File, Line, Msg);
  163. } else {
  164. wsprintfA (Buffer, "%s line %u", File, Line);
  165. }
  166. g_TrackFile = File;
  167. g_TrackLine = Line;
  168. g_TrackAlloc = FALSE;
  169. g_TrackComment = Buffer;
  170. g_UseCount = 1;
  171. return 0;
  172. }
  173. INT
  174. TrackPop (
  175. VOID
  176. )
  177. {
  178. if (g_DisableTrackComment > 0) {
  179. return 0;
  180. }
  181. g_UseCount--;
  182. if (!g_UseCount) {
  183. g_TrackComment = NULL;
  184. g_TrackFile = NULL;
  185. g_TrackLine = 0;
  186. g_TrackAlloc = FALSE;
  187. }
  188. return 0;
  189. }
  190. VOID
  191. TrackDump (
  192. VOID
  193. )
  194. {
  195. if (g_UseCount > 0) {
  196. DEBUGMSGA (("Caller", "%s line %u (%s)", g_TrackFile, g_TrackLine, g_TrackComment));
  197. }
  198. }
  199. VOID
  200. InitAllocationTracking (
  201. VOID
  202. )
  203. {
  204. ZeroMemory (&g_AllocationList, sizeof (g_AllocationList));
  205. g_AllocationList.GrowSize = 65536;
  206. g_FirstDeletedAlloc = NULL;
  207. }
  208. VOID
  209. FreeAllocationTracking (
  210. VOID
  211. )
  212. {
  213. UINT Size;
  214. UINT u;
  215. PALLOCATION_ITEM Item;
  216. GROWBUFFER Msg = INIT_GROWBUFFER;
  217. CHAR Text[1024];
  218. PSTR p;
  219. UINT Bytes;
  220. Size = g_AllocationList.End / sizeof (ALLOCATION_ITEM);;
  221. for (u = 0 ; u < Size ; u++) {
  222. Item = (PALLOCATION_ITEM) g_AllocationList.Buf + u;
  223. if (!Item->FileName) {
  224. continue;
  225. }
  226. Bytes = (UINT) wsprintfA (Text, "%s line %u\r\n", Item->FileName, Item->Line);
  227. p = (PSTR) pOurTrackedGbGrow (&Msg, Bytes);
  228. if (p) {
  229. CopyMemory (p, Text, Bytes);
  230. }
  231. }
  232. if (Msg.End) {
  233. p = (PSTR) pOurTrackedGbGrow (&Msg, 1);
  234. if (p) {
  235. *p = 0;
  236. DEBUGMSGA (("Leaks", "%s", Msg.Buf));
  237. }
  238. GbFree (&Msg);
  239. }
  240. GbFree (&g_AllocationList);
  241. g_FirstDeletedAlloc = NULL;
  242. // Intentional leak -- who cares about track memory
  243. g_TrackPoolDelHead = NULL;
  244. g_TrackPool = NULL;
  245. }
  246. PTRACKBUCKETITEM
  247. pAllocTrackBucketItem (
  248. VOID
  249. )
  250. {
  251. PTRACKBUCKETITEM BucketItem;
  252. if (g_TrackPoolDelHead) {
  253. BucketItem = g_TrackPoolDelHead;
  254. g_TrackPoolDelHead = BucketItem->Next;
  255. } else {
  256. if (!g_TrackPool || g_TrackPool->Count == BUCKET_ITEMS_PER_POOL) {
  257. g_TrackPool = (PTRACKBUCKETPOOL) SafeHeapAlloc (g_hHeap, 0, sizeof (TRACKBUCKETPOOL));
  258. if (!g_TrackPool) {
  259. return NULL;
  260. }
  261. g_TrackPool->Count = 0;
  262. }
  263. BucketItem = g_TrackPool->Items + g_TrackPool->Count;
  264. g_TrackPool->Count++;
  265. }
  266. return BucketItem;
  267. }
  268. VOID
  269. pFreeTrackBucketItem (
  270. PTRACKBUCKETITEM BucketItem
  271. )
  272. {
  273. BucketItem->Next = g_TrackPoolDelHead;
  274. g_TrackPoolDelHead = BucketItem;
  275. }
  276. DWORD
  277. pComputeTrackHashVal (
  278. IN ALLOCTYPE Type,
  279. IN PVOID Ptr
  280. )
  281. {
  282. DWORD Hash;
  283. Hash = (DWORD) ((DWORD)Type << 16) ^ (DWORD)(UBINT)Ptr;
  284. return Hash % TRACK_BUCKETS;
  285. }
  286. VOID
  287. pTrackHashTableInsert (
  288. IN PBYTE Base,
  289. IN ALLOCATION_ITEM_OFFSET ItemOffset
  290. )
  291. {
  292. DWORD Hash;
  293. PTRACKBUCKETITEM BucketItem;
  294. PALLOCATION_ITEM Item;
  295. Item = (PALLOCATION_ITEM) (Base + ItemOffset);
  296. Hash = pComputeTrackHashVal (Item->Type, Item->Ptr);
  297. BucketItem = pAllocTrackBucketItem();
  298. if (!BucketItem) {
  299. DEBUGMSG ((DBG_WHOOPS, "pTrackHashTableInsert failed to alloc memory"));
  300. return;
  301. }
  302. BucketItem->Prev = NULL;
  303. BucketItem->Next = g_TrackBuckets[Hash];
  304. BucketItem->Type = Item->Type;
  305. BucketItem->Ptr = Item->Ptr;
  306. BucketItem->ItemOffset = ItemOffset;
  307. if (BucketItem->Next) {
  308. BucketItem->Next->Prev = BucketItem;
  309. }
  310. g_TrackBuckets[Hash] = BucketItem;
  311. }
  312. VOID
  313. pTrackHashTableDelete (
  314. IN PTRACKBUCKETITEM BucketItem
  315. )
  316. {
  317. DWORD Hash;
  318. Hash = pComputeTrackHashVal (BucketItem->Type, BucketItem->Ptr);
  319. if (BucketItem->Prev) {
  320. BucketItem->Prev->Next = BucketItem->Next;
  321. } else {
  322. g_TrackBuckets[Hash] = BucketItem->Next;
  323. }
  324. if (BucketItem->Next) {
  325. BucketItem->Next->Prev = BucketItem->Prev;
  326. }
  327. pFreeTrackBucketItem (BucketItem);
  328. }
  329. PTRACKBUCKETITEM
  330. pTrackHashTableFind (
  331. IN ALLOCTYPE Type,
  332. IN PVOID Ptr
  333. )
  334. {
  335. PTRACKBUCKETITEM BucketItem;
  336. DWORD Hash;
  337. Hash = pComputeTrackHashVal (Type, Ptr);
  338. BucketItem = g_TrackBuckets[Hash];
  339. while (BucketItem) {
  340. if (BucketItem->Type == Type && BucketItem->Ptr == Ptr) {
  341. return BucketItem;
  342. }
  343. BucketItem = BucketItem->Next;
  344. }
  345. return NULL;
  346. }
  347. VOID
  348. DebugRegisterAllocationEx (
  349. IN ALLOCTYPE Type,
  350. IN PVOID Ptr,
  351. IN PCSTR File,
  352. IN UINT Line,
  353. IN BOOL Alloc
  354. )
  355. {
  356. PALLOCATION_ITEM Item;
  357. MYASSERT (File);
  358. if (!g_FirstDeletedAlloc) {
  359. Item = (PALLOCATION_ITEM) pOurTrackedGbGrow (&g_AllocationList,sizeof(ALLOCATION_ITEM));
  360. } else {
  361. Item = (PALLOCATION_ITEM) g_FirstDeletedAlloc;
  362. g_FirstDeletedAlloc = Item->Ptr;
  363. }
  364. if (Item) {
  365. Item->Type = Type;
  366. Item->Ptr = Ptr;
  367. if (Alloc) {
  368. Item->FileName = SafeHeapAlloc (g_hHeap, 0, SizeOfStringA (File));
  369. if (Item->FileName) {
  370. StringCopyA ((PSTR)Item->FileName, File);
  371. }
  372. Item->Allocated = TRUE;
  373. } else {
  374. Item->FileName = File;
  375. Item->Allocated = FALSE;
  376. }
  377. Item->Line = Line;
  378. pTrackHashTableInsert (
  379. g_AllocationList.Buf,
  380. (ALLOCATION_ITEM_OFFSET) ((PBYTE) Item - g_AllocationList.Buf)
  381. );
  382. //DEBUGMSG ((DBG_VERBOSE, "Allocation: File=%s, Line=%d, Size=%d", File, Line, Size));
  383. }
  384. }
  385. VOID
  386. DebugRegisterAllocation (
  387. IN ALLOCTYPE Type,
  388. IN PVOID Ptr,
  389. IN PCSTR File,
  390. IN UINT Line
  391. )
  392. {
  393. PALLOCATION_ITEM Item;
  394. MYASSERT (File);
  395. if (!g_FirstDeletedAlloc) {
  396. Item = (PALLOCATION_ITEM) pOurTrackedGbGrow (&g_AllocationList,sizeof(ALLOCATION_ITEM));
  397. } else {
  398. Item = (PALLOCATION_ITEM) g_FirstDeletedAlloc;
  399. g_FirstDeletedAlloc = Item->Ptr;
  400. }
  401. if (Item) {
  402. Item->Type = Type;
  403. Item->Ptr = Ptr;
  404. Item->FileName = File;
  405. Item->Line = Line;
  406. Item->Allocated = FALSE;
  407. pTrackHashTableInsert (
  408. g_AllocationList.Buf,
  409. (ALLOCATION_ITEM_OFFSET) ((PBYTE) Item - g_AllocationList.Buf)
  410. );
  411. }
  412. }
  413. VOID
  414. DebugUnregisterAllocation (
  415. IN ALLOCTYPE Type,
  416. IN PVOID Ptr
  417. )
  418. {
  419. PALLOCATION_ITEM Item;
  420. PTRACKBUCKETITEM BucketItem;
  421. BucketItem = pTrackHashTableFind (Type, Ptr);
  422. if (!g_AllocationList.Buf) {
  423. DEBUGMSG ((DBG_WARNING, "Unregister allocation: Allocation buffer already freed"));
  424. return;
  425. }
  426. if (BucketItem) {
  427. Item = (PALLOCATION_ITEM) (g_AllocationList.Buf + BucketItem->ItemOffset);
  428. if (Item->Allocated) {
  429. HeapFree (g_hHeap, 0, (PSTR)Item->FileName);
  430. }
  431. Item->FileName = NULL;
  432. Item->Type = (ALLOCTYPE) -1;
  433. Item->Ptr = g_FirstDeletedAlloc;
  434. g_FirstDeletedAlloc = Item;
  435. pTrackHashTableDelete (BucketItem);
  436. } else {
  437. DEBUGMSG ((DBG_WARNING, "Unregister allocation: Pointer not registered"));
  438. }
  439. }
  440. #endif