Create a branch for working on csrss and co.
[reactos.git] / win32ss / gdi / ntgdi / gdikdbgext.c
1 /*
2 * PROJECT: ReactOS win32 kernel mode subsystem
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: win32ss/gdi/ntgdi/gdikdbgext.x
5 * PURPOSE: KDBG extension 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 PENTRY gpentHmgr;
16 extern PULONG gpaulRefCount;
17 extern PEPROCESS gpepCSRSS;
18 extern ULONG gulFirstUnused;
19
20
21 static const char * gpszObjectTypes[] =
22 {
23 "FREE", "DC", "UNUSED1", "UNUSED2", "RGN", "SURF", "CLIENTOBJ", "PATH",
24 "PAL", "ICMLCS", "LFONT", "RFONT", "PFE", "PFT", "ICMCXF", "SPRITE",
25 "BRUSH", "UMPD", "UNUSED4", "SPACE", "UNUSED5", "META", "EFSTATE",
26 "BMFD", "VTFD", "TTFD", "RC", "TEMP", "DRVOBJ", "DCIOBJ", "SPOOL",
27 "RESERVED", "ALL"
28 };
29
30
31 BOOLEAN
32 KdbIsMemoryValid(PVOID pvBase, ULONG cjSize)
33 {
34 PUCHAR pjAddress;
35
36 pjAddress = ALIGN_DOWN_POINTER_BY(pvBase, PAGE_SIZE);
37
38 while (pjAddress < (PUCHAR)pvBase + cjSize)
39 {
40 if (!MmIsAddressValid(pjAddress)) return FALSE;
41 pjAddress += PAGE_SIZE;
42 }
43
44 return TRUE;
45 }
46
47 static
48 BOOL
49 KdbGetHexNumber(char *pszNum, ULONG_PTR *pulValue)
50 {
51 char *endptr;
52
53 /* Skip optional '0x' prefix */
54 if ((pszNum[0] == '0') && ((pszNum[1] == 'x') || (pszNum[1] == 'X')))
55 pszNum += 2;
56
57 /* Make a number from the string (hex) */
58 *pulValue = strtoul(pszNum, &endptr, 16);
59
60 return (*endptr == '\0');
61 }
62
63 static
64 VOID
65 KdbCommand_Gdi_help(VOID)
66 {
67 DbgPrint("GDI KDBG extension.\nAvailable commands:\n"
68 "- help - Displays this screen.\n"
69 "- dumpht [<type>] - Dumps all handles of <type> or lists all types\n"
70 "- handle <handle> - Displays information about a handle\n"
71 "- entry <entry> - Displays an ENTRY, <entry> can be a pointer or index\n"
72 "- baseobject <object> - Displays a BASEOBJECT\n"
73 #if DBG_ENABLE_EVENT_LOGGING
74 "- eventlist <object> - Displays the eventlist for an object\n"
75 #endif
76 );
77 }
78
79 static
80 VOID
81 KdbCommand_Gdi_dumpht(ULONG argc, char *argv[])
82 {
83 ULONG i;
84 UCHAR Objt, jReqestedType;
85 PENTRY pentry;
86 POBJ pobj;
87 KAPC_STATE ApcState;
88 ULONG_PTR ulArg;
89
90 /* No CSRSS, no handle table */
91 if (!gpepCSRSS) return;
92 KeStackAttachProcess(&gpepCSRSS->Pcb, &ApcState);
93
94 if (argc == 0)
95 {
96 USHORT Counts[GDIObjType_MAX_TYPE + 2] = {0};
97
98 /* Loop all possibly used entries in the handle table */
99 for (i = RESERVE_ENTRIES_COUNT; i < gulFirstUnused; i++)
100 {
101 if (KdbIsMemoryValid(&gpentHmgr[i], sizeof(ENTRY)))
102 {
103 Objt = gpentHmgr[i].Objt & 0x1F;
104 Counts[Objt]++;
105 }
106 }
107
108 DbgPrint("Type Count\n");
109 DbgPrint("-------------------\n");
110 for (i = 0; i <= GDIObjType_MAX_TYPE; i++)
111 {
112 DbgPrint("%02x %-9s %d\n",
113 i, gpszObjectTypes[i], Counts[i]);
114 }
115 DbgPrint("\n");
116 }
117 else
118 {
119 /* Loop all object types */
120 for (i = 0; i <= GDIObjType_MAX_TYPE + 1; i++)
121 {
122 /* Check if this object type was requested */
123 if (stricmp(argv[0], gpszObjectTypes[i]) == 0) break;
124 }
125
126 /* Check if we didn't find it yet */
127 if (i > GDIObjType_MAX_TYPE + 1)
128 {
129 /* Try if it's a number */
130 if (!KdbGetHexNumber(argv[0], &ulArg))
131 {
132 DbgPrint("Invalid parameter: %s\n", argv[0]);
133 return;
134 }
135
136 /* Check if it's inside the allowed range */
137 if (i > GDIObjType_MAX_TYPE)
138 {
139 DbgPrint("Unknown object type: %s\n", argv[0]);
140 goto leave;
141 }
142 }
143
144 jReqestedType = i;
145
146 /* Print header */
147 DbgPrint("Index Handle Type pObject ThreadId cLocks ulRefCount\n");
148 DbgPrint("---------------------------------------------------------------\n");
149
150 /* Loop all possibly used entries in the handle table */
151 for (i = RESERVE_ENTRIES_COUNT; i < gulFirstUnused; i++)
152 {
153 /* Get the entry and the object */
154 pentry = &gpentHmgr[i];
155
156 if (!MmIsAddressValid(pentry)) continue;
157
158 pobj = pentry->einfo.pobj;
159 Objt = pentry->Objt & 0x1F;
160
161 /* Check if ALL objects are requested, or the object type matches */
162 if ((jReqestedType == GDIObjType_MAX_TYPE + 1) ||
163 (Objt == jReqestedType))
164 {
165 DbgPrint("%04lx %p %-9s 0x%p 0x%06lx %-6ld ",
166 i, pobj->hHmgr, gpszObjectTypes[Objt], pobj,
167 pobj->dwThreadId, pobj->cExclusiveLock);
168 if (MmIsAddressValid(&gpaulRefCount[i]))
169 DbgPrint("0x%08lx\n", gpaulRefCount[i]);
170 else
171 DbgPrint("??????????\n");
172 }
173 }
174 }
175
176 leave:
177 KeUnstackDetachProcess(&ApcState);
178 }
179
180 static
181 VOID
182 KdbCommand_Gdi_handle(char *argv)
183 {
184 ULONG_PTR ulObject;
185 BASEOBJECT *pobj;
186 ENTRY *pentry;
187 USHORT usIndex;
188 KAPC_STATE ApcState;
189
190 /* Convert the parameter into a number */
191 if (!KdbGetHexNumber(argv, &ulObject))
192 {
193 DbgPrint("Invalid parameter: %s\n", argv);
194 return;
195 }
196
197 /* No CSRSS, no handle table */
198 if (!gpepCSRSS) return;
199 KeStackAttachProcess(&gpepCSRSS->Pcb, &ApcState);
200
201 usIndex = ulObject & 0xFFFF;
202 pentry = &gpentHmgr[usIndex];
203
204 if (MmIsAddressValid(pentry))
205 {
206 pobj = pentry->einfo.pobj;
207
208 DbgPrint("GDI handle=%p, type=%s, index=0x%lx, pentry=%p.\n",
209 ulObject, gpszObjectTypes[(ulObject >> 16) & 0x1f],
210 usIndex, pentry);
211 DbgPrint(" ENTRY = {.pobj = %p, ObjectOwner = 0x%lx, FullUnique = 0x%04x,\n"
212 " Objt=0x%02x, Flags = 0x%02x, pUser = 0x%p}\n",
213 pentry->einfo.pobj, pentry->ObjectOwner.ulObj, pentry->FullUnique,
214 pentry->Objt, pentry->Flags, pentry->pUser);
215 DbgPrint(" BASEOBJECT = {hHmgr = %p, dwThreadId = 0x%lx,\n"
216 " cExclusiveLock = %ld, BaseFlags = 0x%lx}\n",
217 pobj->hHmgr, pobj->dwThreadId,
218 pobj->cExclusiveLock, pobj->BaseFlags);
219 if (MmIsAddressValid(&gpaulRefCount[usIndex]))
220 DbgPrint(" gpaulRefCount[idx] = %ld\n", gpaulRefCount[usIndex]);
221 }
222 else
223 {
224 DbgPrint("Coudn't access ENTRY. Probably paged out.\n");
225 }
226
227 KeUnstackDetachProcess(&ApcState);
228 }
229
230 static
231 VOID
232 KdbCommand_Gdi_entry(char *argv)
233 {
234 ULONG_PTR ulValue;
235 PENTRY pentry;
236 KAPC_STATE ApcState;
237
238 /* Convert the parameter into a number */
239 if (!KdbGetHexNumber(argv, &ulValue))
240 {
241 DbgPrint("Invalid parameter: %s\n", argv);
242 return;
243 }
244
245 /* No CSRSS, no handle table */
246 if (!gpepCSRSS) return;
247 KeStackAttachProcess(&gpepCSRSS->Pcb, &ApcState);
248
249 /* If the parameter is smaller than 0x10000, it's an index */
250 pentry = (ulValue <= 0xFFFF) ? &gpentHmgr[ulValue] : (PENTRY)ulValue;
251
252 /* Check if the address is readable */
253 if (!MmIsAddressValid(pentry))
254 {
255 DbgPrint("Cannot access entry at %p\n", pentry);
256 goto cleanup;
257 }
258
259 /* print the entry */
260 DbgPrint("Dumping ENTRY #%ld, @%p:\n", (pentry - gpentHmgr), pentry);
261 if (pentry->Objt != 0)
262 DbgPrint(" pobj = 0x%p\n", pentry->einfo.pobj);
263 else
264 DbgPrint(" hFree = 0x%p\n", pentry->einfo.hFree);
265 DbgPrint(" ObjectOwner = 0x%p\n", pentry->ObjectOwner.ulObj);
266 DbgPrint(" FullUnique = 0x%x\n", pentry->FullUnique);
267 DbgPrint(" Objt = 0x%x (%s)\n", pentry->Objt,
268 pentry->Objt <= 0x1E ? gpszObjectTypes[pentry->Objt] : "invalid");
269 DbgPrint(" Flags = 0x%x\n", pentry->Flags);
270 DbgPrint(" pUser = 0x%p\n", pentry->pUser);
271
272 cleanup:
273 KeUnstackDetachProcess(&ApcState);
274 }
275
276 static
277 VOID
278 KdbCommand_Gdi_baseobject(char *argv)
279 {
280 }
281
282 #if DBG_ENABLE_EVENT_LOGGING
283 static
284 VOID
285 KdbCommand_Gdi_eventlist(char *argv)
286 {
287 ULONG_PTR ulValue;
288 POBJ pobj;
289 PSLIST_ENTRY psle, psleFirst;
290 PLOGENTRY pLogEntry;
291
292 /* Convert the parameter into a number */
293 if (!KdbGetHexNumber(argv, &ulValue))
294 {
295 DbgPrint("Invalid parameter: %s\n", argv);
296 return;
297 }
298
299 pobj = (POBJ)ulValue;
300
301 /* Check if the address is readable */
302 if (!KdbIsMemoryValid(pobj, sizeof(BASEOBJECT)))
303 {
304 DbgPrint("Cannot access BASEOBJECT at %p\n", pobj);
305 return;
306 }
307
308 /* The kernel doesn't export RtlFirstEntrySList :( */
309 psleFirst = InterlockedFlushSList(&pobj->slhLog);
310
311 /* Loop all events, but don't remove them */
312 for (psle = psleFirst; psle != NULL; psle = psle->Next)
313 {
314 pLogEntry = CONTAINING_RECORD(psle, LOGENTRY, sleLink);
315 DbgPrintEvent(pLogEntry);
316 }
317
318 /* Put the log back in place */
319 InterlockedPushEntrySList(&pobj->slhLog, psleFirst);
320 }
321 #endif
322
323 BOOLEAN
324 NTAPI
325 DbgGdiKdbgCliCallback(
326 IN PCHAR pszCommand,
327 IN ULONG argc,
328 IN PCH argv[])
329 {
330
331 if (stricmp(argv[0], "!gdi.help") == 0)
332 {
333 KdbCommand_Gdi_help();
334 }
335 else if (stricmp(argv[0], "!gdi.dumpht") == 0)
336 {
337 KdbCommand_Gdi_dumpht(argc - 1, argv + 1);
338 }
339 else if (stricmp(argv[0], "!gdi.handle") == 0)
340 {
341 KdbCommand_Gdi_handle(argv[1]);
342 }
343 else if (stricmp(argv[0], "!gdi.entry") == 0)
344 {
345 KdbCommand_Gdi_entry(argv[1]);
346 }
347 else if (stricmp(argv[0], "!gdi.baseobject") == 0)
348 {
349 KdbCommand_Gdi_baseobject(argv[1]);
350 }
351 #if DBG_ENABLE_EVENT_LOGGING
352 else if (stricmp(argv[0], "!gdi.eventlist") == 0)
353 {
354 KdbCommand_Gdi_eventlist(argv[1]);
355 }
356 #endif
357 else
358 {
359 /* Not handled */
360 return FALSE;
361 }
362
363 return TRUE;
364 }
365
366
367
368
369