3 #define KeRosDumpStackFrames(Frames, Count) KdSystemDebugControl('DsoR', (PVOID)Frames, Count, NULL, 0, NULL, KernelMode)
4 NTSYSAPI ULONG APIENTRY
RtlWalkFrameChain(OUT PVOID
*Callers
, IN ULONG Count
, IN ULONG Flags
);
6 #define GDI_STACK_LEVELS 20
7 static ULONG_PTR GDIHandleAllocator
[GDI_HANDLE_COUNT
][GDI_STACK_LEVELS
+1];
8 static ULONG_PTR GDIHandleLocker
[GDI_HANDLE_COUNT
][GDI_STACK_LEVELS
+1];
9 static ULONG_PTR GDIHandleShareLocker
[GDI_HANDLE_COUNT
][GDI_STACK_LEVELS
+1];
10 static ULONG_PTR GDIHandleDeleter
[GDI_HANDLE_COUNT
][GDI_STACK_LEVELS
+1];
11 struct DbgOpenGDIHandle
16 #define MAX_BACKTRACES 1024
17 static struct DbgOpenGDIHandle AllocatorTable
[MAX_BACKTRACES
];
21 CompareBacktraces(ULONG idx1
, ULONG idx2
)
25 /* Loop all stack levels */
26 for (iLevel
= 0; iLevel
< GDI_STACK_LEVELS
; iLevel
++)
28 if (GDIHandleAllocator
[idx1
][iLevel
]
29 != GDIHandleAllocator
[idx2
][iLevel
])
30 // if (GDIHandleShareLocker[idx1][iLevel]
31 // != GDIHandleShareLocker[idx2][iLevel])
40 #define IS_HANDLE_VALID(idx) \
41 ((GdiHandleTable->Entries[idx].Type & GDI_ENTRY_BASETYPE_MASK) != 0)
43 void IntDumpHandleTable(PGDI_HANDLE_TABLE HandleTable
)
45 static int leak_reported
= 0;
46 int i
, j
, idx
, nTraces
= 0;
51 DPRINT1("gdi handle abusers already reported!\n");
56 DPRINT1("reporting gdi handle abusers:\n");
58 /* We've got serious business to do */
59 KeRaiseIrql(DISPATCH_LEVEL
, &OldIrql
);
61 /* Step through GDI handle table and find out who our culprit is... */
62 for (idx
= RESERVE_ENTRIES_COUNT
; idx
< GDI_HANDLE_COUNT
; idx
++)
64 /* If the handle is free, continue */
65 if (!IS_HANDLE_VALID(idx
)) continue;
67 /* Step through all previous backtraces */
68 for (j
= 0; j
< nTraces
; j
++)
70 /* Check if the backtrace matches */
71 if (CompareBacktraces(idx
, AllocatorTable
[j
].idx
))
73 /* It matches, increment count and break out */
74 AllocatorTable
[j
].count
++;
79 /* Did we find a new backtrace? */
82 /* Break out, if we reached the maximum */
83 if (nTraces
== MAX_BACKTRACES
) break;
85 /* Initialize this entry */
86 AllocatorTable
[j
].idx
= idx
;
87 AllocatorTable
[j
].count
= 1;
92 /* bubble sort time! weeeeee!! */
93 for (i
= 0; i
< nTraces
-1; i
++)
95 if (AllocatorTable
[i
].count
< AllocatorTable
[i
+1].count
)
97 struct DbgOpenGDIHandle temp
;
99 temp
= AllocatorTable
[i
+1];
100 AllocatorTable
[i
+1] = AllocatorTable
[i
];
102 while (j
> 0 && AllocatorTable
[j
-1].count
< temp
.count
)
104 AllocatorTable
[j
] = temp
;
108 /* Print the worst offenders... */
109 DbgPrint("Worst GDI Handle leak offenders (out of %i unique locations):\n", nTraces
);
110 for (i
= 0; i
< nTraces
&& AllocatorTable
[i
].count
> 1; i
++)
112 /* Print out the allocation count */
113 DbgPrint(" %i allocs, type = 0x%lx:\n",
114 AllocatorTable
[i
].count
,
115 GdiHandleTable
->Entries
[AllocatorTable
[i
].idx
].Type
);
117 /* Dump the frames */
118 KeRosDumpStackFrames(GDIHandleAllocator
[AllocatorTable
[i
].idx
], GDI_STACK_LEVELS
);
119 //KeRosDumpStackFrames(GDIHandleShareLocker[AllocatorTable[i].idx], GDI_STACK_LEVELS);
121 /* Print new line for better readability */
126 DbgPrint("(list terminated - the remaining entries have 1 allocation only)\n");
128 KeLowerIrql(OldIrql
);
134 CaptureStackBackTace(PVOID
* pFrames
, ULONG nFramesToCapture
)
138 memset(pFrames
, 0x00, (nFramesToCapture
+ 1) * sizeof(PVOID
));
140 nFrameCount
= RtlWalkFrameChain(pFrames
, nFramesToCapture
, 0);
142 if (nFrameCount
< nFramesToCapture
)
144 nFrameCount
+= RtlWalkFrameChain(pFrames
+ nFrameCount
,
145 nFramesToCapture
- nFrameCount
,
153 GdiDbgHTIntegrityCheck()
155 ULONG i
, nDeleted
= 0, nFree
= 0, nUsed
= 0;
156 PGDI_TABLE_ENTRY pEntry
;
159 KeEnterCriticalRegion();
161 /* FIXME: check reserved entries */
163 /* Now go through the deleted objects */
164 i
= GdiHandleTable
->FirstFree
;
167 pEntry
= &GdiHandleTable
->Entries
[i
];
172 /* Check the entry */
173 if ((pEntry
->Type
& GDI_ENTRY_BASETYPE_MASK
) != 0)
176 DPRINT1("Deleted Entry has a type != 0\n");
178 if ((ULONG_PTR
)pEntry
->KernelData
>= GDI_HANDLE_COUNT
)
181 DPRINT1("Deleted entries KernelPointer too big\n");
183 if (pEntry
->UserData
!= NULL
)
186 DPRINT1("Deleted entry has UserData != 0\n");
188 if (pEntry
->ProcessId
!= 0)
191 DPRINT1("Deleted entry has ProcessId != 0\n");
194 i
= (ULONG_PTR
)pEntry
->KernelData
;
199 pEntry
= &GdiHandleTable
->Entries
[i
];
203 for (i
= GdiHandleTable
->FirstUnused
;
204 i
< GDI_HANDLE_COUNT
;
207 pEntry
= &GdiHandleTable
->Entries
[i
];
209 if ((pEntry
->Type
) != 0)
212 DPRINT1("Free Entry has a type != 0\n");
214 if ((ULONG_PTR
)pEntry
->KernelData
!= 0)
217 DPRINT1("Free entries KernelPointer != 0\n");
219 if (pEntry
->UserData
!= NULL
)
222 DPRINT1("Free entry has UserData != 0\n");
224 if (pEntry
->ProcessId
!= 0)
227 DPRINT1("Free entry has ProcessId != 0\n");
232 for (i
= RESERVE_ENTRIES_COUNT
; i
< GDI_HANDLE_COUNT
; i
++)
237 pEntry
= &GdiHandleTable
->Entries
[i
];
239 Handle
= (HGDIOBJ
)((Type
<< GDI_ENTRY_UPPER_SHIFT
) + i
);
241 if (Type
& GDI_ENTRY_BASETYPE_MASK
)
243 if (pEntry
->KernelData
== NULL
)
246 DPRINT1("Used entry has KernelData == 0\n");
248 if (pEntry
->KernelData
<= MmHighestUserAddress
)
251 DPRINT1("Used entry invalid KernelData\n");
253 if (((POBJ
)(pEntry
->KernelData
))->hHmgr
!= Handle
)
256 DPRINT1("Used entry %ld, has invalid hHmg %p (expected: %p)\n",
257 i
, ((POBJ
)(pEntry
->KernelData
))->hHmgr
, Handle
);
263 if (RESERVE_ENTRIES_COUNT
+ nDeleted
+ nFree
+ nUsed
!= GDI_HANDLE_COUNT
)
266 DPRINT1("Number of all entries incorrect: RESERVE_ENTRIES_COUNT = %ld, nDeleted = %ld, nFree = %ld, nUsed = %ld\n",
267 RESERVE_ENTRIES_COUNT
, nDeleted
, nFree
, nUsed
);
270 KeLeaveCriticalRegion();
275 #define GDIDBG_TRACECALLER() \
276 DPRINT1("-> called from:\n"); \
277 KeRosDumpStackFrames(NULL, 20);
278 #define GDIDBG_TRACEALLOCATOR(handle) \
279 DPRINT1("-> allocated from:\n"); \
280 KeRosDumpStackFrames(GDIHandleAllocator[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
281 #define GDIDBG_TRACELOCKER(handle) \
282 DPRINT1("-> locked from:\n"); \
283 KeRosDumpStackFrames(GDIHandleLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
284 #define GDIDBG_TRACESHARELOCKER(handle) \
285 DPRINT1("-> locked from:\n"); \
286 KeRosDumpStackFrames(GDIHandleShareLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
287 #define GDIDBG_TRACEDELETER(handle) \
288 DPRINT1("-> deleted from:\n"); \
289 KeRosDumpStackFrames(GDIHandleDeleter[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
290 #define GDIDBG_CAPTUREALLOCATOR(handle) \
291 CaptureStackBackTace((PVOID*)GDIHandleAllocator[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
292 #define GDIDBG_CAPTURELOCKER(handle) \
293 CaptureStackBackTace((PVOID*)GDIHandleLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
294 #define GDIDBG_CAPTURESHARELOCKER(handle) \
295 CaptureStackBackTace((PVOID*)GDIHandleShareLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
296 #define GDIDBG_CAPTUREDELETER(handle) \
297 CaptureStackBackTace((PVOID*)GDIHandleDeleter[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
298 #define GDIDBG_DUMPHANDLETABLE() \
299 IntDumpHandleTable(GdiHandleTable)
300 #define GDIDBG_INITLOOPTRACE() \
302 #define GDIDBG_TRACELOOP(Handle, PrevThread, Thread) \
303 if ((++Attempts % 20) == 0) \
305 DPRINT1("[%d] Handle 0x%p Locked by 0x%x (we're 0x%x)\n", Attempts, Handle, PrevThread, Thread); \
310 GDIOBJ_IncrementShareCount(POBJ Object
)
312 INT cLocks
= InterlockedIncrement((PLONG
)&Object
->ulShareCount
);
313 GDIDBG_CAPTURESHARELOCKER(Object
->hHmgr
);
320 #define GDIDBG_TRACECALLER()
321 #define GDIDBG_TRACEALLOCATOR(index)
322 #define GDIDBG_TRACELOCKER(index)
323 #define GDIDBG_TRACESHARELOCKER(index)
324 #define GDIDBG_CAPTUREALLOCATOR(index)
325 #define GDIDBG_CAPTURELOCKER(index)
326 #define GDIDBG_CAPTURESHARELOCKER(index)
327 #define GDIDBG_CAPTUREDELETER(handle)
328 #define GDIDBG_DUMPHANDLETABLE()
329 #define GDIDBG_INITLOOPTRACE()
330 #define GDIDBG_TRACELOOP(Handle, PrevThread, Thread)
331 #define GDIDBG_TRACEDELETER(handle)
333 #endif /* GDI_DEBUG */