[CMAKE]
[reactos.git] / 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 extern ULONG gulFirstFree;
16 extern ULONG gulFirstUnused;
17
18 ULONG gulDebugChannels = 0;
19 ULONG gulLogUnique = 0;
20
21 #ifdef GDI_DEBUG
22 #if 0
23 static
24 BOOL
25 CompareBacktraces(ULONG idx1, ULONG idx2)
26 {
27 ULONG iLevel;
28
29 /* Loop all stack levels */
30 for (iLevel = 0; iLevel < GDI_STACK_LEVELS; iLevel++)
31 {
32 if (GDIHandleAllocator[idx1][iLevel]
33 != GDIHandleAllocator[idx2][iLevel])
34 // if (GDIHandleShareLocker[idx1][iLevel]
35 // != GDIHandleShareLocker[idx2][iLevel])
36 {
37 return FALSE;
38 }
39 }
40
41 return TRUE;
42 }
43
44 VOID
45 NTAPI
46 DbgDumpGdiHandleTable(void)
47 {
48 static int leak_reported = 0;
49 int i, j, idx, nTraces = 0;
50 KIRQL OldIrql;
51
52 if (leak_reported)
53 {
54 DPRINT1("gdi handle abusers already reported!\n");
55 return;
56 }
57
58 leak_reported = 1;
59 DPRINT1("reporting gdi handle abusers:\n");
60
61 /* We've got serious business to do */
62 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
63
64 /* Step through GDI handle table and find out who our culprit is... */
65 for (idx = RESERVE_ENTRIES_COUNT; idx < GDI_HANDLE_COUNT; idx++)
66 {
67 /* If the handle is free, continue */
68 if (!IS_HANDLE_VALID(idx)) continue;
69
70 /* Step through all previous backtraces */
71 for (j = 0; j < nTraces; j++)
72 {
73 /* Check if the backtrace matches */
74 if (CompareBacktraces(idx, AllocatorTable[j].idx))
75 {
76 /* It matches, increment count and break out */
77 AllocatorTable[j].count++;
78 break;
79 }
80 }
81
82 /* Did we find a new backtrace? */
83 if (j == nTraces)
84 {
85 /* Break out, if we reached the maximum */
86 if (nTraces == MAX_BACKTRACES) break;
87
88 /* Initialize this entry */
89 AllocatorTable[j].idx = idx;
90 AllocatorTable[j].count = 1;
91 nTraces++;
92 }
93 }
94
95 /* bubble sort time! weeeeee!! */
96 for (i = 0; i < nTraces-1; i++)
97 {
98 if (AllocatorTable[i].count < AllocatorTable[i+1].count)
99 {
100 struct DbgOpenGDIHandle temp;
101
102 temp = AllocatorTable[i+1];
103 AllocatorTable[i+1] = AllocatorTable[i];
104 j = i;
105 while (j > 0 && AllocatorTable[j-1].count < temp.count)
106 j--;
107 AllocatorTable[j] = temp;
108 }
109 }
110
111 /* Print the worst offenders... */
112 DbgPrint("Worst GDI Handle leak offenders (out of %i unique locations):\n", nTraces);
113 for (i = 0; i < nTraces && AllocatorTable[i].count > 1; i++)
114 {
115 /* Print out the allocation count */
116 DbgPrint(" %i allocs, type = 0x%lx:\n",
117 AllocatorTable[i].count,
118 GdiHandleTable->Entries[AllocatorTable[i].idx].Type);
119
120 /* Dump the frames */
121 KeRosDumpStackFrames(GDIHandleAllocator[AllocatorTable[i].idx], GDI_STACK_LEVELS);
122 //KeRosDumpStackFrames(GDIHandleShareLocker[AllocatorTable[i].idx], GDI_STACK_LEVELS);
123
124 /* Print new line for better readability */
125 DbgPrint("\n");
126 }
127
128 if (i < nTraces)
129 DbgPrint("(list terminated - the remaining entries have 1 allocation only)\n");
130
131 KeLowerIrql(OldIrql);
132
133 ASSERT(FALSE);
134 }
135 #endif
136
137 ULONG
138 NTAPI
139 DbgCaptureStackBackTace(PVOID* pFrames, ULONG nFramesToCapture)
140 {
141 ULONG nFrameCount;
142
143 memset(pFrames, 0x00, (nFramesToCapture + 1) * sizeof(PVOID));
144
145 nFrameCount = RtlWalkFrameChain(pFrames, nFramesToCapture, 0);
146
147 if (nFrameCount < nFramesToCapture)
148 {
149 nFrameCount += RtlWalkFrameChain(pFrames + nFrameCount,
150 nFramesToCapture - nFrameCount,
151 1);
152 }
153
154 return nFrameCount;
155 }
156
157 BOOL
158 NTAPI
159 DbgGdiHTIntegrityCheck()
160 {
161 ULONG i, nDeleted = 0, nFree = 0, nUsed = 0;
162 PGDI_TABLE_ENTRY pEntry;
163 BOOL r = 1;
164
165 KeEnterCriticalRegion();
166
167 /* FIXME: check reserved entries */
168
169 /* Now go through the deleted objects */
170 i = gulFirstFree & 0xffff;
171 while (i)
172 {
173 pEntry = &GdiHandleTable->Entries[i];
174 if (i > GDI_HANDLE_COUNT)
175 {
176 DPRINT1("nDeleted=%ld\n", nDeleted);
177 ASSERT(FALSE);
178 }
179
180 nDeleted++;
181
182 /* Check the entry */
183 if ((pEntry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
184 {
185 r = 0;
186 DPRINT1("Deleted Entry has a type != 0\n");
187 }
188 if ((ULONG_PTR)pEntry->KernelData >= GDI_HANDLE_COUNT)
189 {
190 r = 0;
191 DPRINT1("Deleted entries KernelPointer too big\n");
192 }
193 if (pEntry->UserData != NULL)
194 {
195 r = 0;
196 DPRINT1("Deleted entry has UserData != 0\n");
197 }
198 if (pEntry->ProcessId != 0)
199 {
200 r = 0;
201 DPRINT1("Deleted entry has ProcessId != 0\n");
202 }
203
204 i = (ULONG_PTR)pEntry->KernelData & 0xffff;
205 };
206
207 for (i = gulFirstUnused;
208 i < GDI_HANDLE_COUNT;
209 i++)
210 {
211 pEntry = &GdiHandleTable->Entries[i];
212
213 if ((pEntry->Type) != 0)
214 {
215 r = 0;
216 DPRINT1("Free Entry has a type != 0\n");
217 }
218 if ((ULONG_PTR)pEntry->KernelData != 0)
219 {
220 r = 0;
221 DPRINT1("Free entries KernelPointer != 0\n");
222 }
223 if (pEntry->UserData != NULL)
224 {
225 r = 0;
226 DPRINT1("Free entry has UserData != 0\n");
227 }
228 if (pEntry->ProcessId != 0)
229 {
230 r = 0;
231 DPRINT1("Free entry has ProcessId != 0\n");
232 }
233 nFree++;
234 }
235
236 for (i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++)
237 {
238 HGDIOBJ Handle;
239 ULONG Type;
240
241 pEntry = &GdiHandleTable->Entries[i];
242 Type = pEntry->Type;
243 Handle = (HGDIOBJ)((Type << GDI_ENTRY_UPPER_SHIFT) + i);
244
245 if (Type & GDI_ENTRY_BASETYPE_MASK)
246 {
247 if (pEntry->KernelData == NULL)
248 {
249 r = 0;
250 DPRINT1("Used entry has KernelData == 0\n");
251 }
252 if (pEntry->KernelData <= MmHighestUserAddress)
253 {
254 r = 0;
255 DPRINT1("Used entry invalid KernelData\n");
256 }
257 if (((POBJ)(pEntry->KernelData))->hHmgr != Handle)
258 {
259 r = 0;
260 DPRINT1("Used entry %ld, has invalid hHmg %p (expected: %p)\n",
261 i, ((POBJ)(pEntry->KernelData))->hHmgr, Handle);
262 }
263 nUsed++;
264 }
265 }
266
267 if (RESERVE_ENTRIES_COUNT + nDeleted + nFree + nUsed != GDI_HANDLE_COUNT)
268 {
269 r = 0;
270 DPRINT1("Number of all entries incorrect: RESERVE_ENTRIES_COUNT = %ld, nDeleted = %ld, nFree = %ld, nUsed = %ld\n",
271 RESERVE_ENTRIES_COUNT, nDeleted, nFree, nUsed);
272 }
273
274 KeLeaveCriticalRegion();
275
276 return r;
277 }
278
279 #endif /* GDI_DEBUG */
280
281 VOID
282 NTAPI
283 DbgDumpLockedGdiHandles()
284 {
285 #if 0
286 ULONG i;
287
288 for (i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++)
289 {
290 PENTRY pentry = &gpentHmgr[i];
291
292 if (pentry->Objt)
293 {
294 POBJ pobj = pentry->einfo.pobj;
295 if (pobj->cExclusiveLock > 0)
296 {
297 DPRINT1("Locked object: %lx, type = %lx. allocated from:\n",
298 i, pentry->Objt);
299 DBG_DUMP_EVENT_LIST(&pobj->slhLog);
300 }
301 }
302 }
303 #endif
304 }
305
306 VOID
307 NTAPI
308 DbgLogEvent(PSLIST_HEADER pslh, EVENT_TYPE nEventType, LPARAM lParam)
309 {
310 PLOGENTRY pLogEntry;
311
312 /* Log a maximum of 100 events */
313 if (QueryDepthSList(pslh) >= 1000) return;
314
315 /* Allocate a logentry */
316 pLogEntry = EngAllocMem(0, sizeof(LOGENTRY), 'golG');
317 if (!pLogEntry) return;
318
319 /* Set type */
320 pLogEntry->nEventType = nEventType;
321 pLogEntry->ulUnique = InterlockedIncrement((LONG*)&gulLogUnique);
322 pLogEntry->dwProcessId = HandleToUlong(PsGetCurrentProcessId());
323 pLogEntry->dwThreadId = HandleToUlong(PsGetCurrentThreadId());
324 pLogEntry->lParam = lParam;
325
326 /* Capture a backtrace */
327 DbgCaptureStackBackTace(pLogEntry->apvBackTrace, 20);
328
329 switch (nEventType)
330 {
331 case EVENT_ALLOCATE:
332 case EVENT_CREATE_HANDLE:
333 case EVENT_REFERENCE:
334 case EVENT_DEREFERENCE:
335 case EVENT_LOCK:
336 case EVENT_UNLOCK:
337 case EVENT_DELETE:
338 case EVENT_FREE:
339 case EVENT_SET_OWNER:
340 default:
341 break;
342 }
343
344 /* Push it on the list */
345 InterlockedPushEntrySList(pslh, &pLogEntry->sleLink);
346 }
347
348 #define REL_ADDR(va) ((ULONG_PTR)va - (ULONG_PTR)&__ImageBase)
349
350 VOID
351 DbgPrintEvent(PLOGENTRY pLogEntry)
352 {
353 PSTR pstr;
354
355 switch (pLogEntry->nEventType)
356 {
357 case EVENT_ALLOCATE: pstr = "Allocate"; break;
358 case EVENT_CREATE_HANDLE: pstr = "CreatHdl"; break;
359 case EVENT_REFERENCE: pstr = "Ref"; break;
360 case EVENT_DEREFERENCE: pstr = "Deref"; break;
361 case EVENT_LOCK: pstr = "Lock"; break;
362 case EVENT_UNLOCK: pstr = "Unlock"; break;
363 case EVENT_DELETE: pstr = "Delete"; break;
364 case EVENT_FREE: pstr = "Free"; break;
365 case EVENT_SET_OWNER: pstr = "SetOwner"; break;
366 default: pstr = "Unknown"; break;
367 }
368
369 DbgPrint("[%ld] %03x:%03x %.8s val=%p <%lx,%lx,%lx,%lx>\n",
370 pLogEntry->ulUnique,
371 pLogEntry->dwProcessId,
372 pLogEntry->dwThreadId,
373 pstr,
374 pLogEntry->lParam,
375 REL_ADDR(pLogEntry->apvBackTrace[2]),
376 REL_ADDR(pLogEntry->apvBackTrace[3]),
377 REL_ADDR(pLogEntry->apvBackTrace[4]),
378 REL_ADDR(pLogEntry->apvBackTrace[5]));
379 }
380
381 VOID
382 NTAPI
383 DbgDumpEventList(PSLIST_HEADER pslh)
384 {
385 PSLIST_ENTRY psle;
386 PLOGENTRY pLogEntry;
387
388 while ((psle = InterlockedPopEntrySList(pslh)))
389 {
390 pLogEntry = CONTAINING_RECORD(psle, LOGENTRY, sleLink);
391 DbgPrintEvent(pLogEntry);
392 }
393
394 }
395
396 VOID
397 NTAPI
398 DbgCleanupEventList(PSLIST_HEADER pslh)
399 {
400 PSLIST_ENTRY psle;
401 PLOGENTRY pLogEntry;
402
403 while ((psle = InterlockedPopEntrySList(pslh)))
404 {
405 pLogEntry = CONTAINING_RECORD(psle, LOGENTRY, sleLink);
406 EngFreeMem(pLogEntry);
407 }
408 }
409
410 void
411 NTAPI
412 DbgPreServiceHook(ULONG ulSyscallId, PULONG_PTR pulArguments)
413 {
414 PTHREADINFO pti = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
415 if (pti && pti->cExclusiveLocks != 0)
416 {
417 DbgPrint("FATAL: Win32DbgPreServiceHook(0x%lx): There are %ld exclusive locks!\n",
418 ulSyscallId, pti->cExclusiveLocks);
419 DbgDumpLockedGdiHandles();
420 ASSERT(FALSE);
421 }
422
423 }
424
425 ULONG_PTR
426 NTAPI
427 DbgPostServiceHook(ULONG ulSyscallId, ULONG_PTR ulResult)
428 {
429 PTHREADINFO pti = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
430 if (pti && pti->cExclusiveLocks != 0)
431 {
432 DbgPrint("FATAL: Win32DbgPostServiceHook(0x%lx): There are %ld exclusive locks!\n",
433 ulSyscallId, pti->cExclusiveLocks);
434 DbgDumpLockedGdiHandles();
435 ASSERT(FALSE);
436 }
437 return ulResult;
438 }
439