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