[WIN32K]
[reactos.git] / reactos / subsystems / win32 / win32k / objects / gdidbg.c
index a9991ad..bf46e2b 100644 (file)
@@ -1,25 +1,60 @@
-#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('DsoR', (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)
     {
@@ -30,68 +65,79 @@ void IntDumpHandleTable(PGDI_HANDLE_TABLE HandleTable)
     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
@@ -101,11 +147,13 @@ CaptureStackBackTace(PVOID* pFrames, ULONG nFramesToCapture)
 
     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;
@@ -123,44 +171,42 @@ GdiDbgHTIntegrityCheck()
        /* 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;
@@ -234,39 +280,6 @@ GdiDbgHTIntegrityCheck()
        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)
@@ -277,20 +290,59 @@ 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;
+}
+