[WIN32K]
[reactos.git] / subsystems / win32 / win32k / objects / gdidbg.c
1 #ifdef GDI_DEBUG
2
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);
5
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
12 {
13 ULONG idx;
14 int count;
15 };
16 #define MAX_BACKTRACES 1024
17 static struct DbgOpenGDIHandle AllocatorTable[MAX_BACKTRACES];
18
19 static
20 BOOL
21 CompareBacktraces(ULONG idx1, ULONG idx2)
22 {
23 ULONG iLevel;
24
25 /* Loop all stack levels */
26 for (iLevel = 0; iLevel < GDI_STACK_LEVELS; iLevel++)
27 {
28 if (GDIHandleAllocator[idx1][iLevel]
29 != GDIHandleAllocator[idx2][iLevel])
30 // if (GDIHandleShareLocker[idx1][iLevel]
31 // != GDIHandleShareLocker[idx2][iLevel])
32 {
33 return FALSE;
34 }
35 }
36
37 return TRUE;
38 }
39
40 #define IS_HANDLE_VALID(idx) \
41 ((GdiHandleTable->Entries[idx].Type & GDI_ENTRY_BASETYPE_MASK) != 0)
42
43 void IntDumpHandleTable(PGDI_HANDLE_TABLE HandleTable)
44 {
45 static int leak_reported = 0;
46 int i, j, idx, nTraces = 0;
47 KIRQL OldIrql;
48
49 if (leak_reported)
50 {
51 DPRINT1("gdi handle abusers already reported!\n");
52 return;
53 }
54
55 leak_reported = 1;
56 DPRINT1("reporting gdi handle abusers:\n");
57
58 /* We've got serious business to do */
59 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
60
61 /* Step through GDI handle table and find out who our culprit is... */
62 for (idx = RESERVE_ENTRIES_COUNT; idx < GDI_HANDLE_COUNT; idx++)
63 {
64 /* If the handle is free, continue */
65 if (!IS_HANDLE_VALID(idx)) continue;
66
67 /* Step through all previous backtraces */
68 for (j = 0; j < nTraces; j++)
69 {
70 /* Check if the backtrace matches */
71 if (CompareBacktraces(idx, AllocatorTable[j].idx))
72 {
73 /* It matches, increment count and break out */
74 AllocatorTable[j].count++;
75 break;
76 }
77 }
78
79 /* Did we find a new backtrace? */
80 if (j == nTraces)
81 {
82 /* Break out, if we reached the maximum */
83 if (nTraces == MAX_BACKTRACES) break;
84
85 /* Initialize this entry */
86 AllocatorTable[j].idx = idx;
87 AllocatorTable[j].count = 1;
88 nTraces++;
89 }
90 }
91
92 /* bubble sort time! weeeeee!! */
93 for (i = 0; i < nTraces-1; i++)
94 {
95 if (AllocatorTable[i].count < AllocatorTable[i+1].count)
96 {
97 struct DbgOpenGDIHandle temp;
98
99 temp = AllocatorTable[i+1];
100 AllocatorTable[i+1] = AllocatorTable[i];
101 j = i;
102 while (j > 0 && AllocatorTable[j-1].count < temp.count)
103 j--;
104 AllocatorTable[j] = temp;
105 }
106 }
107
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++)
111 {
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);
116
117 /* Dump the frames */
118 KeRosDumpStackFrames(GDIHandleAllocator[AllocatorTable[i].idx], GDI_STACK_LEVELS);
119 //KeRosDumpStackFrames(GDIHandleShareLocker[AllocatorTable[i].idx], GDI_STACK_LEVELS);
120
121 /* Print new line for better readability */
122 DbgPrint("\n");
123 }
124
125 if (i < nTraces)
126 DbgPrint("(list terminated - the remaining entries have 1 allocation only)\n");
127
128 KeLowerIrql(OldIrql);
129
130 ASSERT(FALSE);
131 }
132
133 ULONG
134 CaptureStackBackTace(PVOID* pFrames, ULONG nFramesToCapture)
135 {
136 ULONG nFrameCount;
137
138 memset(pFrames, 0x00, (nFramesToCapture + 1) * sizeof(PVOID));
139
140 nFrameCount = RtlWalkFrameChain(pFrames, nFramesToCapture, 0);
141
142 if (nFrameCount < nFramesToCapture)
143 {
144 nFrameCount += RtlWalkFrameChain(pFrames + nFrameCount,
145 nFramesToCapture - nFrameCount,
146 1);
147 }
148
149 return nFrameCount;
150 }
151
152 BOOL
153 GdiDbgHTIntegrityCheck()
154 {
155 ULONG i, nDeleted = 0, nFree = 0, nUsed = 0;
156 PGDI_TABLE_ENTRY pEntry;
157 BOOL r = 1;
158
159 KeEnterCriticalRegion();
160
161 /* FIXME: check reserved entries */
162
163 /* Now go through the deleted objects */
164 i = GdiHandleTable->FirstFree;
165 if (i)
166 {
167 pEntry = &GdiHandleTable->Entries[i];
168 for (;;)
169 {
170 nDeleted++;
171
172 /* Check the entry */
173 if ((pEntry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
174 {
175 r = 0;
176 DPRINT1("Deleted Entry has a type != 0\n");
177 }
178 if ((ULONG_PTR)pEntry->KernelData >= GDI_HANDLE_COUNT)
179 {
180 r = 0;
181 DPRINT1("Deleted entries KernelPointer too big\n");
182 }
183 if (pEntry->UserData != NULL)
184 {
185 r = 0;
186 DPRINT1("Deleted entry has UserData != 0\n");
187 }
188 if (pEntry->ProcessId != 0)
189 {
190 r = 0;
191 DPRINT1("Deleted entry has ProcessId != 0\n");
192 }
193
194 i = (ULONG_PTR)pEntry->KernelData;
195 if (!i)
196 {
197 break;
198 }
199 pEntry = &GdiHandleTable->Entries[i];
200 }
201 }
202
203 for (i = GdiHandleTable->FirstUnused;
204 i < GDI_HANDLE_COUNT;
205 i++)
206 {
207 pEntry = &GdiHandleTable->Entries[i];
208
209 if ((pEntry->Type) != 0)
210 {
211 r = 0;
212 DPRINT1("Free Entry has a type != 0\n");
213 }
214 if ((ULONG_PTR)pEntry->KernelData != 0)
215 {
216 r = 0;
217 DPRINT1("Free entries KernelPointer != 0\n");
218 }
219 if (pEntry->UserData != NULL)
220 {
221 r = 0;
222 DPRINT1("Free entry has UserData != 0\n");
223 }
224 if (pEntry->ProcessId != 0)
225 {
226 r = 0;
227 DPRINT1("Free entry has ProcessId != 0\n");
228 }
229 nFree++;
230 }
231
232 for (i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++)
233 {
234 HGDIOBJ Handle;
235 ULONG Type;
236
237 pEntry = &GdiHandleTable->Entries[i];
238 Type = pEntry->Type;
239 Handle = (HGDIOBJ)((Type << GDI_ENTRY_UPPER_SHIFT) + i);
240
241 if (Type & GDI_ENTRY_BASETYPE_MASK)
242 {
243 if (pEntry->KernelData == NULL)
244 {
245 r = 0;
246 DPRINT1("Used entry has KernelData == 0\n");
247 }
248 if (pEntry->KernelData <= MmHighestUserAddress)
249 {
250 r = 0;
251 DPRINT1("Used entry invalid KernelData\n");
252 }
253 if (((POBJ)(pEntry->KernelData))->hHmgr != Handle)
254 {
255 r = 0;
256 DPRINT1("Used entry %ld, has invalid hHmg %p (expected: %p)\n",
257 i, ((POBJ)(pEntry->KernelData))->hHmgr, Handle);
258 }
259 nUsed++;
260 }
261 }
262
263 if (RESERVE_ENTRIES_COUNT + nDeleted + nFree + nUsed != GDI_HANDLE_COUNT)
264 {
265 r = 0;
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);
268 }
269
270 KeLeaveCriticalRegion();
271
272 return r;
273 }
274
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() \
301 ULONG Attempts = 0;
302 #define GDIDBG_TRACELOOP(Handle, PrevThread, Thread) \
303 if ((++Attempts % 20) == 0) \
304 { \
305 DPRINT1("[%d] Handle 0x%p Locked by 0x%x (we're 0x%x)\n", Attempts, Handle, PrevThread, Thread); \
306 }
307
308 ULONG
309 FASTCALL
310 GDIOBJ_IncrementShareCount(POBJ Object)
311 {
312 INT cLocks = InterlockedIncrement((PLONG)&Object->ulShareCount);
313 GDIDBG_CAPTURESHARELOCKER(Object->hHmgr);
314 ASSERT(cLocks >= 1);
315 return cLocks;
316 }
317
318 #else
319
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)
332
333 #endif /* GDI_DEBUG */
334