Commit the stuff. Lots of changes. I'll write a proper list of changes when it's...
[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 static int leak_reported = 0;
7 #define GDI_STACK_LEVELS 12
8 ULONG GDIHandleAllocator[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
9 ULONG GDIHandleLocker[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
10 ULONG GDIHandleShareLocker[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
11 ULONG GDIHandleDeleter[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
12 struct DbgOpenGDIHandle
13 {
14 ULONG idx;
15 int count;
16 };
17 #define H 1024
18 static struct DbgOpenGDIHandle h[H];
19
20 void IntDumpHandleTable(PGDI_HANDLE_TABLE HandleTable)
21 {
22 int i, n = 0, j, k, J;
23
24 if (leak_reported)
25 {
26 DPRINT1("gdi handle abusers already reported!\n");
27 return;
28 }
29
30 leak_reported = 1;
31 DPRINT1("reporting gdi handle abusers:\n");
32
33 /* step through GDI handle table and find out who our culprit is... */
34 for (i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++)
35 {
36 for (j = 0; j < n; j++)
37 {
38 next:
39 J = h[j].idx;
40 for (k = 0; k < GDI_STACK_LEVELS; k++)
41 {
42 if (GDIHandleAllocator[i][k]
43 != GDIHandleAllocator[J][k])
44 {
45 if (++j == n)
46 goto done;
47 else
48 goto next;
49 }
50 }
51 goto done;
52 }
53 done:
54 if (j < H)
55 {
56 if (j == n)
57 {
58 h[j].idx = i;
59 h[j].count = 1;
60 n = n + 1;
61 }
62 else
63 h[j].count++;
64 }
65 }
66 /* bubble sort time! weeeeee!! */
67 for (i = 0; i < n-1; i++)
68 {
69 if (h[i].count < h[i+1].count)
70 {
71 struct DbgOpenGDIHandle t;
72 t = h[i+1];
73 h[i+1] = h[i];
74 j = i;
75 while (j > 0 && h[j-1].count < t.count)
76 j--;
77 h[j] = t;
78 }
79 }
80 /* print the worst offenders... */
81 DbgPrint("Worst GDI Handle leak offenders (out of %i unique locations):\n", n);
82 for (i = 0; i < n && h[i].count > 1; i++)
83 {
84 /* Print out the allocation count */
85 DbgPrint(" %i allocs: ", h[i].count);
86
87 /* Dump the frames */
88 KeRosDumpStackFrames(GDIHandleAllocator[h[i].idx], GDI_STACK_LEVELS);
89
90 /* Print new line for better readability */
91 DbgPrint("\n");
92 }
93 if (i < n && h[i].count == 1)
94 DbgPrint("(list terminated - the remaining entries have 1 allocation only)\n");
95 }
96
97 ULONG
98 CaptureStackBackTace(PVOID* pFrames, ULONG nFramesToCapture)
99 {
100 ULONG nFrameCount;
101
102 memset(pFrames, 0x00, (nFramesToCapture + 1) * sizeof(PVOID));
103
104 nFrameCount = RtlCaptureStackBackTrace(1, nFramesToCapture, pFrames, NULL);
105
106 if (nFrameCount < nFramesToCapture)
107 {
108 nFrameCount += RtlWalkFrameChain(pFrames + nFrameCount, nFramesToCapture - nFrameCount, 1);
109 }
110
111 return nFrameCount;
112 }
113
114 BOOL
115 GdiDbgHTIntegrityCheck()
116 {
117 ULONG i, nDeleted = 0, nFree = 0, nUsed = 0;
118 PGDI_TABLE_ENTRY pEntry;
119 BOOL r = 1;
120
121 KeEnterCriticalRegion();
122
123 /* FIXME: check reserved entries */
124
125 /* Now go through the deleted objects */
126 i = GdiHandleTable->FirstFree;
127 if (i)
128 {
129 pEntry = &GdiHandleTable->Entries[i];
130 for (;;)
131 {
132 nDeleted++;
133
134 /* Check the entry */
135 if ((pEntry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
136 {
137 r = 0;
138 DPRINT1("Deleted Entry has a type != 0\n");
139 }
140 if ((ULONG_PTR)pEntry->KernelData >= GDI_HANDLE_COUNT)
141 {
142 r = 0;
143 DPRINT1("Deleted entries KernelPointer too big\n");
144 }
145 if (pEntry->UserData != NULL)
146 {
147 r = 0;
148 DPRINT1("Deleted entry has UserData != 0\n");
149 }
150 if (pEntry->ProcessId != 0)
151 {
152 r = 0;
153 DPRINT1("Deleted entry has ProcessId != 0\n");
154 }
155
156 i = (ULONG_PTR)pEntry->KernelData;
157 if (!i)
158 {
159 break;
160 }
161 pEntry = &GdiHandleTable->Entries[i];
162 }
163 }
164
165 for (i = GdiHandleTable->FirstUnused;
166 i < GDI_HANDLE_COUNT;
167 i++)
168 {
169 pEntry = &GdiHandleTable->Entries[i];
170
171 if ((pEntry->Type) != 0)
172 {
173 r = 0;
174 DPRINT1("Free Entry has a type != 0\n");
175 }
176 if ((ULONG_PTR)pEntry->KernelData != 0)
177 {
178 r = 0;
179 DPRINT1("Free entries KernelPointer != 0\n");
180 }
181 if (pEntry->UserData != NULL)
182 {
183 r = 0;
184 DPRINT1("Free entry has UserData != 0\n");
185 }
186 if (pEntry->ProcessId != 0)
187 {
188 r = 0;
189 DPRINT1("Free entry has ProcessId != 0\n");
190 }
191 nFree++;
192 }
193
194 for (i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++)
195 {
196 HGDIOBJ Handle;
197 ULONG Type;
198
199 pEntry = &GdiHandleTable->Entries[i];
200 Type = pEntry->Type;
201 Handle = (HGDIOBJ)((Type << GDI_ENTRY_UPPER_SHIFT) + i);
202
203 if (Type & GDI_ENTRY_BASETYPE_MASK)
204 {
205 if (pEntry->KernelData == NULL)
206 {
207 r = 0;
208 DPRINT1("Used entry has KernelData == 0\n");
209 }
210 if (pEntry->KernelData <= MmHighestUserAddress)
211 {
212 r = 0;
213 DPRINT1("Used entry invalid KernelData\n");
214 }
215 if (((POBJ)(pEntry->KernelData))->hHmgr != Handle)
216 {
217 r = 0;
218 DPRINT1("Used entry %ld, has invalid hHmg %p (expected: %p)\n",
219 i, ((POBJ)(pEntry->KernelData))->hHmgr, Handle);
220 }
221 nUsed++;
222 }
223 }
224
225 if (RESERVE_ENTRIES_COUNT + nDeleted + nFree + nUsed != GDI_HANDLE_COUNT)
226 {
227 r = 0;
228 DPRINT1("Number of all entries incorrect: RESERVE_ENTRIES_COUNT = %ld, nDeleted = %ld, nFree = %ld, nUsed = %ld\n",
229 RESERVE_ENTRIES_COUNT, nDeleted, nFree, nUsed);
230 }
231
232 KeLeaveCriticalRegion();
233
234 return r;
235 }
236
237 #define GDIDBG_TRACECALLER() \
238 DPRINT1("-> called from:\n"); \
239 KeRosDumpStackFrames(NULL, 20);
240 #define GDIDBG_TRACEALLOCATOR(handle) \
241 DPRINT1("-> allocated from:\n"); \
242 KeRosDumpStackFrames(GDIHandleAllocator[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
243 #define GDIDBG_TRACELOCKER(handle) \
244 DPRINT1("-> locked from:\n"); \
245 KeRosDumpStackFrames(GDIHandleLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
246 #define GDIDBG_TRACESHARELOCKER(handle) \
247 DPRINT1("-> locked from:\n"); \
248 KeRosDumpStackFrames(GDIHandleShareLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
249 #define GDIDBG_TRACEDELETER(handle) \
250 DPRINT1("-> deleted from:\n"); \
251 KeRosDumpStackFrames(GDIHandleDeleter[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
252 #define GDIDBG_CAPTUREALLOCATOR(handle) \
253 CaptureStackBackTace((PVOID*)GDIHandleAllocator[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
254 #define GDIDBG_CAPTURELOCKER(handle) \
255 CaptureStackBackTace((PVOID*)GDIHandleLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
256 #define GDIDBG_CAPTURESHARELOCKER(handle) \
257 CaptureStackBackTace((PVOID*)GDIHandleShareLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
258 #define GDIDBG_CAPTUREDELETER(handle) \
259 CaptureStackBackTace((PVOID*)GDIHandleDeleter[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
260 #define GDIDBG_DUMPHANDLETABLE() \
261 IntDumpHandleTable(GdiHandleTable)
262 #define GDIDBG_INITLOOPTRACE() \
263 ULONG Attempts = 0;
264 #define GDIDBG_TRACELOOP(Handle, PrevThread, Thread) \
265 if ((++Attempts % 20) == 0) \
266 { \
267 DPRINT1("[%d] Handle 0x%p Locked by 0x%x (we're 0x%x)\n", Attempts, Handle, PrevThread, Thread); \
268 }
269
270 ULONG
271 FASTCALL
272 GDIOBJ_IncrementShareCount(POBJ Object)
273 {
274 INT cLocks = InterlockedIncrement((PLONG)&Object->ulShareCount);
275 GDIDBG_CAPTURESHARELOCKER(Object->hHmgr);
276 ASSERT(cLocks >= 1);
277 return cLocks;
278 }
279
280 #else
281
282 #define GDIDBG_TRACECALLER()
283 #define GDIDBG_TRACEALLOCATOR(index)
284 #define GDIDBG_TRACELOCKER(index)
285 #define GDIDBG_TRACESHARELOCKER(index)
286 #define GDIDBG_CAPTUREALLOCATOR(index)
287 #define GDIDBG_CAPTURELOCKER(index)
288 #define GDIDBG_CAPTURESHARELOCKER(index)
289 #define GDIDBG_CAPTUREDELETER(handle)
290 #define GDIDBG_DUMPHANDLETABLE()
291 #define GDIDBG_INITLOOPTRACE()
292 #define GDIDBG_TRACELOOP(Handle, PrevThread, Thread)
293 #define GDIDBG_TRACEDELETER(handle)
294
295 #endif /* GDI_DEBUG */
296