[WIN32K]
[reactos.git] / reactos / subsystems / win32 / win32k / objects / gdidbg.c
1 /*
2 * PROJECT: ReactOS win32 kernel mode subsystem
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: subsystems/win32/win32k/objects/gdidbg.c
5 * PURPOSE: Special debugging functions for gdi
6 * PROGRAMMERS: Timo Kreuzer
7 */
8
9 /** INCLUDES ******************************************************************/
10
11 #include <win32k.h>
12 #define NDEBUG
13 #include <debug.h>
14
15
16 ULONG gulDebugChannels = 0;
17
18 #ifdef GDI_DEBUG
19
20 ULONG_PTR GDIHandleAllocator[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
21 ULONG_PTR GDIHandleLocker[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
22 ULONG_PTR GDIHandleShareLocker[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
23 ULONG_PTR GDIHandleDeleter[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
24 struct DbgOpenGDIHandle
25 {
26 ULONG idx;
27 int count;
28 };
29 #define MAX_BACKTRACES 1024
30 static struct DbgOpenGDIHandle AllocatorTable[MAX_BACKTRACES];
31
32 static
33 BOOL
34 CompareBacktraces(ULONG idx1, ULONG idx2)
35 {
36 ULONG iLevel;
37
38 /* Loop all stack levels */
39 for (iLevel = 0; iLevel < GDI_STACK_LEVELS; iLevel++)
40 {
41 if (GDIHandleAllocator[idx1][iLevel]
42 != GDIHandleAllocator[idx2][iLevel])
43 // if (GDIHandleShareLocker[idx1][iLevel]
44 // != GDIHandleShareLocker[idx2][iLevel])
45 {
46 return FALSE;
47 }
48 }
49
50 return TRUE;
51 }
52
53 void IntDumpHandleTable(PGDI_HANDLE_TABLE HandleTable)
54 {
55 static int leak_reported = 0;
56 int i, j, idx, nTraces = 0;
57 KIRQL OldIrql;
58
59 if (leak_reported)
60 {
61 DPRINT1("gdi handle abusers already reported!\n");
62 return;
63 }
64
65 leak_reported = 1;
66 DPRINT1("reporting gdi handle abusers:\n");
67
68 /* We've got serious business to do */
69 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
70
71 /* Step through GDI handle table and find out who our culprit is... */
72 for (idx = RESERVE_ENTRIES_COUNT; idx < GDI_HANDLE_COUNT; idx++)
73 {
74 /* If the handle is free, continue */
75 if (!IS_HANDLE_VALID(idx)) continue;
76
77 /* Step through all previous backtraces */
78 for (j = 0; j < nTraces; j++)
79 {
80 /* Check if the backtrace matches */
81 if (CompareBacktraces(idx, AllocatorTable[j].idx))
82 {
83 /* It matches, increment count and break out */
84 AllocatorTable[j].count++;
85 break;
86 }
87 }
88
89 /* Did we find a new backtrace? */
90 if (j == nTraces)
91 {
92 /* Break out, if we reached the maximum */
93 if (nTraces == MAX_BACKTRACES) break;
94
95 /* Initialize this entry */
96 AllocatorTable[j].idx = idx;
97 AllocatorTable[j].count = 1;
98 nTraces++;
99 }
100 }
101
102 /* bubble sort time! weeeeee!! */
103 for (i = 0; i < nTraces-1; i++)
104 {
105 if (AllocatorTable[i].count < AllocatorTable[i+1].count)
106 {
107 struct DbgOpenGDIHandle temp;
108
109 temp = AllocatorTable[i+1];
110 AllocatorTable[i+1] = AllocatorTable[i];
111 j = i;
112 while (j > 0 && AllocatorTable[j-1].count < temp.count)
113 j--;
114 AllocatorTable[j] = temp;
115 }
116 }
117
118 /* Print the worst offenders... */
119 DbgPrint("Worst GDI Handle leak offenders (out of %i unique locations):\n", nTraces);
120 for (i = 0; i < nTraces && AllocatorTable[i].count > 1; i++)
121 {
122 /* Print out the allocation count */
123 DbgPrint(" %i allocs, type = 0x%lx:\n",
124 AllocatorTable[i].count,
125 GdiHandleTable->Entries[AllocatorTable[i].idx].Type);
126
127 /* Dump the frames */
128 KeRosDumpStackFrames(GDIHandleAllocator[AllocatorTable[i].idx], GDI_STACK_LEVELS);
129 //KeRosDumpStackFrames(GDIHandleShareLocker[AllocatorTable[i].idx], GDI_STACK_LEVELS);
130
131 /* Print new line for better readability */
132 DbgPrint("\n");
133 }
134
135 if (i < nTraces)
136 DbgPrint("(list terminated - the remaining entries have 1 allocation only)\n");
137
138 KeLowerIrql(OldIrql);
139
140 ASSERT(FALSE);
141 }
142
143 ULONG
144 CaptureStackBackTace(PVOID* pFrames, ULONG nFramesToCapture)
145 {
146 ULONG nFrameCount;
147
148 memset(pFrames, 0x00, (nFramesToCapture + 1) * sizeof(PVOID));
149
150 nFrameCount = RtlWalkFrameChain(pFrames, nFramesToCapture, 0);
151
152 if (nFrameCount < nFramesToCapture)
153 {
154 nFrameCount += RtlWalkFrameChain(pFrames + nFrameCount,
155 nFramesToCapture - nFrameCount,
156 1);
157 }
158
159 return nFrameCount;
160 }
161
162 BOOL
163 GdiDbgHTIntegrityCheck()
164 {
165 ULONG i, nDeleted = 0, nFree = 0, nUsed = 0;
166 PGDI_TABLE_ENTRY pEntry;
167 BOOL r = 1;
168
169 KeEnterCriticalRegion();
170
171 /* FIXME: check reserved entries */
172
173 /* Now go through the deleted objects */
174 i = GdiHandleTable->FirstFree & 0xffff;
175 while (i)
176 {
177 pEntry = &GdiHandleTable->Entries[i];
178 if (i > GDI_HANDLE_COUNT)
179 {
180 DPRINT1("nDeleted=%ld\n", nDeleted);
181 ASSERT(FALSE);
182 }
183
184 nDeleted++;
185
186 /* Check the entry */
187 if ((pEntry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
188 {
189 r = 0;
190 DPRINT1("Deleted Entry has a type != 0\n");
191 }
192 if ((ULONG_PTR)pEntry->KernelData >= GDI_HANDLE_COUNT)
193 {
194 r = 0;
195 DPRINT1("Deleted entries KernelPointer too big\n");
196 }
197 if (pEntry->UserData != NULL)
198 {
199 r = 0;
200 DPRINT1("Deleted entry has UserData != 0\n");
201 }
202 if (pEntry->ProcessId != 0)
203 {
204 r = 0;
205 DPRINT1("Deleted entry has ProcessId != 0\n");
206 }
207
208 i = (ULONG_PTR)pEntry->KernelData & 0xffff;
209 };
210
211 for (i = GdiHandleTable->FirstUnused;
212 i < GDI_HANDLE_COUNT;
213 i++)
214 {
215 pEntry = &GdiHandleTable->Entries[i];
216
217 if ((pEntry->Type) != 0)
218 {
219 r = 0;
220 DPRINT1("Free Entry has a type != 0\n");
221 }
222 if ((ULONG_PTR)pEntry->KernelData != 0)
223 {
224 r = 0;
225 DPRINT1("Free entries KernelPointer != 0\n");
226 }
227 if (pEntry->UserData != NULL)
228 {
229 r = 0;
230 DPRINT1("Free entry has UserData != 0\n");
231 }
232 if (pEntry->ProcessId != 0)
233 {
234 r = 0;
235 DPRINT1("Free entry has ProcessId != 0\n");
236 }
237 nFree++;
238 }
239
240 for (i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++)
241 {
242 HGDIOBJ Handle;
243 ULONG Type;
244
245 pEntry = &GdiHandleTable->Entries[i];
246 Type = pEntry->Type;
247 Handle = (HGDIOBJ)((Type << GDI_ENTRY_UPPER_SHIFT) + i);
248
249 if (Type & GDI_ENTRY_BASETYPE_MASK)
250 {
251 if (pEntry->KernelData == NULL)
252 {
253 r = 0;
254 DPRINT1("Used entry has KernelData == 0\n");
255 }
256 if (pEntry->KernelData <= MmHighestUserAddress)
257 {
258 r = 0;
259 DPRINT1("Used entry invalid KernelData\n");
260 }
261 if (((POBJ)(pEntry->KernelData))->hHmgr != Handle)
262 {
263 r = 0;
264 DPRINT1("Used entry %ld, has invalid hHmg %p (expected: %p)\n",
265 i, ((POBJ)(pEntry->KernelData))->hHmgr, Handle);
266 }
267 nUsed++;
268 }
269 }
270
271 if (RESERVE_ENTRIES_COUNT + nDeleted + nFree + nUsed != GDI_HANDLE_COUNT)
272 {
273 r = 0;
274 DPRINT1("Number of all entries incorrect: RESERVE_ENTRIES_COUNT = %ld, nDeleted = %ld, nFree = %ld, nUsed = %ld\n",
275 RESERVE_ENTRIES_COUNT, nDeleted, nFree, nUsed);
276 }
277
278 KeLeaveCriticalRegion();
279
280 return r;
281 }
282
283 ULONG
284 FASTCALL
285 GDIOBJ_IncrementShareCount(POBJ Object)
286 {
287 INT cLocks = InterlockedIncrement((PLONG)&Object->ulShareCount);
288 GDIDBG_CAPTURESHARELOCKER(Object->hHmgr);
289 ASSERT(cLocks >= 1);
290 return cLocks;
291 }
292
293 #endif /* GDI_DEBUG */
294
295 void
296 GdiDbgDumpLockedHandles()
297 {
298 ULONG i;
299
300 for (i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++)
301 {
302 PGDI_TABLE_ENTRY pEntry = &GdiHandleTable->Entries[i];
303
304 if (pEntry->Type & GDI_ENTRY_BASETYPE_MASK)
305 {
306 BASEOBJECT *pObject = pEntry->KernelData;
307 if (pObject->cExclusiveLock > 0)
308 {
309 DPRINT1("Locked object: %lx, type = %lx. allocated from:\n",
310 i, pEntry->Type);
311 GDIDBG_TRACEALLOCATOR(i);
312 DPRINT1("Locked from:\n");
313 GDIDBG_TRACELOCKER(i);
314 }
315 }
316 }
317 }
318
319 void
320 NTAPI
321 DbgPreServiceHook(ULONG ulSyscallId, PULONG_PTR pulArguments)
322 {
323 PTHREADINFO pti = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
324 if (pti && pti->cExclusiveLocks != 0)
325 {
326 DbgPrint("FATAL: Win32DbgPreServiceHook(%ld): There are %ld exclusive locks!\n",
327 ulSyscallId, pti->cExclusiveLocks);
328 GdiDbgDumpLockedHandles();
329 ASSERT(FALSE);
330 }
331
332 }
333
334 ULONG_PTR
335 NTAPI
336 DbgPostServiceHook(ULONG ulSyscallId, ULONG_PTR ulResult)
337 {
338 PTHREADINFO pti = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
339 if (pti && pti->cExclusiveLocks != 0)
340 {
341 DbgPrint("FATAL: Win32DbgPostServiceHook(%ld): There are %ld exclusive locks!\n",
342 ulSyscallId, pti->cExclusiveLocks);
343 GdiDbgDumpLockedHandles();
344 ASSERT(FALSE);
345 }
346 return ulResult;
347 }
348