Author: tkreuzer Date: Fri Feb 20 21:25:10 2015 New Revision: 66374
URL: http://svn.reactos.org/svn/reactos?rev=66374&view=rev Log: [WIN32K] Add back support for allocator stack backtraces for GDI objects and dump a list of BTs, when the GDI object table is exhausted. Disabled by default.
Modified: trunk/reactos/win32ss/gdi/ntgdi/gdidbg.c trunk/reactos/win32ss/gdi/ntgdi/gdidebug.h trunk/reactos/win32ss/gdi/ntgdi/gdiobj.c trunk/reactos/win32ss/gdi/ntgdi/gdiobj.h trunk/reactos/win32ss/win32kp.h
Modified: trunk/reactos/win32ss/gdi/ntgdi/gdidbg.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/gdi/ntgdi/gdidbg.c?... ============================================================================== --- trunk/reactos/win32ss/gdi/ntgdi/gdidbg.c [iso-8859-1] (original) +++ trunk/reactos/win32ss/gdi/ntgdi/gdidbg.c [iso-8859-1] Fri Feb 20 21:25:10 2015 @@ -15,6 +15,7 @@
extern ULONG gulFirstFree; extern ULONG gulFirstUnused; +extern PENTRY gpentHmgr;;
ULONG gulLogUnique = 0;
@@ -94,21 +95,64 @@ {L"UserWnd", DbgChUserWnd} };
-#ifdef GDI_DEBUG -#if 0 +ULONG +NTAPI +DbgCaptureStackBackTace( + _Out_writes_(cFramesToCapture) PVOID* ppvFrames, + _In_ ULONG cFramesToSkip, + _In_ ULONG cFramesToCapture) +{ + ULONG cFrameCount; + PVOID apvTemp[30]; + NT_ASSERT(cFramesToCapture <= _countof(apvTemp)); + + /* Zero it out */ + RtlZeroMemory(ppvFrames, cFramesToCapture * sizeof(PVOID)); + + /* Capture kernel stack */ + cFrameCount = RtlWalkFrameChain(apvTemp, _countof(apvTemp), 0); + + /* If we should skip more than we have, we are done */ + if (cFramesToSkip > cFrameCount) + return 0; + + /* Copy, but skip frames */ + cFrameCount -= cFramesToSkip; + cFrameCount = min(cFrameCount, cFramesToCapture); + RtlCopyMemory(ppvFrames, &apvTemp[cFramesToSkip], cFrameCount * sizeof(PVOID)); + + /* Check if there is still space left */ + if (cFrameCount < cFramesToCapture) + { + /* Capture user stack */ + cFrameCount += RtlWalkFrameChain(&ppvFrames[cFrameCount], + cFramesToCapture - cFrameCount, + 1); + } + + return cFrameCount; +} + +#if DBG_ENABLE_GDIOBJ_BACKTRACES + static BOOL -CompareBacktraces(ULONG idx1, ULONG idx2) -{ +CompareBacktraces( + USHORT idx1, + USHORT idx2) +{ + POBJ pobj1, pobj2; ULONG iLevel;
+ /* Get the objects */ + pobj1 = gpentHmgr[idx1].einfo.pobj; + pobj2 = gpentHmgr[idx2].einfo.pobj; + /* Loop all stack levels */ - for (iLevel = 0; iLevel < GDI_STACK_LEVELS; iLevel++) - { - if (GDIHandleAllocator[idx1][iLevel] - != GDIHandleAllocator[idx2][iLevel]) -// if (GDIHandleShareLocker[idx1][iLevel] -// != GDIHandleShareLocker[idx2][iLevel]) + for (iLevel = 0; iLevel < GDI_OBJECT_STACK_LEVELS; iLevel++) + { + /* If one level doesn't match we are done */ + if (pobj1->apvBackTrace[iLevel] != pobj2->apvBackTrace[iLevel]) { return FALSE; } @@ -117,22 +161,37 @@ return TRUE; }
+typedef struct +{ + USHORT idx; + USHORT iCount; +} GDI_DBG_HANDLE_BT; + VOID NTAPI DbgDumpGdiHandleTableWithBT(void) { - static int leak_reported = 0; - int i, j, idx, nTraces = 0; + static BOOL bLeakReported = FALSE; + ULONG idx, j; + BOOL bAlreadyPresent; + GDI_DBG_HANDLE_BT aBacktraceTable[GDI_DBG_MAX_BTS]; + USHORT iCount; KIRQL OldIrql; - - if (leak_reported) + POBJ pobj; + ULONG iLevel, ulObj; + + /* Only report once */ + if (bLeakReported) { DPRINT1("GDI handle abusers already reported!\n"); return; }
- leak_reported = 1; + bLeakReported = TRUE; DPRINT1("Reporting GDI handle abusers:\n"); + + /* Zero out the table */ + RtlZeroMemory(aBacktraceTable, sizeof(aBacktraceTable));
/* We've got serious business to do */ KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); @@ -141,94 +200,84 @@ for (idx = RESERVE_ENTRIES_COUNT; idx < GDI_HANDLE_COUNT; idx++) { /* If the handle is free, continue */ - if (!IS_HANDLE_VALID(idx)) continue; - - /* Step through all previous backtraces */ - for (j = 0; j < nTraces; j++) - { - /* Check if the backtrace matches */ - if (CompareBacktraces(idx, AllocatorTable[j].idx)) + if (gpentHmgr[idx].einfo.pobj == 0) continue; + + /* Check if this backtrace is already covered */ + bAlreadyPresent = FALSE; + for (j = RESERVE_ENTRIES_COUNT; j < idx; j++) + { + if (CompareBacktraces(idx, j)) { - /* It matches, increment count and break out */ - AllocatorTable[j].count++; + bAlreadyPresent = TRUE; break; } }
- /* Did we find a new backtrace? */ - if (j == nTraces) - { - /* Break out, if we reached the maximum */ - if (nTraces == MAX_BACKTRACES) break; - - /* Initialize this entry */ - AllocatorTable[j].idx = idx; - AllocatorTable[j].count = 1; - nTraces++; - } - } - - /* bubble sort time! weeeeee!! */ - for (i = 0; i < nTraces-1; i++) - { - if (AllocatorTable[i].count < AllocatorTable[i+1].count) - { - struct DbgOpenGDIHandle temp; - - temp = AllocatorTable[i+1]; - AllocatorTable[i+1] = AllocatorTable[i]; - j = i; - while (j > 0 && AllocatorTable[j-1].count < temp.count) - j--; - AllocatorTable[j] = temp; + if (bAlreadyPresent) continue; + + /* We don't have this BT yet, count how often it is present */ + iCount = 1; + for (j = idx + 1; j < GDI_HANDLE_COUNT; j++) + { + if (CompareBacktraces(idx, j)) + { + iCount++; + } + } + + /* Now add this backtrace */ + for (j = 0; j < GDI_DBG_MAX_BTS; j++) + { + /* Insert it below the next smaller count */ + if (aBacktraceTable[j].iCount < iCount) + { + /* Check if there are entries above */ + if (j < GDI_DBG_MAX_BTS - 1) + { + /* Move the following entries up by 1 */ + RtlMoveMemory(&aBacktraceTable[j], + &aBacktraceTable[j + 1], + GDI_DBG_MAX_BTS - j - 1); + } + + /* Set this entry */ + aBacktraceTable[j].idx = idx; + aBacktraceTable[j].iCount = iCount; + + /* We are done here */ + break; + } } }
/* Print the worst offenders... */ - DbgPrint("Worst GDI Handle leak offenders (out of %i unique locations):\n", nTraces); - for (i = 0; i < nTraces && AllocatorTable[i].count > 1; i++) - { - /* Print out the allocation count */ - DbgPrint(" %i allocs, type = 0x%lx:\n", - AllocatorTable[i].count, - GdiHandleTable->Entries[AllocatorTable[i].idx].Type); - - /* Dump the frames */ - KeRosDumpStackFrames(GDIHandleAllocator[AllocatorTable[i].idx], GDI_STACK_LEVELS); - //KeRosDumpStackFrames(GDIHandleShareLocker[AllocatorTable[i].idx], GDI_STACK_LEVELS); - - /* Print new line for better readability */ + DbgPrint("Count Handle Backtrace\n"); + DbgPrint("------------------------------------------------\n"); + for (j = 0; j < GDI_DBG_MAX_BTS; j++) + { + idx = aBacktraceTable[j].idx; + if (idx == 0) + break; + + ulObj = ((ULONG)gpentHmgr[idx].FullUnique << 16) | idx; + pobj = gpentHmgr[idx].einfo.pobj; + + DbgPrint("%5d %08lx ", aBacktraceTable[j].iCount, ulObj); + for (iLevel = 0; iLevel < GDI_OBJECT_STACK_LEVELS; iLevel++) + { + DbgPrint("%p,", pobj->apvBackTrace[iLevel]); + } DbgPrint("\n"); }
- if (i < nTraces) - DbgPrint("(List terminated - the remaining entries have 1 allocation only)\n"); + __debugbreak();
KeLowerIrql(OldIrql); - - ASSERT(FALSE); -} -#endif - -ULONG -NTAPI -DbgCaptureStackBackTace(PVOID* pFrames, ULONG nFramesToCapture) -{ - ULONG nFrameCount; - - memset(pFrames, 0x00, (nFramesToCapture + 1) * sizeof(PVOID)); - - nFrameCount = RtlWalkFrameChain(pFrames, nFramesToCapture, 0); - - if (nFrameCount < nFramesToCapture) - { - nFrameCount += RtlWalkFrameChain(pFrames + nFrameCount, - nFramesToCapture - nFrameCount, - 1); - } - - return nFrameCount; -} +} + +#endif /* DBG_ENABLE_GDIOBJ_BACKTRACES */ + +#if DBG
BOOL NTAPI @@ -352,32 +401,10 @@ return r; }
-#endif /* GDI_DEBUG */ - -VOID -NTAPI -DbgDumpLockedGdiHandles() -{ -#if 0 - ULONG i; - - for (i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++) - { - PENTRY pentry = &gpentHmgr[i]; - - if (pentry->Objt) - { - POBJ pobj = pentry->einfo.pobj; - if (pobj->cExclusiveLock > 0) - { - DPRINT1("Locked object: %lx, type = %lx. allocated from:\n", - i, pentry->Objt); - DBG_DUMP_EVENT_LIST(&pobj->slhLog); - } - } - } -#endif -} +#endif /* DBG */ + + +#if DBG_ENABLE_EVENT_LOGGING
VOID NTAPI @@ -400,7 +427,7 @@ pLogEntry->lParam = lParam;
/* Capture a backtrace */ - DbgCaptureStackBackTace(pLogEntry->apvBackTrace, 20); + DbgCaptureStackBackTace(pLogEntry->apvBackTrace, 1, 20);
switch (nEventType) { @@ -483,6 +510,33 @@ } }
+#endif /* DBG_ENABLE_EVENT_LOGGING */ + +#if 1 || DBG_ENABLE_SERVICE_HOOKS + +VOID +NTAPI +DbgDumpLockedGdiHandles() +{ + ULONG i; + + for (i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++) + { + PENTRY pentry = &gpentHmgr[i]; + + if (pentry->Objt) + { + POBJ pobj = pentry->einfo.pobj; + if (pobj->cExclusiveLock > 0) + { + DPRINT1("Locked object: %lx, type = %lx. allocated from:\n", + i, pentry->Objt); + DBG_DUMP_EVENT_LIST(&pobj->slhLog); + } + } + } +} + void NTAPI GdiDbgPreServiceHook(ULONG ulSyscallId, PULONG_PTR pulArguments) @@ -512,6 +566,9 @@ } return ulResult; } + +#endif /* DBG_ENABLE_SERVICE_HOOKS */ +
NTSTATUS NTAPI QueryEnvironmentVariable(PUNICODE_STRING Name,
Modified: trunk/reactos/win32ss/gdi/ntgdi/gdidebug.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/gdi/ntgdi/gdidebug.... ============================================================================== --- trunk/reactos/win32ss/gdi/ntgdi/gdidebug.h [iso-8859-1] (original) +++ trunk/reactos/win32ss/gdi/ntgdi/gdidebug.h [iso-8859-1] Fri Feb 20 21:25:10 2015 @@ -1,4 +1,61 @@ #pragma once + +#define GDI_DBG_MAX_BTS 10 + +#if DBG +#define ASSERT_NOGDILOCKS() GdiDbgAssertNoLocks(__FILE__,__LINE__) +#define KeRosDumpStackFrames(Frames, Count) \ + KdSystemDebugControl('DsoR', (PVOID)Frames, Count, NULL, 0, NULL, KernelMode) +#else +#define ASSERT_NOGDILOCKS() +#define KeRosDumpStackFrames(Frames, Count) +#endif + +NTSYSAPI +ULONG +APIENTRY +RtlWalkFrameChain( + _Out_ PVOID *Callers, + _In_ ULONG Count, + _In_ ULONG Flags); + +ULONG +NTAPI +DbgCaptureStackBackTace( + _Out_writes_(cFramesToCapture) PVOID* ppvFrames, + _In_ ULONG cFramesToSkip, + _In_ ULONG cFramesToCapture); + +BOOL +NTAPI +DbgGdiHTIntegrityCheck( + VOID); + +VOID +NTAPI +DbgDumpLockedGdiHandles( + VOID); + +#if DBG_ENABLE_GDIOBJ_BACKTRACES + +VOID +NTAPI +DbgDumpGdiHandleTableWithBT(VOID); + +#endif + +#if KDBG + +BOOLEAN +NTAPI +DbgGdiKdbgCliCallback( + _In_ PCHAR Command, + _In_ ULONG Argc, + _In_ PCH Argv[]); + +#endif + +#if DBG_ENABLE_EVENT_LOGGING
typedef enum _LOG_EVENT_TYPE { @@ -28,39 +85,41 @@ } data; } LOGENTRY, *PLOGENTRY;
-#if KDBG -BOOLEAN +VOID NTAPI -DbgGdiKdbgCliCallback( - IN PCHAR Command, - IN ULONG Argc, - IN PCH Argv[]); -#endif +DbgDumpEventList( + _Inout_ PSLIST_HEADER pslh);
-#if DBG_ENABLE_EVENT_LOGGING -VOID NTAPI DbgDumpEventList(PSLIST_HEADER pslh); -VOID NTAPI DbgLogEvent(PSLIST_HEADER pslh, LOG_EVENT_TYPE nEventType, LPARAM lParam); -VOID NTAPI DbgCleanupEventList(PSLIST_HEADER pslh); -VOID NTAPI DbgPrintEvent(PLOGENTRY pLogEntry); +VOID +NTAPI +DbgLogEvent( + _Inout_ PSLIST_HEADER pslh, + _In_ LOG_EVENT_TYPE nEventType, + _In_ LPARAM lParam); + +VOID +NTAPI +DbgCleanupEventList( + _Inout_ PSLIST_HEADER pslh); + +VOID +NTAPI +DbgPrintEvent( + _Inout_ PLOGENTRY pLogEntry); + #define DBG_LOGEVENT(pslh, type, val) DbgLogEvent(pslh, type, (ULONG_PTR)val) #define DBG_INITLOG(pslh) InitializeSListHead(pslh) #define DBG_DUMP_EVENT_LIST(pslh) DbgDumpEventList(pslh) #define DBG_CLEANUP_EVENT_LIST(pslh) DbgCleanupEventList(pslh) + #else + #define DBG_LOGEVENT(pslh, type, val) ((void)(val)) #define DBG_INITLOG(pslh) #define DBG_DUMP_EVENT_LIST(pslh) #define DBG_CLEANUP_EVENT_LIST(pslh) + #endif - - -VOID NTAPI DbgDumpGdiHandleTableWithBT(VOID); -ULONG NTAPI DbgCaptureStackBackTace(PVOID* pFrames, ULONG nFramesToCapture); -BOOL NTAPI DbgGdiHTIntegrityCheck(VOID); -VOID NTAPI DbgDumpLockedGdiHandles(VOID); - -#define KeRosDumpStackFrames(Frames, Count) KdSystemDebugControl('DsoR', (PVOID)Frames, Count, NULL, 0, NULL, KernelMode) -NTSYSAPI ULONG APIENTRY RtlWalkFrameChain(OUT PVOID *Callers, IN ULONG Count, IN ULONG Flags);
#if DBG void @@ -88,9 +147,6 @@ ASSERT(FALSE); } } - -#define ASSERT_NOGDILOCKS() GdiDbgAssertNoLocks(__FILE__,__LINE__) -#else -#define ASSERT_NOGDILOCKS() #endif
+
Modified: trunk/reactos/win32ss/gdi/ntgdi/gdiobj.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/gdi/ntgdi/gdiobj.c?... ============================================================================== --- trunk/reactos/win32ss/gdi/ntgdi/gdiobj.c [iso-8859-1] (original) +++ trunk/reactos/win32ss/gdi/ntgdi/gdiobj.c [iso-8859-1] Fri Feb 20 21:25:10 2015 @@ -339,6 +339,10 @@ if (iFirst >= GDI_HANDLE_COUNT) { DPRINT1("No more GDI handles left!\n"); +#if DBG_ENABLE_GDIOBJ_BACKTRACES + DbgDumpGdiHandleTableWithBT(); +#endif + InterlockedDecrement((LONG*)&gulFirstUnused); return 0; }
@@ -350,7 +354,7 @@ pentFree = &gpentHmgr[iFirst & GDI_HANDLE_INDEX_MASK];
/* Create a new value with an increased sequence number */ - iNext = (USHORT)(ULONG_PTR)pentFree->einfo.pobj; + iNext = GDI_HANDLE_GET_INDEX(pentFree->einfo.hFree); iNext |= (iFirst & ~GDI_HANDLE_INDEX_MASK) + 0x10000;
/* Try to exchange the FirstFree value */ @@ -516,6 +520,9 @@ pobj->BaseFlags = fl & 0xffff; DBG_INITLOG(&pobj->slhLog); DBG_LOGEVENT(&pobj->slhLog, EVENT_ALLOCATE, 0); +#if DBG_ENABLE_GDIOBJ_BACKTRACES + DbgCaptureStackBackTace(pobj->apvBackTrace, 1, GDI_OBJECT_STACK_LEVELS); +#endif /* GDI_DEBUG */
return pobj; } @@ -1497,9 +1504,7 @@ }
#if DBG -//#ifdef GDI_DEBUG - DbgGdiHTIntegrityCheck(); -//#endif + DbgGdiHTIntegrityCheck(); #endif
ppi = PsGetCurrentProcessWin32Process();
Modified: trunk/reactos/win32ss/gdi/ntgdi/gdiobj.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/gdi/ntgdi/gdiobj.h?... ============================================================================== --- trunk/reactos/win32ss/gdi/ntgdi/gdiobj.h [iso-8859-1] (original) +++ trunk/reactos/win32ss/gdi/ntgdi/gdiobj.h [iso-8859-1] Fri Feb 20 21:25:10 2015 @@ -4,6 +4,8 @@ */
#pragma once + +#define GDI_OBJECT_STACK_LEVELS 10
/* The first 10 entries are never used in windows, they are empty */ static const unsigned RESERVE_ENTRIES_COUNT = 10; @@ -42,6 +44,9 @@ USHORT cExclusiveLock; USHORT BaseFlags; EX_PUSH_LOCK pushlock; +#if DBG_ENABLE_GDIOBJ_BACKTRACES + PVOID apvBackTrace[GDI_OBJECT_STACK_LEVELS]; +#endif #if DBG_ENABLE_EVENT_LOGGING SLIST_HEADER slhLog; #endif
Modified: trunk/reactos/win32ss/win32kp.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/win32kp.h?rev=66374... ============================================================================== --- trunk/reactos/win32ss/win32kp.h [iso-8859-1] (original) +++ trunk/reactos/win32ss/win32kp.h [iso-8859-1] Fri Feb 20 21:25:10 2015 @@ -23,6 +23,7 @@
/* Enable debugging features */ #define GDI_DEBUG 0 +#define DBG_ENABLE_GDIOBJ_BACKTRACES 0 #define DBG_ENABLE_EVENT_LOGGING 0 #define DBG_ENABLE_SERVICE_HOOKS 0