dump some statistics on the gdi handle table when it runs out of handles

svn path=/trunk/; revision=12206
This commit is contained in:
Royce Mitchell III 2004-12-18 21:41:17 +00:00
parent de1d28fabc
commit 5016bfecf7

View file

@ -19,7 +19,7 @@
/* /*
* GDIOBJ.C - GDI object manipulation routines * GDIOBJ.C - GDI object manipulation routines
* *
* $Id: gdiobj.c,v 1.78 2004/12/17 15:12:37 navaraf Exp $ * $Id: gdiobj.c,v 1.79 2004/12/18 21:41:17 royce Exp $
*/ */
#include <w32k.h> #include <w32k.h>
@ -53,7 +53,7 @@ STDCALL PsGetProcessId(
typedef struct _GDI_HANDLE_TABLE typedef struct _GDI_HANDLE_TABLE
{ {
PPAGED_LOOKASIDE_LIST LookasideLists; PPAGED_LOOKASIDE_LIST LookasideLists;
SLIST_HEADER FreeEntriesHead; SLIST_HEADER FreeEntriesHead;
SLIST_ENTRY FreeEntries[((GDI_HANDLE_COUNT * sizeof(GDI_TABLE_ENTRY)) << 3) / SLIST_ENTRY FreeEntries[((GDI_HANDLE_COUNT * sizeof(GDI_TABLE_ENTRY)) << 3) /
(sizeof(SLIST_ENTRY) << 3)]; (sizeof(SLIST_ENTRY) << 3)];
@ -126,7 +126,7 @@ GDIOBJ_iAllocHandleTable(VOID)
handleTable = ExAllocatePoolWithTag(NonPagedPool, sizeof(GDI_HANDLE_TABLE), TAG_GDIHNDTBLE); handleTable = ExAllocatePoolWithTag(NonPagedPool, sizeof(GDI_HANDLE_TABLE), TAG_GDIHNDTBLE);
ASSERT( handleTable ); ASSERT( handleTable );
RtlZeroMemory(handleTable, sizeof(GDI_HANDLE_TABLE)); RtlZeroMemory(handleTable, sizeof(GDI_HANDLE_TABLE));
/* /*
* initialize the free entry cache * initialize the free entry cache
*/ */
@ -209,6 +209,71 @@ GetObjectSize(DWORD ObjectType)
return 0; return 0;
} }
#ifdef DBG
static int leak_reported = 0;
#define GDI_STACK_LEVELS 4
static ULONG GDIHandleAllocator[GDI_STACK_LEVELS][GDI_HANDLE_COUNT];
struct DbgOpenGDIHandle
{
ULONG loc;
int count;
};
#define H 1024
static struct DbgOpenGDIHandle h[H];
void IntDumpHandleTable ( int which )
{
int i, n = 0, j;
// step through GDI handle table and find out who our culprit is...
for ( i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++ )
{
for ( j = 0; j < n; j++ )
{
if ( GDIHandleAllocator[which][i] == h[j].loc )
break;
}
if ( j < H )
{
if ( j == n )
{
h[j].loc = GDIHandleAllocator[which][i];
h[j].count = 1;
n = n + 1;
}
else
h[j].count++;
}
}
// bubble sort time! weeeeee!!
for ( i = 0; i < n-1; i++ )
{
if ( h[i].count < h[i+1].count )
{
struct DbgOpenGDIHandle t;
t.loc = h[i+1].loc;
t.count = h[i+1].count;
h[i+1].loc = h[i].loc;
h[i+1].count = h[i].count;
j = i;
while ( j > 0 && h[j-1].count < t.count )
j--;
h[j] = t;
}
}
// print the first 30 offenders...
DbgPrint ( "Worst GDI Handle leak offenders - stack trace level %i (out of %i unique locations):\n", which, n );
for ( i = 0; i < 30 && i < n; i++ )
{
DbgPrint ( "\t" );
if ( !KeRosPrintAddress ( (PVOID)h[i].loc ) )
DbgPrint ( "<%X>", h[i].loc );
DbgPrint ( " (%i allocations)\n", h[i].count );
}
}
#endif//DBG
/*! /*!
* Allocate memory for GDI object and return handle to it. * Allocate memory for GDI object and return handle to it.
* *
@ -269,14 +334,14 @@ GDIOBJ_AllocObj(ULONG ObjectType)
RtlZeroMemory(ObjectBody, GetObjectSize(ObjectType)); RtlZeroMemory(ObjectBody, GetObjectSize(ObjectType));
TypeInfo = (ObjectType & 0xFFFF0000) | (ObjectType >> 16); TypeInfo = (ObjectType & 0xFFFF0000) | (ObjectType >> 16);
FreeEntry = InterlockedPopEntrySList(&HandleTable->FreeEntriesHead); FreeEntry = InterlockedPopEntrySList(&HandleTable->FreeEntriesHead);
if(FreeEntry != NULL) if(FreeEntry != NULL)
{ {
LONG PrevProcId; LONG PrevProcId;
UINT Index; UINT Index;
HGDIOBJ Handle; HGDIOBJ Handle;
/* calculate the entry from the address of the entry in the free slot array */ /* calculate the entry from the address of the entry in the free slot array */
Index = ((ULONG_PTR)FreeEntry - (ULONG_PTR)&HandleTable->FreeEntries[0]) / Index = ((ULONG_PTR)FreeEntry - (ULONG_PTR)&HandleTable->FreeEntries[0]) /
sizeof(HandleTable->FreeEntries[0]); sizeof(HandleTable->FreeEntries[0]);
@ -288,7 +353,7 @@ LockHandle:
if(PrevProcId == 0) if(PrevProcId == 0)
{ {
ASSERT(Entry->KernelData == NULL); ASSERT(Entry->KernelData == NULL);
Entry->KernelData = ObjectBody; Entry->KernelData = ObjectBody;
/* we found a free entry, no need to exchange this field atomically /* we found a free entry, no need to exchange this field atomically
@ -298,6 +363,27 @@ LockHandle:
/* unlock the entry */ /* unlock the entry */
InterlockedExchange(&Entry->ProcessId, CurrentProcessId); InterlockedExchange(&Entry->ProcessId, CurrentProcessId);
#ifdef DBG
{
PULONG Frame;
int which;
#if defined __GNUC__
__asm__("mov %%ebp, %%ebx" : "=b" (Frame) : );
#elif defined(_MSC_VER)
__asm mov [Frame], ebp
#endif
Frame = (PULONG)Frame[0]; // step out of AllocObj()
for ( which = 0; which < GDI_STACK_LEVELS && Frame[1] != 0 && Frame[1] != 0xDEADBEEF; which++ )
{
GDIHandleAllocator[which][Index] = Frame[1]; // step out of AllocObj()
Frame = ((PULONG)Frame[0]);
}
for ( ; which < GDI_STACK_LEVELS; which++ )
GDIHandleAllocator[which][Index] = 0xDEADBEEF;
}
#endif//DBG
//ExAllocatePool ( PagedPool, (ULONG)newObject ); // initiate red-zone verification of object we allocated
if(W32Process != NULL) if(W32Process != NULL)
{ {
InterlockedIncrement(&W32Process->GDIObjects); InterlockedIncrement(&W32Process->GDIObjects);
@ -325,6 +411,20 @@ LockHandle:
ExFreeToPagedLookasideList(LookasideList, newObject); ExFreeToPagedLookasideList(LookasideList, newObject);
DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n"); DPRINT1("Failed to insert gdi object into the handle table, no handles left!\n");
#ifdef DBG
if ( !leak_reported )
{
DPRINT1("reporting gdi handle abusers:\n");
int which;
for ( which = 0; which < GDI_STACK_LEVELS; which++ )
IntDumpHandleTable(which);
leak_reported = 1;
}
else
{
DPRINT1("gdi handle abusers already reported!\n");
}
#endif//DBG
} }
else else
{ {
@ -363,7 +463,7 @@ GDIOBJ_FreeObj(HGDIOBJ hObj, DWORD ObjectType)
#endif #endif
DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj); DPRINT("GDIOBJ_FreeObj: hObj: 0x%08x\n", hObj);
if(GDI_HANDLE_IS_STOCKOBJ(hObj)) if(GDI_HANDLE_IS_STOCKOBJ(hObj))
{ {
DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj); DPRINT1("GDIOBJ_FreeObj() failed, can't delete stock object handle: 0x%x !!!\n", hObj);
@ -403,14 +503,14 @@ LockHandle:
/* Clear the type field so when unlocking the handle it gets finally deleted */ /* Clear the type field so when unlocking the handle it gets finally deleted */
Entry->Type = 0; Entry->Type = 0;
Entry->KernelData = NULL; Entry->KernelData = NULL;
/* unlock the handle slot */ /* unlock the handle slot */
InterlockedExchange(&Entry->ProcessId, 0); InterlockedExchange(&Entry->ProcessId, 0);
/* push this entry to the free list */ /* push this entry to the free list */
InterlockedPushEntrySList(&HandleTable->FreeEntriesHead, InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
&HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]); &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
if(W32Process != NULL) if(W32Process != NULL)
{ {
InterlockedDecrement(&W32Process->GDIObjects); InterlockedDecrement(&W32Process->GDIObjects);
@ -433,7 +533,7 @@ LockHandle:
/* the object is currently locked. just clear the type field so when the /* the object is currently locked. just clear the type field so when the
object gets unlocked it will be finally deleted from the table. */ object gets unlocked it will be finally deleted from the table. */
Entry->Type = 0; Entry->Type = 0;
/* unlock the handle slot */ /* unlock the handle slot */
InterlockedExchange(&Entry->ProcessId, 0); InterlockedExchange(&Entry->ProcessId, 0);
@ -680,7 +780,7 @@ GDIOBJ_LockObj (HGDIOBJ hObj, DWORD ObjectType)
#endif #endif
DPRINT("GDIOBJ_LockObj: hObj: 0x%08x\n", hObj); DPRINT("GDIOBJ_LockObj: hObj: 0x%08x\n", hObj);
Thread = PsGetCurrentThread(); Thread = PsGetCurrentThread();
/* shift the process id to the left so we can use the first bit to lock the object. /* shift the process id to the left so we can use the first bit to lock the object.
@ -713,7 +813,7 @@ LockHandle:
that locked the object. There's no need to do this atomically as we're that locked the object. There's no need to do this atomically as we're
holding the lock of the handle slot, but this way it's easier ;) */ holding the lock of the handle slot, but this way it's easier ;) */
PrevThread = InterlockedCompareExchangePointer(&GdiHdr->LockingThread, Thread, NULL); PrevThread = InterlockedCompareExchangePointer(&GdiHdr->LockingThread, Thread, NULL);
if(PrevThread == NULL || PrevThread == Thread) if(PrevThread == NULL || PrevThread == Thread)
{ {
if(++GdiHdr->Locks == 1) if(++GdiHdr->Locks == 1)
@ -725,14 +825,14 @@ LockHandle:
} }
InterlockedExchange(&Entry->ProcessId, PrevProcId); InterlockedExchange(&Entry->ProcessId, PrevProcId);
/* we're done, return the object body */ /* we're done, return the object body */
return GDIHdrToBdy(GdiHdr); return GDIHdrToBdy(GdiHdr);
} }
else else
{ {
InterlockedExchange(&Entry->ProcessId, PrevProcId); InterlockedExchange(&Entry->ProcessId, PrevProcId);
#ifdef GDI_DEBUG #ifdef GDI_DEBUG
if(++Attempts > 20) if(++Attempts > 20)
{ {
@ -747,14 +847,16 @@ LockHandle:
else else
{ {
InterlockedExchange(&Entry->ProcessId, PrevProcId); InterlockedExchange(&Entry->ProcessId, PrevProcId);
if(EntryType == 0) if(EntryType == 0)
{ {
DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj); DPRINT1("Attempted to lock object 0x%x that is deleted!\n", hObj);
KeRosDumpStackFrames ( NULL, 20 );
} }
else else
{ {
DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, EntryType, ExpectedType); DPRINT1("Attempted to lock object 0x%x, type mismatch (0x%x : 0x%x)\n", hObj, EntryType, ExpectedType);
KeRosDumpStackFrames ( NULL, 20 );
} }
#ifdef GDI_DEBUG #ifdef GDI_DEBUG
DPRINT1("-> called from %s:%i\n", file, line); DPRINT1("-> called from %s:%i\n", file, line);
@ -786,6 +888,7 @@ LockHandle:
else else
{ {
DPRINT1("Attempted to lock foreign handle: 0x%x, Owner: 0x%x locked: 0x%x Caller: 0x%x, stockobj: 0x%x\n", hObj, PrevProcId >> 1, PrevProcId & 0x1, PsGetCurrentProcessId(), GDI_HANDLE_IS_STOCKOBJ(hObj)); DPRINT1("Attempted to lock foreign handle: 0x%x, Owner: 0x%x locked: 0x%x Caller: 0x%x, stockobj: 0x%x\n", hObj, PrevProcId >> 1, PrevProcId & 0x1, PsGetCurrentProcessId(), GDI_HANDLE_IS_STOCKOBJ(hObj));
KeRosDumpStackFrames ( NULL, 20 );
#ifdef GDI_DEBUG #ifdef GDI_DEBUG
DPRINT1("-> called from %s:%i\n", file, line); DPRINT1("-> called from %s:%i\n", file, line);
#endif #endif
@ -842,7 +945,8 @@ LockHandle:
PGDIOBJHDR GdiHdr; PGDIOBJHDR GdiHdr;
GdiHdr = GDIBdyToHdr(Entry->KernelData); GdiHdr = GDIBdyToHdr(Entry->KernelData);
//ExAllocatePool ( PagedPool, (ULONG)GdiHdr ); // initiate red-zone validation on this block
PrevThread = GdiHdr->LockingThread; PrevThread = GdiHdr->LockingThread;
if(PrevThread == Thread) if(PrevThread == Thread)
{ {
@ -851,33 +955,33 @@ LockHandle:
if(--GdiHdr->Locks == 0) if(--GdiHdr->Locks == 0)
{ {
GdiHdr->LockingThread = NULL; GdiHdr->LockingThread = NULL;
#ifdef GDI_DEBUG #ifdef GDI_DEBUG
GdiHdr->lockfile = NULL; GdiHdr->lockfile = NULL;
GdiHdr->lockline = 0; GdiHdr->lockline = 0;
#endif #endif
} }
if(Entry->Type == 0 && GdiHdr->Locks == 0) if(Entry->Type == 0 && GdiHdr->Locks == 0)
{ {
PPAGED_LOOKASIDE_LIST LookasideList; PPAGED_LOOKASIDE_LIST LookasideList;
PW32PROCESS W32Process = PsGetWin32Process(); PW32PROCESS W32Process = PsGetWin32Process();
DWORD Type = GDI_HANDLE_GET_TYPE(hObj); DWORD Type = GDI_HANDLE_GET_TYPE(hObj);
ASSERT(ProcessId != 0); /* must not delete a global handle!!!! */ ASSERT(ProcessId != 0); /* must not delete a global handle!!!! */
/* we should delete the handle */ /* we should delete the handle */
Entry->KernelData = NULL; Entry->KernelData = NULL;
InterlockedExchange(&Entry->ProcessId, 0); InterlockedExchange(&Entry->ProcessId, 0);
InterlockedPushEntrySList(&HandleTable->FreeEntriesHead, InterlockedPushEntrySList(&HandleTable->FreeEntriesHead,
&HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]); &HandleTable->FreeEntries[GDI_ENTRY_TO_INDEX(HandleTable, Entry)]);
if(W32Process != NULL) if(W32Process != NULL)
{ {
InterlockedDecrement(&W32Process->GDIObjects); InterlockedDecrement(&W32Process->GDIObjects);
} }
/* call the cleanup routine. */ /* call the cleanup routine. */
Ret = RunCleanupCallback(GDIHdrToBdy(GdiHdr), Type); Ret = RunCleanupCallback(GDIHdrToBdy(GdiHdr), Type);
@ -994,11 +1098,11 @@ GDIOBJ_ConvertToStockObj(HGDIOBJ *hObj)
#ifdef GDI_DEBUG #ifdef GDI_DEBUG
ULONG Attempts = 0; ULONG Attempts = 0;
#endif #endif
ASSERT(hObj); ASSERT(hObj);
DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj); DPRINT("GDIOBJ_ConvertToStockObj: hObj: 0x%08x\n", *hObj);
Thread = PsGetCurrentThread(); Thread = PsGetCurrentThread();
if(!GDI_HANDLE_IS_STOCKOBJ(*hObj)) if(!GDI_HANDLE_IS_STOCKOBJ(*hObj))
@ -1130,7 +1234,7 @@ GDIOBJ_SetOwnership(HGDIOBJ ObjectHandle, PEPROCESS NewOwner)
#endif #endif
DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle, (NewOwner ? PsGetProcessId(NewOwner) : 0)); DPRINT("GDIOBJ_SetOwnership: hObj: 0x%x, NewProcess: 0x%x\n", ObjectHandle, (NewOwner ? PsGetProcessId(NewOwner) : 0));
Thread = PsGetCurrentThread(); Thread = PsGetCurrentThread();
if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle)) if(!GDI_HANDLE_IS_STOCKOBJ(ObjectHandle))
@ -1152,7 +1256,7 @@ LockHandle:
if(Entry->Type != 0 && Entry->KernelData != NULL) if(Entry->Type != 0 && Entry->KernelData != NULL)
{ {
PGDIOBJHDR GdiHdr = GDIBdyToHdr(Entry->KernelData); PGDIOBJHDR GdiHdr = GDIBdyToHdr(Entry->KernelData);
PrevThread = GdiHdr->LockingThread; PrevThread = GdiHdr->LockingThread;
if(PrevThread == NULL || PrevThread == Thread) if(PrevThread == NULL || PrevThread == Thread)
{ {
@ -1265,7 +1369,7 @@ GDIOBJ_CopyOwnership(HGDIOBJ CopyFrom, HGDIOBJ CopyTo)
#endif #endif
DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom, CopyTo); DPRINT("GDIOBJ_CopyOwnership: from: 0x%x, to: 0x%x\n", CopyFrom, CopyTo);
Thread = PsGetCurrentThread(); Thread = PsGetCurrentThread();
if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo)) if(!GDI_HANDLE_IS_STOCKOBJ(CopyFrom) && !GDI_HANDLE_IS_STOCKOBJ(CopyTo))