-#ifdef GDI_DEBUG
+/*
+ * PROJECT: ReactOS win32 kernel mode subsystem
+ * LICENSE: GPL - See COPYING in the top level directory
+ * FILE: subsystems/win32/win32k/objects/gdidbg.c
+ * PURPOSE: Special debugging functions for gdi
+ * PROGRAMMERS: Timo Kreuzer
+ */
+
+/** INCLUDES ******************************************************************/
+
+#include <win32k.h>
+#define NDEBUG
+#include <debug.h>
+
+
+ULONG gulDebugChannels = 0;
-#define KeRosDumpStackFrames(Frames, Count) KdSystemDebugControl(TAG('R', 'o', 's', 'D'), (PVOID)Frames, Count, NULL, 0, NULL, KernelMode)
-NTSYSAPI ULONG APIENTRY RtlWalkFrameChain(OUT PVOID *Callers, IN ULONG Count, IN ULONG Flags);
+#ifdef GDI_DEBUG
-static int leak_reported = 0;
-#define GDI_STACK_LEVELS 12
-static ULONG GDIHandleAllocator[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
-static ULONG GDIHandleLocker[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
-static ULONG GDIHandleShareLocker[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
-static ULONG GDIHandleDeleter[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
+ULONG_PTR GDIHandleAllocator[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
+ULONG_PTR GDIHandleLocker[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
+ULONG_PTR GDIHandleShareLocker[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
+ULONG_PTR GDIHandleDeleter[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
struct DbgOpenGDIHandle
{
ULONG idx;
int count;
};
-#define H 1024
-static struct DbgOpenGDIHandle h[H];
+#define MAX_BACKTRACES 1024
+static struct DbgOpenGDIHandle AllocatorTable[MAX_BACKTRACES];
+
+static
+BOOL
+CompareBacktraces(ULONG idx1, ULONG idx2)
+{
+ ULONG iLevel;
+
+ /* 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])
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
void IntDumpHandleTable(PGDI_HANDLE_TABLE HandleTable)
{
- int i, n = 0, j, k, J;
+ static int leak_reported = 0;
+ int i, j, idx, nTraces = 0;
+ KIRQL OldIrql;
if (leak_reported)
{
leak_reported = 1;
DPRINT1("reporting gdi handle abusers:\n");
- /* step through GDI handle table and find out who our culprit is... */
- for (i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++)
+ /* We've got serious business to do */
+ KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
+
+ /* Step through GDI handle table and find out who our culprit is... */
+ for (idx = RESERVE_ENTRIES_COUNT; idx < GDI_HANDLE_COUNT; idx++)
{
- for (j = 0; j < n; j++)
+ /* If the handle is free, continue */
+ if (!IS_HANDLE_VALID(idx)) continue;
+
+ /* Step through all previous backtraces */
+ for (j = 0; j < nTraces; j++)
{
-next:
- J = h[j].idx;
- for (k = 0; k < GDI_STACK_LEVELS; k++)
+ /* Check if the backtrace matches */
+ if (CompareBacktraces(idx, AllocatorTable[j].idx))
{
- if (GDIHandleAllocator[i][k]
- != GDIHandleAllocator[J][k])
- {
- if (++j == n)
- goto done;
- else
- goto next;
- }
+ /* It matches, increment count and break out */
+ AllocatorTable[j].count++;
+ break;
}
- goto done;
}
-done:
- if (j < H)
+
+ /* Did we find a new backtrace? */
+ if (j == nTraces)
{
- if (j == n)
- {
- h[j].idx = i;
- h[j].count = 1;
- n = n + 1;
- }
- else
- h[j].count++;
+ /* 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 < n-1; i++)
+ for (i = 0; i < nTraces-1; i++)
{
- if (h[i].count < h[i+1].count)
+ if (AllocatorTable[i].count < AllocatorTable[i+1].count)
{
- struct DbgOpenGDIHandle t;
- t = h[i+1];
- h[i+1] = h[i];
+ struct DbgOpenGDIHandle temp;
+
+ temp = AllocatorTable[i+1];
+ AllocatorTable[i+1] = AllocatorTable[i];
j = i;
- while (j > 0 && h[j-1].count < t.count)
+ while (j > 0 && AllocatorTable[j-1].count < temp.count)
j--;
- h[j] = t;
+ AllocatorTable[j] = temp;
}
}
- /* print the worst offenders... */
- DbgPrint("Worst GDI Handle leak offenders (out of %i unique locations):\n", n);
- for (i = 0; i < n && h[i].count > 1; i++)
+
+ /* 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: ", h[i].count);
+ DbgPrint(" %i allocs, type = 0x%lx:\n",
+ AllocatorTable[i].count,
+ GdiHandleTable->Entries[AllocatorTable[i].idx].Type);
/* Dump the frames */
- KeRosDumpStackFrames(GDIHandleAllocator[h[i].idx], GDI_STACK_LEVELS);
+ KeRosDumpStackFrames(GDIHandleAllocator[AllocatorTable[i].idx], GDI_STACK_LEVELS);
+ //KeRosDumpStackFrames(GDIHandleShareLocker[AllocatorTable[i].idx], GDI_STACK_LEVELS);
/* Print new line for better readability */
DbgPrint("\n");
}
- if (i < n && h[i].count == 1)
+
+ if (i < nTraces)
DbgPrint("(list terminated - the remaining entries have 1 allocation only)\n");
+
+ KeLowerIrql(OldIrql);
+
+ ASSERT(FALSE);
}
ULONG
memset(pFrames, 0x00, (nFramesToCapture + 1) * sizeof(PVOID));
- nFrameCount = RtlCaptureStackBackTrace(1, nFramesToCapture, pFrames, NULL);
+ nFrameCount = RtlWalkFrameChain(pFrames, nFramesToCapture, 0);
if (nFrameCount < nFramesToCapture)
{
- nFrameCount += RtlWalkFrameChain(pFrames + nFrameCount, nFramesToCapture - nFrameCount, 1);
+ nFrameCount += RtlWalkFrameChain(pFrames + nFrameCount,
+ nFramesToCapture - nFrameCount,
+ 1);
}
return nFrameCount;
/* FIXME: check reserved entries */
/* Now go through the deleted objects */
- i = GdiHandleTable->FirstFree;
- if (i)
+ i = GdiHandleTable->FirstFree & 0xffff;
+ while (i)
{
pEntry = &GdiHandleTable->Entries[i];
- for (;;)
+ if (i > GDI_HANDLE_COUNT)
{
- nDeleted++;
+ DPRINT1("nDeleted=%ld\n", nDeleted);
+ ASSERT(FALSE);
+ }
- /* Check the entry */
- if ((pEntry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
- {
- r = 0;
- DPRINT1("Deleted Entry has a type != 0\n");
- }
- if ((ULONG_PTR)pEntry->KernelData >= GDI_HANDLE_COUNT)
- {
- r = 0;
- DPRINT1("Deleted entries KernelPointer too big\n");
- }
- if (pEntry->UserData != NULL)
- {
- r = 0;
- DPRINT1("Deleted entry has UserData != 0\n");
- }
- if (pEntry->ProcessId != 0)
- {
- r = 0;
- DPRINT1("Deleted entry has ProcessId != 0\n");
- }
+ nDeleted++;
- i = (ULONG_PTR)pEntry->KernelData;
- if (!i)
- {
- break;
- }
- pEntry = &GdiHandleTable->Entries[i];
- }
- }
+ /* Check the entry */
+ if ((pEntry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
+ {
+ r = 0;
+ DPRINT1("Deleted Entry has a type != 0\n");
+ }
+ if ((ULONG_PTR)pEntry->KernelData >= GDI_HANDLE_COUNT)
+ {
+ r = 0;
+ DPRINT1("Deleted entries KernelPointer too big\n");
+ }
+ if (pEntry->UserData != NULL)
+ {
+ r = 0;
+ DPRINT1("Deleted entry has UserData != 0\n");
+ }
+ if (pEntry->ProcessId != 0)
+ {
+ r = 0;
+ DPRINT1("Deleted entry has ProcessId != 0\n");
+ }
+
+ i = (ULONG_PTR)pEntry->KernelData & 0xffff;
+ };
for (i = GdiHandleTable->FirstUnused;
i < GDI_HANDLE_COUNT;
return r;
}
-#define GDIDBG_TRACECALLER() \
- DPRINT1("-> called from:\n"); \
- KeRosDumpStackFrames(NULL, 20);
-#define GDIDBG_TRACEALLOCATOR(handle) \
- DPRINT1("-> allocated from:\n"); \
- KeRosDumpStackFrames(GDIHandleAllocator[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
-#define GDIDBG_TRACELOCKER(handle) \
- DPRINT1("-> locked from:\n"); \
- KeRosDumpStackFrames(GDIHandleLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
-#define GDIDBG_TRACESHARELOCKER(handle) \
- DPRINT1("-> locked from:\n"); \
- KeRosDumpStackFrames(GDIHandleShareLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
-#define GDIDBG_TRACEDELETER(handle) \
- DPRINT1("-> deleted from:\n"); \
- KeRosDumpStackFrames(GDIHandleDeleter[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
-#define GDIDBG_CAPTUREALLOCATOR(handle) \
- CaptureStackBackTace((PVOID*)GDIHandleAllocator[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
-#define GDIDBG_CAPTURELOCKER(handle) \
- CaptureStackBackTace((PVOID*)GDIHandleLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
-#define GDIDBG_CAPTURESHARELOCKER(handle) \
- CaptureStackBackTace((PVOID*)GDIHandleShareLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
-#define GDIDBG_CAPTUREDELETER(handle) \
- CaptureStackBackTace((PVOID*)GDIHandleDeleter[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
-#define GDIDBG_DUMPHANDLETABLE() \
- IntDumpHandleTable(GdiHandleTable)
-#define GDIDBG_INITLOOPTRACE() \
- ULONG Attempts = 0;
-#define GDIDBG_TRACELOOP(Handle, PrevThread, Thread) \
- if ((++Attempts % 20) == 0) \
- { \
- DPRINT1("[%d] Handle 0x%p Locked by 0x%x (we're 0x%x)\n", Attempts, Handle, PrevThread, Thread); \
- }
-
ULONG
FASTCALL
GDIOBJ_IncrementShareCount(POBJ Object)
return cLocks;
}
-#else
-
-#define GDIDBG_TRACECALLER()
-#define GDIDBG_TRACEALLOCATOR(index)
-#define GDIDBG_TRACELOCKER(index)
-#define GDIDBG_TRACESHARELOCKER(index)
-#define GDIDBG_CAPTUREALLOCATOR(index)
-#define GDIDBG_CAPTURELOCKER(index)
-#define GDIDBG_CAPTURESHARELOCKER(index)
-#define GDIDBG_CAPTUREDELETER(handle)
-#define GDIDBG_DUMPHANDLETABLE()
-#define GDIDBG_INITLOOPTRACE()
-#define GDIDBG_TRACELOOP(Handle, PrevThread, Thread)
-#define GDIDBG_TRACEDELETER(handle)
-
#endif /* GDI_DEBUG */
+void
+GdiDbgDumpLockedHandles()
+{
+ ULONG i;
+
+ for (i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++)
+ {
+ PGDI_TABLE_ENTRY pEntry = &GdiHandleTable->Entries[i];
+
+ if (pEntry->Type & GDI_ENTRY_BASETYPE_MASK)
+ {
+ BASEOBJECT *pObject = pEntry->KernelData;
+ if (pObject->cExclusiveLock > 0)
+ {
+ DPRINT1("Locked object: %lx, type = %lx. allocated from:\n",
+ i, pEntry->Type);
+ GDIDBG_TRACEALLOCATOR(i);
+ DPRINT1("Locked from:\n");
+ GDIDBG_TRACELOCKER(i);
+ }
+ }
+ }
+}
+
+void
+NTAPI
+DbgPreServiceHook(ULONG ulSyscallId, PULONG_PTR pulArguments)
+{
+ PTHREADINFO pti = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
+ if (pti && pti->cExclusiveLocks != 0)
+ {
+ DbgPrint("FATAL: Win32DbgPreServiceHook(%ld): There are %ld exclusive locks!\n",
+ ulSyscallId, pti->cExclusiveLocks);
+ GdiDbgDumpLockedHandles();
+ ASSERT(FALSE);
+ }
+
+}
+
+ULONG_PTR
+NTAPI
+DbgPostServiceHook(ULONG ulSyscallId, ULONG_PTR ulResult)
+{
+ PTHREADINFO pti = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
+ if (pti && pti->cExclusiveLocks != 0)
+ {
+ DbgPrint("FATAL: Win32DbgPostServiceHook(%ld): There are %ld exclusive locks!\n",
+ ulSyscallId, pti->cExclusiveLocks);
+ GdiDbgDumpLockedHandles();
+ ASSERT(FALSE);
+ }
+ return ulResult;
+}
+