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