/*++ Copyright (c) 2000 Microsoft Corporation Module Name: extapi.cxx Abstract: This file contains the generic routines and initialization code used by the debugger extensions routines. Author: Jason Hartman (JasonHa) 2000-08-18 Environment: User Mode --*/ #include "precomp.hxx" const BOOL ClientInitialized = FALSE; PDEBUG_ADVANCED g_pExtAdvanced; PDEBUG_CLIENT g_pExtClient; PDEBUG_CONTROL g_pExtControl; PDEBUG_DATA_SPACES g_pExtData; PDEBUG_REGISTERS g_pExtRegisters; PDEBUG_SYMBOLS g_pExtSymbols; PDEBUG_SYMBOL_GROUP g_pExtSymbolGroup; PDEBUG_SYSTEM_OBJECTS g_pExtSystem; #define REF_LIMIT 100 LONG ExtRefCount = 0; BOOL ExtReady = FALSE; #define MAX_NAME 2048 DefOutputCallbacks *g_pDefOutputCallbacks = NULL; // Queries for all debugger interfaces. extern "C" HRESULT ExtQuery(PDEBUG_CLIENT Client) { HRESULT Hr; LONG RefCheck; // Have to have a client to start with;) if (Client == NULL) { return S_FALSE; } RefCheck = InterlockedIncrement(&ExtRefCount); if (RefCheck > REF_LIMIT) { DbgPrint("ExtQuery has calls exceeding limit.\n"); InterlockedDecrement(&ExtRefCount); return S_FALSE; } if (RefCheck > 1) { // Wait until original refencer completes setup. // // If ExtRefCount drops below RefCheck then the // original referencer failed as well as any // waiters who started after us. while (!ExtReady && ExtRefCount >= RefCheck) Sleep(10); if (ExtReady) { // Make sure the clients match if (g_pExtClient != Client) { InterlockedDecrement(&ExtRefCount); return S_FALSE; } return S_OK; } } // Prepare to query interfaces. // Make sure no one is currently cleaning up. while (ExtReady) { Sleep(10); } if ((Hr = Client->QueryInterface(__uuidof(IDebugAdvanced), (void **)&g_pExtAdvanced)) != S_OK) { goto Fail; } if ((Hr = Client->QueryInterface(__uuidof(IDebugControl), (void **)&g_pExtControl)) != S_OK) { goto Fail; } if ((Hr = Client->QueryInterface(__uuidof(IDebugDataSpaces), (void **)&g_pExtData)) != S_OK) { goto Fail; } if ((Hr = Client->QueryInterface(__uuidof(IDebugRegisters), (void **)&g_pExtRegisters)) != S_OK) { goto Fail; } if ((Hr = Client->QueryInterface(__uuidof(IDebugSymbols), (void **)&g_pExtSymbols)) != S_OK) { goto Fail; } if ((Hr = g_pExtSymbols->CreateSymbolGroup(&g_pExtSymbolGroup)) != S_OK) { goto Fail; } if ((Hr = Client->QueryInterface(__uuidof(IDebugSystemObjects), (void **)&g_pExtSystem)) != S_OK) { goto Fail; } // If symbols state has changed, make sure GDI symbols are loaded. if (gbSymbolsNotLoaded) { SymbolLoad(Client); } g_pExtClient = Client; ExtReady = TRUE; return S_OK; Fail: ExtRelease(TRUE); InterlockedDecrement(&ExtRefCount); return Hr; } // Cleans up all debugger interfaces when there are no more references. // A cleanup will be forced if Cleanup is TRUE. void ExtRelease(BOOL Cleanup) { // Don't decrement the count when forcing cleanup. if (Cleanup || InterlockedDecrement(&ExtRefCount) < 1) { DbgPrint("Cleaning up interfaces.\n"); EXT_RELEASE(g_pExtAdvanced); EXT_RELEASE(g_pExtControl); EXT_RELEASE(g_pExtData); EXT_RELEASE(g_pExtRegisters); EXT_RELEASE(g_pExtSymbols); EXT_RELEASE(g_pExtSystem); g_pExtClient = NULL; ExtReady = FALSE; } return; } // Normal output. void __cdecl ExtOut(PCSTR Format, ...) { va_list Args; if (g_pExtControl == NULL) { DbgPrint("g_pExtControl is NULL.\n"); return; } va_start(Args, Format); g_pExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args); va_end(Args); } // Error output. void __cdecl ExtErr(PCSTR Format, ...) { va_list Args; if (g_pExtControl == NULL) { DbgPrint("g_pExtControl is NULL.\n"); return; } va_start(Args, Format); g_pExtControl->OutputVaList(DEBUG_OUTPUT_ERROR, Format, Args); va_end(Args); } // Warning output. void __cdecl ExtWarn(PCSTR Format, ...) { va_list Args; if (g_pExtControl == NULL) { DbgPrint("g_pExtControl is NULL.\n"); return; } va_start(Args, Format); g_pExtControl->OutputVaList(DEBUG_OUTPUT_WARNING, Format, Args); va_end(Args); } // Verbose output. void __cdecl ExtVerb(PCSTR Format, ...) { va_list Args; if (g_pExtControl == NULL) { DbgPrint("g_pExtControl is NULL.\n"); return; } va_start(Args, Format); g_pExtControl->OutputVaList(DEBUG_OUTPUT_VERBOSE, Format, Args); va_end(Args); } ExtApiClass::ExtApiClass( PDEBUG_CLIENT DbgClient ) { Client = DbgClient ? DbgClient : g_pExtClient; if (Client != NULL) { Client->AddRef(); } else { if (GetDebugClient(&Client) != S_OK) { DbgPrint("Error: Client creation failed.\n"); return; } } if (ExtQuery(Client) != S_OK) { DbgPrint("Error: Interface queries failed.\n"); EXT_RELEASE(Client); } } ExtApiClass::~ExtApiClass() { if (Client) { ExtRelease(); EXT_RELEASE(Client); } } HRESULT ReadSymbolData( IN PDEBUG_CLIENT Client, IN PCSTR Symbol, OUT PVOID Buffer, IN ULONG BufferSize, OUT OPTIONAL PULONG SizeRead ) { HRESULT hr; ULONG64 Module; ULONG64 Offset; ULONG TypeId; ULONG TypeSize; if (Buffer != NULL) { RtlZeroMemory(Buffer, BufferSize); } if (SizeRead != NULL) { *SizeRead = 0; } if (Client == NULL) { return E_POINTER; } OutputControl OutCtl(Client); PDEBUG_SYMBOLS Symbols; if ((hr = Client->QueryInterface(__uuidof(IDebugSymbols), (void **)&Symbols)) != S_OK) { return hr; } if ((hr = Symbols->GetOffsetByName(Symbol, &Offset)) == S_OK) { if ((hr = Symbols->GetSymbolTypeId(Symbol, &TypeId, &Module)) == S_OK && (hr = Symbols->GetTypeSize(Module, TypeId, &TypeSize)) == S_OK) { BufferSize = min(BufferSize, TypeSize); if (SessionId == CURRENT_SESSION) { hr = Symbols->ReadTypedDataVirtual(Offset, Module, TypeId, Buffer, BufferSize, SizeRead); } else { ULONG64 OffsetPhys; if ((hr = GetPhysicalAddress(Client, SessionId, Offset, &OffsetPhys)) == S_OK) { hr = Symbols->ReadTypedDataPhysical(OffsetPhys, Module, TypeId, Buffer, BufferSize, SizeRead); } } } else { OutCtl.OutErr("Couldn't get type info for %s; result 0x%lx.\n", Symbol, hr); } } else { OutCtl.OutErr("Couldn't get offset of %s; result 0x%lx.\n", Symbol, hr); } Symbols->Release(); return hr; } const CHAR szNULL[] = "(null)"; DEBUG_VALUE DbgValNULL = { 0, DEBUG_VALUE_INT64 }; HRESULT Evaluate( IN PDEBUG_CLIENT Client, IN PCSTR Expression, IN ULONG DesiredType, IN ULONG Radix, OUT PDEBUG_VALUE Value, OUT OPTIONAL PULONG RemainderIndex, OUT OPTIONAL PULONG StartIndex, OUT OPTIONAL FLONG Flags ) { HRESULT hr = S_FALSE; PDEBUG_CONTROL Control; PSTR pStr; BOOL FoundNULL = FALSE; ULONG OrgRadix; CHAR EvalBuffer[128]; ULONG EvalLen; if (RemainderIndex != NULL) *RemainderIndex = 0; if (StartIndex != NULL) *StartIndex = 0; if (Expression == NULL || Client == NULL || (hr = Client->QueryInterface(__uuidof(IDebugControl), (void **)&Control)) != S_OK) { return hr; } pStr = (PSTR)Expression; while (*pStr != '\n' && (isspace(*pStr) || (*pStr != '-' && ispunct(*pStr)))) { if (_strnicmp(pStr, szNULL, sizeof(szNULL)-1) == 0) { FoundNULL = TRUE; break; } pStr++; } if (FoundNULL) { hr = Control->CoerceValue(&DbgValNULL, (DesiredType == DEBUG_VALUE_INVALID) ? DEBUG_VALUE_INT64 : DesiredType, Value); EvalLen = sizeof(szNULL)-1; } else { // Find expression string and only text revalent // to evalutating that expression. // // Otherwise IDebugControl::Evaluate will spend // too much time looking up values that are not // really part of the expression. // // IDebugControl::Evaluate also doesn't handle // binary strings well. We expect binary strings // to be followed by a non-binary value enclosed // in parenthesis. Just use that value. char *psz; int i = 0; while (pStr[i] != '\0' && (pStr[i] == '0' || pStr[i] == '1')) { i++; } if (i && pStr[i] == ' ' && pStr[i+1] == '(' && isdigit(pStr[i+2])) { pStr += i + 1; } psz = pStr; i = 0; if (Flags & EVALUATE_COMPACT_EXPR) { while ((i < sizeof(EvalBuffer)-1) && *psz != '\0' && !isspace(*psz)) { EvalBuffer[i++] = *psz++; } } else { do { while ((i < sizeof(EvalBuffer)-1) && *psz != '\0' && !isspace(*psz)) { EvalBuffer[i++] = *psz++; } while ((i < sizeof(EvalBuffer)-1) && (*psz == ' ' || *psz == '\t')) { EvalBuffer[i++] = *psz++; } } while ((i < sizeof(EvalBuffer)-1) && (ispunct(*psz) && *psz != '-' && *psz != '_' && !(psz[0] == '-' && psz[1] == '>'))); // Remove any trailing whitespace while (i > 0 && isspace(EvalBuffer[i-1])) i--; } EvalBuffer[i] = '\0'; if (Radix == 0 || ((hr = Control->GetRadix(&OrgRadix)) == S_OK && (hr = Control->SetRadix(Radix)) == S_OK) ) { // DbgPrint("Calling Eval(%s) --\n", EvalBuffer); hr = Control->Evaluate(EvalBuffer, DesiredType, Value, &EvalLen); // DbgPrint("-- Eval returned\n"); if (Radix != 0) { Control->SetRadix(OrgRadix); } if (hr == S_OK && Flags & EVALUATE_COMPACT_EXPR && EvalLen != i) { hr = S_FALSE; } } else { DbgPrint("Can't setup new radix, %lu, for Evaluate.\n", Radix); } } Control->Release(); if (hr == S_OK) { if (RemainderIndex != NULL) { *RemainderIndex = (ULONG)(pStr - Expression) + EvalLen; } if (StartIndex != NULL) { *StartIndex = (ULONG)(pStr - Expression); } } return hr; } HRESULT ReadPointerPhysical( PDEBUG_CLIENT Client, ULONG64 Offset, PULONG64 Ptr ) { HRESULT hr; PDEBUG_CONTROL Control; PDEBUG_DATA_SPACES Data; DEBUG_VALUE PtrVal; ULONG BytesRead; if (Ptr != NULL) *Ptr = 0; if (Client == NULL) return E_INVALIDARG; if ((hr = Client->QueryInterface(__uuidof(IDebugControl), (void **)&Control)) != S_OK) { return hr; } if ((hr = Client->QueryInterface(__uuidof(IDebugDataSpaces), (void **)&Data)) != S_OK) { Client->Release(); return hr; } if (Control->IsPointer64Bit() == S_OK) { Control->Output(DEBUG_OUTPUT_VERBOSE, "Read64PointerPhysical(0x%p)\n", Offset); hr = Data->ReadPhysical(Offset, &PtrVal.I64, sizeof(PtrVal.I64), &BytesRead); if (hr == S_OK && BytesRead != sizeof(PtrVal.I64)) { Control->Output(DEBUG_OUTPUT_VERBOSE, "ReadPhysicalPointer only read %lu bytes not %lu.\n", BytesRead, sizeof(PtrVal.I64)); hr = S_FALSE; } if (hr == S_OK) Control->Output(DEBUG_OUTPUT_VERBOSE, " read 0x%p\n", PtrVal.I64); } else { Control->Output(DEBUG_OUTPUT_VERBOSE, "Read32PointerPhysical(0x%p)\n", Offset); hr = Data->ReadPhysical(Offset, &PtrVal.I32, sizeof(PtrVal.I32), &BytesRead); if (hr == S_OK) { if (BytesRead != sizeof(PtrVal.I32)) { Control->Output(DEBUG_OUTPUT_VERBOSE, "ReadPhysicalPointer only read %lu bytes not %lu.\n", BytesRead, sizeof(PtrVal.I32)); hr = S_FALSE; } else { Control->Output(DEBUG_OUTPUT_VERBOSE, " read 0x%p", PtrVal.I64); PtrVal.I64 = DEBUG_EXTEND64(PtrVal.I32); Control->Output(DEBUG_OUTPUT_VERBOSE, " -> 0x%I64x\n", PtrVal.I64); } } } if (hr == S_OK && Ptr != NULL) { *Ptr = PtrVal.I64; } Data->Release(); Control->Release(); return hr; } HRESULT GetTypeId( IN PDEBUG_CLIENT Client, IN PCSTR Type, OUT PULONG TypeId, OUT OPTIONAL PULONG64 Module ) { HRESULT hr; PDEBUG_SYMBOLS Symbols; if (Client == NULL || Type == NULL || TypeId == NULL) { return E_INVALIDARG; } if ((hr = Client->QueryInterface(__uuidof(IDebugSymbols), (void **)&Symbols)) != S_OK) { return hr; } if (strchr(Type, '!') == NULL && Type_Module.Base != 0 && (hr = Symbols->GetTypeId(Type_Module.Base, Type, TypeId)) == S_OK) { if (Module != NULL) { *Module = Type_Module.Base; } } else { hr = Symbols->GetSymbolTypeId(Type, TypeId, Module); } Symbols->Release(); return hr; } HRESULT GetBasicTypeSize( IN PCSTR Type, OUT PULONG Size ) { HRESULT hr; ULONG Bytes = 0; static CHAR PointerBase[] = "Ptr"; static CHAR Char[] = "Char"; static CHAR IntBase[] = "Int"; if (_strnicmp(Type, PointerBase, sizeof(PointerBase)) == 0) { Bytes = strtoul(Type+sizeof(PointerBase), NULL, 10) / 8; } else { // Remove U indicating unsigned if (*Type == 'U') { Type++; } if (_strnicmp(Type, Char, sizeof(Char)) == 0) { Bytes = 1; } else if (_strnicmp(Type, IntBase, sizeof(IntBase)) == 0) { PCHAR NextChar; Bytes = strtoul(Type+sizeof(IntBase), &NextChar, 10); if (NextChar == NULL || toupper(*NextChar) != 'B') { Bytes = 0; } } } if (Bytes != 0) { hr = S_OK; if (Size != NULL) { *Size = Bytes; } } else { hr = S_FALSE; } return hr; } HRESULT GetFieldSize( IN PDEBUG_CLIENT Client, IN ULONG64 Module, IN ULONG TypeId, IN PCSTR FieldPath, OUT PULONG pSize, OUT OPTIONAL PULONG pLength, OUT OPTIONAL PULONG pEntrySize ) { HRESULT hr; PDEBUG_CONTROL Control; PDEBUG_SYMBOLS Symbols; if (pSize != NULL) *pSize = 0; if (pLength != NULL) *pLength = 0; if (pEntrySize != NULL) *pEntrySize = 0; if (Client == NULL || FieldPath == NULL || !iscsymf(*FieldPath)) { return E_INVALIDARG; } if ((hr = Client->QueryInterface(__uuidof(IDebugControl), (void **)&Control)) != S_OK) { return hr; } if ((hr = Client->QueryInterface(__uuidof(IDebugSymbols), (void **)&Symbols)) != S_OK) { Client->Release(); return hr; } OutputReader OutReader; OutputState OutState(Client, FALSE); PSTR TypeLayout; PSTR Field; SIZE_T FieldLen; CHAR FieldCopy[80]; PCSTR SubFieldPath; ULONG Size, ArrayLen = 0; if ((hr = OutState.Setup(0, &OutReader)) == S_OK && (hr = OutState.OutputTypeVirtual(0, Module, TypeId, DEBUG_OUTTYPE_NO_INDENT | DEBUG_OUTTYPE_NO_OFFSET | DEBUG_OUTTYPE_COMPACT_OUTPUT)) == S_OK && (hr = OutReader.GetOutputCopy(&TypeLayout)) == S_OK) { SubFieldPath = strchr(FieldPath, '.'); if (SubFieldPath != NULL) { FieldLen = SubFieldPath - FieldPath - 1; SubFieldPath++; if (FieldLen + 1 > sizeof(FieldCopy)) { Field = (PSTR)HeapAlloc(GetProcessHeap(), 0, FieldLen + 1); if (Field == NULL) { hr = E_OUTOFMEMORY; } } else { Field = FieldCopy; } if (hr == S_OK) { RtlCopyMemory(Field, FieldPath, FieldLen); Field[FieldLen] = '\0'; } } else { Field = (PSTR)FieldPath; } if (hr == S_OK) { PSTR pStr = TypeLayout; BOOL FieldFound = FALSE; while (!FieldFound && pStr != NULL) { pStr = strstr(pStr, Field); if (pStr != NULL) { // Check Field is bounded by non-symbol characters BOOL FieldStart = (pStr-1 < TypeLayout) || (!__iscsym(*(pStr-1))); // Advance search location pStr += strlen(Field); if (FieldStart && !__iscsym(*pStr)) { FieldFound = TRUE; } } } if (FieldFound) { while (isspace(*pStr)) pStr++; // Check for an array if (*pStr == '[') { PCHAR EvalEnd; ArrayLen = strtoul(pStr+1, &EvalEnd, 10); if (ArrayLen != 0 && *EvalEnd == ']') { pStr = EvalEnd + 1; while (isspace(*pStr)) pStr++; } // else following csym check will fail returning error. } if (iscsymf(*pStr)) { PSTR FieldType = pStr; ULONG SubTypeId; while (iscsym(*pStr)) pStr++; *pStr = '\0'; hr = Symbols->GetTypeId(Module, FieldType, &SubTypeId); if (SubFieldPath != NULL) { if (hr == S_OK) { hr = GetFieldSize(Client, Module, SubTypeId, SubFieldPath, pSize, pLength, pEntrySize); } } else { if (hr == S_OK) { hr = Symbols->GetTypeSize(Module, SubTypeId, &Size); } else { if (GetBasicTypeSize(FieldType, &Size) == S_OK) { hr = S_OK; } } if (hr == S_OK) { if (pEntrySize != NULL) *pEntrySize = Size; if (pLength != NULL) *pLength = ArrayLen; if (pSize != NULL) { if (ArrayLen != 0) Size *= ArrayLen; *pSize = Size; } } } } else { hr = S_FALSE; } } } if (Field != NULL && Field != FieldCopy && Field != FieldPath) { HeapFree(GetProcessHeap(), 0, Field); } OutReader.FreeOutputCopy(TypeLayout); } Symbols->Release(); Control->Release(); return hr; } ULONG DbgIntValTypeFromSize( ULONG Size ) { ULONG Type; switch (Size) { case 1: Type = DEBUG_VALUE_INT8; break; case 2: Type = DEBUG_VALUE_INT16; break; case 4: Type = DEBUG_VALUE_INT32; break; case 8: Type = DEBUG_VALUE_INT64; break; default: Type = DEBUG_VALUE_INVALID; break; } return Type; } BOOL GetArrayDimensions( IN PDEBUG_CLIENT Client, IN PCSTR Type, OPTIONAL IN PCSTR Field, OPTIONAL OUT PULONG ArraySize, OPTIONAL OUT PULONG ArrayLength, OPTIONAL OUT PULONG EntrySize ) { HRESULT hr; ULONG64 Module; ULONG TypeId; BOOL GotDimensions = FALSE; ULONG Size, ESize; if (ArraySize) *ArraySize = 0; if (ArrayLength) *ArrayLength = 0; if (EntrySize) *EntrySize = 0; if (Type == NULL) { return FALSE; } if (Field != NULL) { hr = GetTypeId(Client, Type, &TypeId, &Module); if (hr == S_OK) { hr = GetFieldSize(Client, Module, TypeId, Field, ArraySize, ArrayLength, EntrySize); if (hr == S_OK) GotDimensions = TRUE; } } if (!GotDimensions && ExtQuery(Client) == S_OK) { ULONG GrpIndex; CHAR SymbolName[MAX_PATH]; DEBUG_SYMBOL_PARAMETERS Array; _snprintf(SymbolName, sizeof(SymbolName), (Field) ? "%s.%s" : Type, Type, Field); if (g_pExtSymbolGroup->AddSymbol(SymbolName, &GrpIndex) == S_OK) { if (g_pExtSymbolGroup->GetSymbolParameters(GrpIndex, 1, &Array) == S_OK) { if (Array.SubElements) { if (g_pExtSymbols->GetTypeSize(Array.Module, Array.TypeId, &Size) == S_OK) { ExtVerb(" Array %s - Size: %u bytes Length: %u\n", SymbolName, Size, Array.SubElements); if (ArraySize) *ArraySize = Size; if (ArrayLength) *ArrayLength = Array.SubElements; if (EntrySize) *EntrySize = Size / Array.SubElements; GotDimensions = TRUE; } else { ExtErr("Couldn't get size of %s.\n", Type); } } else { ExtErr("%s has 0 subelements.\n", SymbolName); } } else { ExtErr("Couldn't get parameter info for %s.\n", SymbolName); } g_pExtSymbolGroup->RemoveSymbolByIndex(GrpIndex); } else { ExtErr("Couldn't lookup symbol %s.\n", SymbolName); } ExtRelease(); } if (!GotDimensions) { ULONG Index = -1; PCSTR ArrayName = Field ? Field : Type; char FirstEntryName[128]; ULONG error; FIELD_INFO EntrySizeField = { DbgStr(Field), NULL, 0, DBG_DUMP_FIELD_FULL_NAME, 0, NULL}; SYM_DUMP_PARAM ArraySym = { sizeof (SYM_DUMP_PARAM), DbgStr(Type), DBG_DUMP_NO_PRINT, 0, NULL, NULL, NULL, (Field ? 1 : 0), &EntrySizeField }; ExtVerb("Using WinDbg extension interface.\n"); if (Field) { error = Ioctl(IG_DUMP_SYMBOL_INFO, &ArraySym, ArraySym.size); Size = (error) ? 0 : EntrySizeField.size; EntrySizeField.fName = DbgStr(FirstEntryName); } else { Size = Ioctl(IG_GET_TYPE_SIZE, &ArraySym, ArraySym.size); ArraySym.sName = DbgStr(FirstEntryName); } if (Size == 0) { dprintf("Array size is zero.\n"); return FALSE; } if (ArraySize) *ArraySize = Size; _snprintf(FirstEntryName, sizeof(FirstEntryName), "%s[0]", ArrayName); if (Field) { error = Ioctl(IG_DUMP_SYMBOL_INFO, &ArraySym, ArraySym.size); ESize = (error) ? 0 : EntrySizeField.size; vPrintNativeSymDumpParam(&ArraySym); } else { ESize = Ioctl(IG_GET_TYPE_SIZE, &ArraySym, ArraySym.size); } if (ESize) { DbgPrint("%s dimensions: %u bytes [%u] = %u bytes.\n", ArrayName, ESize, Size/ESize, Size); if (ArrayLength) *ArrayLength = Size/ESize; if (EntrySize) *EntrySize = ESize; GotDimensions = TRUE; } } return GotDimensions; } HRESULT DumpType( PDEBUG_CLIENT Client, PCSTR Type, ULONG64 Offset, ULONG Flags, OutputControl *OutCtl, BOOL Physical ) { HRESULT hr; PDEBUG_SYMBOLS Symbols; ULONG64 Module; ULONG TypeId; OutputControl DefaultOutput; if (Client == NULL) return E_INVALIDARG; if (OutCtl == NULL) { DefaultOutput.SetControl(DEBUG_OUTCTL_AMBIENT, Client); OutCtl = &DefaultOutput; } if ((hr = GetTypeId(Client, Type, &TypeId, &Module)) != S_OK) { OutCtl->OutErr(" Not a type nor symbol - HRESULT %s.\n", pszHRESULT(hr)); } else { TypeOutputDumper TypeReader(Client, OutCtl); if (!(Flags & DEBUG_OUTTYPE_NO_OFFSET)) { OutCtl->Output(" %s", Type); if (Offset != 0) OutCtl->Output(" @ %s0x%p", ((Physical) ? "#" : ""), Offset); OutCtl->Output((Flags & DEBUG_OUTTYPE_COMPACT_OUTPUT) ? " " : ":\n"); } hr = TypeReader.OutputType(Physical, Module, TypeId, Offset, Flags, NULL); } return hr; } HRESULT ExtDumpType( IN PDEBUG_CLIENT Client, IN PCSTR ExtName, IN PCSTR Type, IN PCSTR Args ) { INIT_API(); HRESULT hr = S_OK; DEBUG_VALUE Offset; Offset.I64 = 0; while (isspace(*Args)) Args++; if (*Args == '-' || (hr = Evaluate(Client, Args, DEBUG_VALUE_INT64, 0, &Offset, NULL)) != S_OK) { if (hr != S_OK) { ExtErr("Evaluate '%s' returned %s.\n", Args, pszHRESULT(hr)); } ExtOut("Usage: %s [-?] [%s Addr]\n", ExtName, Type); } else { hr = DumpType(Client, Type, Offset.I64); if (hr != S_OK) { ExtErr("Type Dump for %s returned %s.\n", Type, pszHRESULT(hr)); } } EXIT_API(hr); } void DumpDSP(PDEBUG_CLIENT Client, PDEBUG_SYMBOL_PARAMETERS pDSP) { ExtOut(" Module Base : %p\n", pDSP->Module); ExtOut(" TypeId : %lx\n", pDSP->TypeId); ExtOut(" ParentSymbol : %lx\n", pDSP->ParentSymbol); ExtOut(" SubElements : %lu\n", pDSP->SubElements); ExtOut(" Flags : %lx\n", pDSP->Flags); ExtOut(" Reserved : %I64u\n", pDSP->Reserved); } DECLARE_API(dt) { HRESULT Hr = E_INVALIDARG; BOOL VerboseInfo = FALSE; BOOL NotGDIType = FALSE; BOOL AllClients = FALSE; BOOL SessionAddr = FALSE; BOOL Physical = FALSE; char TypeSym[MAX_NAME]; char *pcTypeSym = TypeSym; DEBUG_VALUE Offset; ULONG Index; DEBUG_SYMBOL_PARAMETERS DSP; DEBUG_VALUE RepeatCount = { {1}, DEBUG_VALUE_INVALID}; ULONG Size = 0; INIT_API(); while (isspace(*args)) args++; while (*args == '-') { args++; do { switch (tolower(*args)) { case 'v': VerboseInfo = TRUE; break; case 's': SessionAddr = TRUE; break; case 'n': NotGDIType = TRUE; break; case 'c': AllClients = TRUE; break; case 'l': { ULONG RemIndex; Hr = Evaluate(Client, args+1, DEBUG_VALUE_INT32, 0, &RepeatCount, &RemIndex); if (Hr == S_OK) { if (RepeatCount.Type == DEBUG_VALUE_INT32 && RepeatCount.I32 > 0) { if (RepeatCount.I32 < 512) { args += RemIndex; break; } else { ExtErr("Array count %lu is higher than 512 limit.\n\n", RepeatCount.I32); } } else { ExtErr("Invalid array count at \"%s\"\n\n", args+1); } Hr = E_INVALIDARG; } else { ExtErr("Missing array count.\n\n"); } } default: { ExtOut("dt dumps GDI types expanding enum and flag values.\n" "\n" "Usage: dt [-?vsn] [-l] [[#]Offset]\n" "\n" " -v Verbose type/symbol information\n" " -s Lookup according to !session setting\n" " -n Directly through debug engine\n" " -l Dump an array of Type/Symbol Count times\n"); EXIT_API(*args == '?' ? S_OK : Hr); } } args++; } while (!isspace(*args) && *args != '\0'); while (isspace(*args)) args++; } // Get Type/Symbol name from argument string while (*args != '\0' && !isspace(*args) && (pcTypeSym < (TypeSym + sizeof(TypeSym) - 2))) { *pcTypeSym++ = *args++; } // Type/Symbols should be followed by a space or nothing if (*args != '\0' && !isspace(*args)) { ExtErr("Invalid arguments\n"); EXIT_API(E_INVALIDARG); } *pcTypeSym = '\0'; while (isspace(*args)) { args++; } if (*args == '#') { if (SessionAddr) { ExtErr("-s may not be combined with physical addresses.\n"); EXIT_API(E_INVALIDARG); } Physical = TRUE; args++; } Offset.Type = DEBUG_VALUE_INVALID; Hr = Evaluate(Client, args, DEBUG_VALUE_INT64, 0, &Offset, NULL); if (Physical && Offset.Type == DEBUG_VALUE_INVALID) { ExtErr("Invalid offset\n"); EXIT_API(((Hr != S_OK) ? Hr : E_INVALIDARG)); } if ((Hr = g_pExtSymbolGroup->AddSymbol(TypeSym, &Index)) == S_OK) { if ((Hr = g_pExtSymbolGroup->GetSymbolParameters(Index, 1, &DSP)) == S_OK) { if (VerboseInfo) DumpDSP(Client, &DSP); } else { ExtErr(" GetSymbolParameters returned error %lX.\n", Hr); } g_pExtSymbolGroup->RemoveSymbolByIndex(Index); } else { ExtVerb(" Not a symbol - Symbol lookup returned error %lX.\n", Hr); Hr = GetTypeId(Client, TypeSym, &DSP.TypeId, &DSP.Module); if (Hr == S_OK) { if (VerboseInfo) { ExtOut(" Module Base : %p\n", DSP.Module); ExtOut(" TypeId : %lx\n", DSP.TypeId); } } else { ExtErr(" Not a type/symbol - %s.\n", pszHRESULT(Hr)); } } if (Hr == S_OK && VerboseInfo) { HRESULT HrCur; char ModuleName[40]; Hr = g_pExtSymbols->GetModuleNames(DEBUG_ANY_ID, DSP.Module, NULL, 0, NULL, ModuleName, sizeof(ModuleName), NULL, NULL, 0, NULL); if (Hr == S_OK) { ExtOut(" Module Name : %s\n", ModuleName); } else { ExtErr(" GetModuleNames returned error %lx.\n", Hr); } ExtVerb("GetTypeName(%p, %lx)\n", DSP.Module, DSP.TypeId); char TypeName[MAX_NAME]; HrCur = g_pExtSymbols->GetTypeName(DSP.Module, DSP.TypeId, TypeName, sizeof(TypeName), NULL); if (HrCur == S_OK) { ExtOut(" Type Name : %s\n", TypeName); } else { ExtErr(" GetTypeName returned error %lx.\n", HrCur); if (Hr == S_OK) Hr = HrCur; } } if (Hr == S_OK && ((RepeatCount.I32 != 1) || VerboseInfo)) { Hr = g_pExtSymbols->GetTypeSize(DSP.Module, DSP.TypeId, &Size); if (Hr == S_OK) { if (VerboseInfo) { ExtOut(" Type Size : %lu\n", Size); } if (RepeatCount.I32 != 1 && Size == 0) { ExtErr("Error: GetTypeSize returned size of 0.\n"); Hr = E_FAIL; } } else { ExtErr(" GetTypeSize returned error %lx.\n", Hr); } } if (Hr == S_OK) { // Try to evaluate TypeSym for an offset if none was specified. if (*args == '\0' && (Offset.Type == DEBUG_VALUE_INVALID || (Offset.I64 == 0 && !Physical))) { Offset.Type = DEBUG_VALUE_INVALID; Evaluate(Client, TypeSym, DEBUG_VALUE_INT64, 0, &Offset, NULL); } if (Offset.Type == DEBUG_VALUE_INVALID) { Offset.I64 = 0; } else if (SessionAddr && SessionId != CURRENT_SESSION) { ULONG64 PhysAddr; Hr = GetPhysicalAddress(Client, SessionId, Offset.I64, &PhysAddr); if (Hr == S_OK) { Physical = TRUE; Offset.I64 = PhysAddr; if (RepeatCount.I32 != 1) { ExtWarn("Array dumping is not supported for session dumps.\n"); RepeatCount.I32 = 1; } } else { ExtErr("Couldn't lookup 0x%p in Session %s.\n", Offset.I64, SessionStr); } } if (Hr == S_OK) { if (Offset.I64 == 0 && !Physical && RepeatCount.I32 != 1) { ExtWarn("No valid offset was found so array dump has been overridden.\n"); RepeatCount.I32 = 1; } while (RepeatCount.I32 > 0 && Hr == S_OK) { if ((Offset.I64 == 0 && !Physical) || NotGDIType) { ExtVerb("OutputTypedData(DEBUG_OUTCTL_THIS_CLIENT, 0x%p, %p, %lx, 0)\n", Offset.I64, DSP.Module, DSP.TypeId); if (Physical) { Hr = g_pExtSymbols->OutputTypedDataPhysical((AllClients ? DEBUG_OUTCTL_ALL_CLIENTS : DEBUG_OUTCTL_THIS_CLIENT), Offset.I64, DSP.Module, DSP.TypeId, 0); } else { Hr = g_pExtSymbols->OutputTypedDataVirtual((AllClients ? DEBUG_OUTCTL_ALL_CLIENTS : DEBUG_OUTCTL_THIS_CLIENT), Offset.I64, DSP.Module, DSP.TypeId, 0); } } else { Hr = DumpType(Client, TypeSym, Offset.I64, DEBUG_OUTTYPE_DEFAULT, NULL, Physical); } if (--RepeatCount.I32 > 0) { Offset.I64 += Size; } } if (Hr != S_OK) { ExtErr("Type dump returned %s.\n", pszHRESULT(Hr)); } } } EXIT_API(Hr); }