9118b313571b34c91ce98ea1518c1d38fd57b6ad
[reactos.git] / reactos / 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 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