/******************************Module*Header*******************************\ * Module Name: pooltrk.cxx * * Pool allocation tracker. * * Created: 23-Feb-1998 20:09:03 * Author: Gilman Wong [gilmanw] * * Copyright (c) 1998-1999 Microsoft Corporation * \**************************************************************************/ #include "precomp.hxx" #include "pooltrk.hxx" #if DBG #ifdef _HYDRA_ #ifndef USER_POOL_TAGGING_ON extern BOOL G_fConsole; LIST_ENTRY gleDebugGrePoolTrackerHead; HSEMAPHORE ghsemDebugGrePoolTracker = NULL; /******************************Public*Routine******************************\ * DebugGrePoolTrackerInit * * Initialize the pool tracker. * * Returns: * TRUE if successful, FALSE otherwise. * * History: * 23-Feb-1998 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ BOOL DebugGrePoolTrackerInit() { // // Initialize doubly linked list used to track pool allocations. // InitializeListHead(&gleDebugGrePoolTrackerHead); // // Initialize the list semaphore. // // Note: allocate from non-tracked pool. We will trust the pool // tracker to cleanup its own allocation. // ghsemDebugGrePoolTracker = GreCreateSemaphoreNonTracked(); return (ghsemDebugGrePoolTracker != NULL); } /******************************Public*Routine******************************\ * DebugGrePoolTrackerAdd * * Add specified pool allocation to list. * * History: * 23-Feb-1998 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ VOID DebugGrePoolTrackerAdd(POOLTRACKHDR *pTrack, SIZE_T cj, ULONG ulTag) { PLIST_ENTRY pleNew = (PLIST_ENTRY) pTrack; // // Setup pool track header. // // Sundown note: pool allocations in GRE should be < 4GB // pTrack->ulSize = cj; pTrack->ulTag = ulTag; // // Lock the pool tracking list. // // During GRE initialization, we may be called while allocating // the ghsemDebugGrePoolTracker. So we actually need to check // if it exists. // if (ghsemDebugGrePoolTracker) GreAcquireSemaphore(ghsemDebugGrePoolTracker); // // Insert into the pool tracking list. // InsertTailList(&gleDebugGrePoolTrackerHead, pleNew); // // Unlock the pool tracking list. // if (ghsemDebugGrePoolTracker) GreReleaseSemaphore(ghsemDebugGrePoolTracker); } /******************************Public*Routine******************************\ * DebugGrePoolTrackerRemove * * Remove specified pool allocation from list. * * History: * 23-Feb-1998 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ VOID DebugGrePoolTrackerRemove(POOLTRACKHDR *pTrack) { PLIST_ENTRY pleVictim = (PLIST_ENTRY) pTrack; // // Lock the pool tracking list. // // During GRE initialization, we may be called while allocating // the ghsemDebugGrePoolTracker. So we actually need to check // if it exists. // if (ghsemDebugGrePoolTracker) GreAcquireSemaphore(ghsemDebugGrePoolTracker); // // Remove entry from pool tracking list. // RemoveEntryList(pleVictim); // // Unlock the pool tracking list. // if (ghsemDebugGrePoolTracker) GreReleaseSemaphore(ghsemDebugGrePoolTracker); } /******************************Public*Routine******************************\ * DebugGreAllocPool * * Allocates paged pool and tracks it in the pool tracking list. * Free with DebugGreFreePool. * * Buffer * pTrack --> +----------------+ * | POOLTRACKHDR | * pvRet --> +----------------+ * | Returned | * | Buffer | * | Allocation | * | | * ... * | | * +----------------+ * * * History: * 23-Feb-1998 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ extern "C" PVOID DebugGreAllocPool(SIZE_T ulBytes, ULONG ulTag) { PVOID pv; // // If Hydra, adjust requested size to include tracking header. // // Sundown note: sizeof(POOLTRACKHDR) will fit into 32-bits. // if (!G_fConsole) { if (ulBytes <= (MAXULONG - sizeof(POOLTRACKHDR))) ulBytes += ((ULONG) sizeof(POOLTRACKHDR)); else return NULL; } // // Allocate paged pool. // pv = ExAllocatePoolWithTag( (POOL_TYPE) (SESSION_POOL_MASK | PagedPool), ulBytes, ulTag); if (pv) { // // Tracking overhead if Hydra. // if (!G_fConsole) { // // Add allocation to tracking list. // POOLTRACKHDR *pTrack = (POOLTRACKHDR *) pv; DebugGrePoolTrackerAdd(pTrack, ulBytes, ulTag); #ifdef POOLTRACK_STACKTRACE_ENABLE // // Save the stack back trace. // ULONG ulHash; RtlZeroMemory(pTrack->apvStackTrace, POOLTRACK_TRACESIZE * sizeof(PVOID)); RtlCaptureStackBackTrace(1, POOLTRACK_TRACESIZE, pTrack->apvStackTrace, &ulHash); #endif // // Adjust return pointer to exclude tracking header. // pv = (PVOID) (pTrack + 1); } } return pv; } /******************************Public*Routine******************************\ * DebugGreAllocPoolNonPaged * * Allocates nonpaged pool and tracks it in the pool tracking list. * Free with DebugGreFreePool. * * History: * 23-Feb-1998 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ extern "C" PVOID DebugGreAllocPoolNonPaged(SIZE_T ulBytes, ULONG ulTag) { PVOID pv; // // If Hydra, adjust requested size to include tracking header. // // Sundown note: sizeof(POOLTRACKHDR) will fit into 32-bits. // if (!G_fConsole) { ulBytes += ((ULONG) sizeof(POOLTRACKHDR)); } // // Allocate nonpaged pool. // pv = ExAllocatePoolWithTag( (POOL_TYPE)NonPagedPool, ulBytes, ulTag); if (pv) { // // Tracking overhead if Hydra. // if (!G_fConsole) { // // Add allocation to tracking list. // POOLTRACKHDR *pTrack = (POOLTRACKHDR *) pv; DebugGrePoolTrackerAdd(pTrack, ulBytes, ulTag); // // Adjust return pointer to exclude tracking header. // pv = (PVOID) (pTrack + 1); } } return pv; } /******************************Public*Routine******************************\ * DebugGreFreePool * * Free pool memory allocated by DebugGreAllocPool and * DebugGreAllocPoolNonPaged. Removes the allocation from tracking list. * * History: * 23-Feb-1998 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ extern "C" VOID DebugGreFreePool(PVOID pv) { if (pv) { // // Tracking overhead if Hydra. // if (!G_fConsole) { // // Find header and remove from tracking list. // POOLTRACKHDR *pTrack = ((POOLTRACKHDR *) pv) - 1; DebugGrePoolTrackerRemove(pTrack); // // Adjust pointer to base of allocation. // pv = (PVOID) pTrack; } // // Free the pool allocation. // ExFreePool(pv); } } /******************************Public*Routine******************************\ * DebugGreCleanupPoolTracker * * Frees any allocations remaining in the tracking list (but asserts so * that debugger is informed that a leak exists). Free resources used * to maintain the tracking list. * * History: * 23-Feb-1998 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ VOID DebugGreCleanupPoolTracker() { BOOL bTitle = FALSE; ULONG cLeaks = 0; volatile PLIST_ENTRY pleNext; // // Assert if list not empty (i.e., there are leaks!). // // What to do if there is a leak? // // Well, it might be enough just to look at the tags that leak. // If that doesn't provide enough detail to track the leak, // recompile with USER_POOL_TAGGING_ON defined in engine.h and // define POOL_ALLOC_TRACE in w32\w32inc\usergdi.h to use the // USER pool tracking code. You'll have build a checked version // of win32k (clean build both USER and GDI), but this pool tracker // will record a stack trace for every allocation. If we leak, // USER will assert during Hydra session shutdown and the userkdx // dpa extension will dump the allocations (use !dpa -ts 24 to // dump the leaked TAG_GDI allocations with a stack trace). // if (!IsListEmpty(&gleDebugGrePoolTrackerHead)) { DbgPrint("DebugGreCleanupPoolTracker: " "gleDebugGrePoolTrackerHead 0x%08lx not empty\n", &gleDebugGrePoolTrackerHead); RIP("DebugGreCleanupPoolTracker: leak detected\n"); } // // Free all allocations in the list. // pleNext = gleDebugGrePoolTrackerHead.Flink; while (pleNext != &gleDebugGrePoolTrackerHead) { // // Pool allocation starts after the POOLTRACKHDR. // PVOID pvVictim = (PVOID) (((POOLTRACKHDR *) pleNext) + 1); // // Count the number of leaked allocations. // cLeaks++; // // Print out the allocation information. // if (!bTitle) { DbgPrint("\nDebugGreCleanupPoolTracker: cleaning up pool allocations\n"); DbgPrint("----------\t----------\t-----------------\n"); DbgPrint("Address \tSize \tTag\n"); DbgPrint("----------\t----------\t-----------------\n"); bTitle = TRUE; } DbgPrint("0x%08lx\t0x%08lx\t0x%08lx (%c%c%c%c)\n", pleNext, ((POOLTRACKHDR *) pleNext)->ulSize, ((POOLTRACKHDR *) pleNext)->ulTag, ((((POOLTRACKHDR *) pleNext)->ulTag) ) & 0xff, ((((POOLTRACKHDR *) pleNext)->ulTag) >> 8) & 0xff, ((((POOLTRACKHDR *) pleNext)->ulTag) >> 16) & 0xff, ((((POOLTRACKHDR *) pleNext)->ulTag) >> 24) & 0xff); // // Delete the allocation. // DebugGreFreePool(pvVictim); // // Restart at the begining of the list since our // entry got deleted. // pleNext = gleDebugGrePoolTrackerHead.Flink; } if (bTitle) { DbgPrint("----------\t----------\t-----------------\n"); } if (cLeaks) { DbgPrint("%ld allocations leaked\n\n", cLeaks); } // // Delete the list lock. // // Note that the list lock was allocated from non-tracked pool. // It is also a non-tracked semaphore - that is, it must be deleted // explicitly. // GreDeleteSemaphoreNonTracked(ghsemDebugGrePoolTracker); ghsemDebugGrePoolTracker = NULL; } #endif //USER_POOL_TAGGING_ON #endif //_HYDRA_ #endif //DBG