Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4920 lines
112 KiB

//----------------------------------------------------------------------------
//
// IDebugSymbols implementation.
//
// Copyright (C) Microsoft Corporation, 1999-2002.
//
//----------------------------------------------------------------------------
#include "ntsdp.hpp"
#define DBG_SYMGROUP_ENABLED 0
// Special type status value that maps to E_UNEXPECTED.
#define TYPE_E_UNEXPECTED 0xfefefefe
BOOL
GetAllModuleName(ULONG64 Base,
PCHAR Name,
ULONG SizeOfName)
{
ImageInfo* Image = g_Process->FindImageByOffset(Base, TRUE);
if (Image)
{
return CopyString(Name, Image->m_ModuleName, SizeOfName);
}
return FALSE;
}
HRESULT
ResultFromTypeStatus(ULONG Status)
{
switch(Status)
{
case NO_ERROR:
return S_OK;
case MEMORY_READ_ERROR:
case EXIT_ON_CONTROLC:
return E_FAIL;
case SYMBOL_TYPE_INDEX_NOT_FOUND:
case SYMBOL_TYPE_INFO_NOT_FOUND:
return E_NOINTERFACE;
case FIELDS_DID_NOT_MATCH:
case NULL_SYM_DUMP_PARAM:
case NULL_FIELD_NAME:
case INCORRECT_VERSION_INFO:
return E_INVALIDARG;
case CANNOT_ALLOCATE_MEMORY:
case INSUFFICIENT_SPACE_TO_COPY:
return E_OUTOFMEMORY;
case TYPE_E_UNEXPECTED:
return E_UNEXPECTED;
}
return E_FAIL;
}
STDMETHODIMP
DebugClient::GetSymbolOptions(
THIS_
OUT PULONG Options
)
{
ENTER_ENGINE();
*Options = g_SymOptions;
LEAVE_ENGINE();
return S_OK;
}
#define ALL_SYMBOL_OPTIONS \
(SYMOPT_CASE_INSENSITIVE | \
SYMOPT_UNDNAME | \
SYMOPT_DEFERRED_LOADS | \
SYMOPT_NO_CPP | \
SYMOPT_LOAD_LINES | \
SYMOPT_OMAP_FIND_NEAREST | \
SYMOPT_LOAD_ANYTHING | \
SYMOPT_IGNORE_CVREC | \
SYMOPT_NO_UNQUALIFIED_LOADS | \
SYMOPT_FAIL_CRITICAL_ERRORS | \
SYMOPT_EXACT_SYMBOLS | \
SYMOPT_ALLOW_ABSOLUTE_SYMBOLS | \
SYMOPT_IGNORE_NT_SYMPATH | \
SYMOPT_INCLUDE_32BIT_MODULES | \
SYMOPT_PUBLICS_ONLY | \
SYMOPT_NO_PUBLICS | \
SYMOPT_AUTO_PUBLICS | \
SYMOPT_NO_IMAGE_SEARCH | \
SYMOPT_SECURE | \
SYMOPT_NO_PROMPTS | \
SYMOPT_DEBUG)
STDMETHODIMP
DebugClient::AddSymbolOptions(
THIS_
IN ULONG Options
)
{
HRESULT Status;
if (Options & ~ALL_SYMBOL_OPTIONS)
{
return E_INVALIDARG;
}
ENTER_ENGINE();
Status = SetSymOptions(g_SymOptions | Options);
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::RemoveSymbolOptions(
THIS_
IN ULONG Options
)
{
HRESULT Status;
if (Options & ~ALL_SYMBOL_OPTIONS)
{
return E_INVALIDARG;
}
ENTER_ENGINE();
Status = SetSymOptions(g_SymOptions & ~Options);
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::SetSymbolOptions(
THIS_
IN ULONG Options
)
{
HRESULT Status;
if (Options & ~ALL_SYMBOL_OPTIONS)
{
return E_INVALIDARG;
}
ENTER_ENGINE();
Status = SetSymOptions(Options);
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetNameByOffset(
THIS_
IN ULONG64 Offset,
OUT OPTIONAL PSTR NameBuffer,
IN ULONG NameBufferSize,
OUT OPTIONAL PULONG NameSize,
OUT OPTIONAL PULONG64 Displacement
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
char Sym[MAX_SYMBOL_LEN];
ULONG64 _Disp;
if (GetSymbol(Offset, Sym, DIMA(Sym), &_Disp))
{
Status = FillStringBuffer(Sym, 0, NameBuffer, NameBufferSize,
NameSize);
if (Displacement)
{
*Displacement = _Disp;
}
}
else
{
Status = E_FAIL;
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetOffsetByName(
THIS_
IN PCSTR Symbol,
OUT PULONG64 Offset
)
{
HRESULT Status;
ENTER_ENGINE();
ULONG Count;
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else if (Count = GetOffsetFromSym(g_Process, Symbol, Offset, NULL))
{
Status = Count > 1 ? S_FALSE : S_OK;
}
else
{
Status = E_FAIL;
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetNearNameByOffset(
THIS_
IN ULONG64 Offset,
IN LONG Delta,
OUT OPTIONAL PSTR NameBuffer,
IN ULONG NameBufferSize,
OUT OPTIONAL PULONG NameSize,
OUT OPTIONAL PULONG64 Displacement
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
char Sym[MAX_SYMBOL_LEN];
ULONG64 _Disp;
if (GetNearSymbol(Offset, Sym, sizeof(Sym), &_Disp, Delta))
{
Status = FillStringBuffer(Sym, 0, NameBuffer, NameBufferSize,
NameSize);
if (Displacement)
{
*Displacement = _Disp;
}
}
else
{
Status = E_NOINTERFACE;
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetLineByOffset(
THIS_
IN ULONG64 Offset,
OUT OPTIONAL PULONG Line,
OUT OPTIONAL PSTR FileBuffer,
IN ULONG FileBufferSize,
OUT OPTIONAL PULONG FileSize,
OUT OPTIONAL PULONG64 Displacement
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
IMAGEHLP_LINE64 DbgLine;
ULONG Disp;
if (GetLineFromAddr(g_Process, Offset, &DbgLine, &Disp))
{
if (Line != NULL)
{
*Line = DbgLine.LineNumber;
}
Status = FillStringBuffer(DbgLine.FileName, 0,
FileBuffer, FileBufferSize, FileSize);
if (Displacement != NULL)
{
*Displacement = Disp;
}
}
else
{
Status = E_FAIL;
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetOffsetByLine(
THIS_
IN ULONG Line,
IN PCSTR File,
OUT PULONG64 Offset
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
IMAGEHLP_LINE64 DbgLine;
LONG Disp;
DbgLine.SizeOfStruct = sizeof(DbgLine);
if (SymGetLineFromName64(g_Process->m_SymHandle, NULL, (PSTR)File,
Line, &Disp, &DbgLine))
{
*Offset = DbgLine.Address;
Status = S_OK;
}
else
{
Status = E_FAIL;
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetNumberModules(
THIS_
OUT PULONG Loaded,
OUT PULONG Unloaded
)
{
HRESULT Status;
ENTER_ENGINE();
if (g_Process == NULL)
{
Status = E_UNEXPECTED;
}
else
{
*Loaded = g_Process->m_NumImages;
*Unloaded = g_Process->m_NumUnloadedModules;
Status = S_OK;
}
LEAVE_ENGINE();
return Status;
}
HRESULT
GetUnloadedModuleByIndex(ULONG Index, UnloadedModuleInfo** IterRet,
PSTR Name, PDEBUG_MODULE_PARAMETERS Params)
{
HRESULT Status;
UnloadedModuleInfo* Iter;
if ((Iter = g_Target->GetUnloadedModuleInfo()) == NULL)
{
return E_FAIL;
}
if ((Status = Iter->Initialize(g_Thread)) != S_OK)
{
return Status;
}
do
{
if ((Status = Iter->GetEntry(Name, Params)) != S_OK)
{
if (Status == S_FALSE)
{
return E_INVALIDARG;
}
return Status;
}
} while (Index-- > 0);
if (IterRet != NULL)
{
*IterRet = Iter;
}
return S_OK;
}
STDMETHODIMP
DebugClient::GetModuleByIndex(
THIS_
IN ULONG Index,
OUT PULONG64 Base
)
{
HRESULT Status;
ENTER_ENGINE();
if (g_Process == NULL)
{
Status = E_UNEXPECTED;
}
else if (Index >= g_Process->m_NumImages)
{
DEBUG_MODULE_PARAMETERS Params;
if ((Status = GetUnloadedModuleByIndex
(Index - g_Process->m_NumImages,
NULL, NULL, &Params)) == S_OK)
{
*Base = Params.Base;
}
}
else
{
*Base = g_Process->FindImageByIndex(Index)->m_BaseOfImage;
Status = S_OK;
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetModuleByModuleName(
THIS_
IN PCSTR Name,
IN ULONG StartIndex,
OUT OPTIONAL PULONG Index,
OUT OPTIONAL PULONG64 Base
)
{
HRESULT Status;
ENTER_ENGINE();
if (g_Process == NULL)
{
Status = E_UNEXPECTED;
}
else
{
ULONG Idx = 0;
Status = E_NOINTERFACE;
ImageInfo* Image = g_Process->m_ImageHead;
while (Image != NULL)
{
if (StartIndex == 0 &&
!_strcmpi(Name, Image->m_ModuleName))
{
if (Index != NULL)
{
*Index = Idx;
}
if (Base != NULL)
{
*Base = Image->m_BaseOfImage;
}
Status = S_OK;
break;
}
Image = Image->m_Next;
Idx++;
if (StartIndex > 0)
{
StartIndex--;
}
}
if (Image == NULL)
{
UnloadedModuleInfo* Iter;
char UnlName[MAX_INFO_UNLOADED_NAME];
DEBUG_MODULE_PARAMETERS Params;
Status = GetUnloadedModuleByIndex(StartIndex, &Iter, UnlName,
&Params);
for (;;)
{
if (Status == S_FALSE || Status == E_INVALIDARG)
{
Status = E_NOINTERFACE;
break;
}
else if (Status != S_OK)
{
break;
}
if (!_strcmpi(Name, UnlName))
{
if (Index != NULL)
{
*Index = Idx;
}
if (Base != NULL)
{
*Base = Params.Base;
}
Status = S_OK;
break;
}
Status = Iter->GetEntry(UnlName, &Params);
Idx++;
}
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetModuleByOffset(
THIS_
IN ULONG64 Offset,
IN ULONG StartIndex,
OUT OPTIONAL PULONG Index,
OUT OPTIONAL PULONG64 Base
)
{
HRESULT Status;
ENTER_ENGINE();
if (g_Process == NULL)
{
Status = E_UNEXPECTED;
}
else
{
ULONG Idx = 0;
Status = E_NOINTERFACE;
ImageInfo* Image = g_Process->m_ImageHead;
while (Image != NULL)
{
if (StartIndex == 0 &&
Offset >= Image->m_BaseOfImage &&
Offset < Image->m_BaseOfImage + Image->m_SizeOfImage)
{
if (Index != NULL)
{
*Index = Idx;
}
if (Base != NULL)
{
*Base = Image->m_BaseOfImage;
}
Status = S_OK;
break;
}
Image = Image->m_Next;
Idx++;
if (StartIndex > 0)
{
StartIndex--;
}
}
if (Image == NULL)
{
UnloadedModuleInfo* Iter;
DEBUG_MODULE_PARAMETERS Params;
Status = GetUnloadedModuleByIndex(StartIndex, &Iter, NULL,
&Params);
for (;;)
{
if (Status == S_FALSE || Status == E_INVALIDARG)
{
Status = E_NOINTERFACE;
break;
}
else if (Status != S_OK)
{
break;
}
if (Offset >= Params.Base &&
Offset < Params.Base + Params.Size)
{
if (Index != NULL)
{
*Index = Idx;
}
if (Base != NULL)
{
*Base = Params.Base;
}
Status = S_OK;
break;
}
Status = Iter->GetEntry(NULL, &Params);
Idx++;
}
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetModuleNames(
THIS_
IN ULONG Index,
IN ULONG64 Base,
OUT OPTIONAL PSTR ImageNameBuffer,
IN ULONG ImageNameBufferSize,
OUT OPTIONAL PULONG ImageNameSize,
OUT OPTIONAL PSTR ModuleNameBuffer,
IN ULONG ModuleNameBufferSize,
OUT OPTIONAL PULONG ModuleNameSize,
OUT OPTIONAL PSTR LoadedImageNameBuffer,
IN ULONG LoadedImageNameBufferSize,
OUT OPTIONAL PULONG LoadedImageNameSize
)
{
HRESULT Status;
ENTER_ENGINE();
if (g_Process == NULL)
{
Status = E_UNEXPECTED;
}
else
{
ULONG Idx = 0;
Status = E_NOINTERFACE;
ImageInfo* Image = g_Process->m_ImageHead;
while (Image != NULL)
{
if ((Index != DEBUG_ANY_ID && Idx == Index) ||
(Index == DEBUG_ANY_ID && Base == Image->m_BaseOfImage))
{
IMAGEHLP_MODULE64 ModInfo;
ModInfo.SizeOfStruct = sizeof(ModInfo);
if (!SymGetModuleInfo64(g_Process->m_SymHandle,
Image->m_BaseOfImage, &ModInfo))
{
Status = WIN32_LAST_STATUS();
break;
}
Status = FillStringBuffer(Image->m_ImagePath, 0,
ImageNameBuffer,
ImageNameBufferSize,
ImageNameSize);
if (FillStringBuffer(Image->m_ModuleName, 0,
ModuleNameBuffer,
ModuleNameBufferSize,
ModuleNameSize) == S_FALSE)
{
Status = S_FALSE;
}
if (FillStringBuffer(ModInfo.LoadedImageName, 0,
LoadedImageNameBuffer,
LoadedImageNameBufferSize,
LoadedImageNameSize) == S_FALSE)
{
Status = S_FALSE;
}
break;
}
Image = Image->m_Next;
Idx++;
}
if (Image == NULL)
{
UnloadedModuleInfo* Iter;
char UnlName[MAX_INFO_UNLOADED_NAME];
DEBUG_MODULE_PARAMETERS Params;
ULONG StartIndex = 0;
if (Index != DEBUG_ANY_ID)
{
// If the index was already hit we
// shouldn't be here.
DBG_ASSERT(Index >= Idx);
StartIndex = Index - Idx;
}
Status = GetUnloadedModuleByIndex(StartIndex, &Iter, UnlName,
&Params);
Idx += StartIndex;
for (;;)
{
if (Status == S_FALSE || Status == E_INVALIDARG)
{
Status = E_NOINTERFACE;
break;
}
else if (Status != S_OK)
{
break;
}
if ((Index != DEBUG_ANY_ID && Idx == Index) ||
(Index == DEBUG_ANY_ID && Base == Params.Base))
{
Status = FillStringBuffer(UnlName, 0,
ImageNameBuffer,
ImageNameBufferSize,
ImageNameSize);
FillStringBuffer(NULL, 0,
ModuleNameBuffer,
ModuleNameBufferSize,
ModuleNameSize);
FillStringBuffer(NULL, 0,
LoadedImageNameBuffer,
LoadedImageNameBufferSize,
LoadedImageNameSize);
break;
}
Status = Iter->GetEntry(UnlName, &Params);
Idx++;
}
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetModuleParameters(
THIS_
IN ULONG Count,
IN OPTIONAL /* size_is(Count) */ PULONG64 Bases,
IN ULONG Start,
OUT /* size_is(Count) */ PDEBUG_MODULE_PARAMETERS Params
)
{
HRESULT Status;
ENTER_ENGINE();
UnloadedModuleInfo* Iter;
if (g_Process == NULL)
{
Status = E_UNEXPECTED;
}
else if (Bases != NULL)
{
Status = S_OK;
while (Count-- > 0)
{
ImageInfo* Image = g_Process->m_ImageHead;
while (Image != NULL)
{
if (*Bases == Image->m_BaseOfImage)
{
Image->FillModuleParameters(Params);
break;
}
Image = Image->m_Next;
}
if (Image == NULL)
{
Status = E_NOINTERFACE;
Iter = g_Target->GetUnloadedModuleInfo();
if (Iter != NULL &&
Iter->Initialize(g_Thread) == S_OK)
{
while (Iter->GetEntry(NULL, Params) == S_OK)
{
if (*Bases == Params->Base)
{
Status = S_OK;
break;
}
}
}
if (Status != S_OK)
{
ZeroMemory(Params, sizeof(*Params));
Params->Base = DEBUG_INVALID_OFFSET;
}
}
Bases++;
Params++;
}
}
else
{
ULONG i, End;
HRESULT SingleStatus;
Status = S_OK;
i = Start;
End = Start + Count;
if (i < g_Process->m_NumImages)
{
ImageInfo* Image = g_Process->FindImageByIndex(i);
while (i < g_Process->m_NumImages && i < End)
{
Image->FillModuleParameters(Params);
Image = Image->m_Next;
Params++;
i++;
}
}
if (i < End)
{
DEBUG_MODULE_PARAMETERS Param;
SingleStatus = GetUnloadedModuleByIndex
(i - g_Process->m_NumImages,
&Iter, NULL, &Param);
if (SingleStatus != S_OK)
{
Iter = NULL;
}
while (i < End)
{
if (SingleStatus != S_OK)
{
ZeroMemory(Params, sizeof(*Params));
Params->Base = DEBUG_INVALID_OFFSET;
Status = SingleStatus;
}
else
{
*Params = Param;
}
Params++;
if (Iter != NULL)
{
SingleStatus = Iter->GetEntry(NULL, &Param);
if (SingleStatus == S_FALSE)
{
SingleStatus = E_INVALIDARG;
}
}
i++;
}
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetSymbolModule(
THIS_
IN PCSTR Symbol,
OUT PULONG64 Base
)
{
HRESULT Status;
ENTER_ENGINE();
if (g_Process == NULL)
{
Status = E_UNEXPECTED;
}
else
{
PCSTR ModEnd;
ULONG Len;
ModEnd = strchr(Symbol, '!');
if (ModEnd == NULL)
{
Status = E_INVALIDARG;
}
else if (*(ModEnd+1) != '\0')
{
SYM_DUMP_PARAM_EX Param =
{
sizeof(Param), (PUCHAR)Symbol, DBG_DUMP_NO_PRINT, 0,
NULL, NULL, NULL, 0, NULL
};
ULONG TypeStatus;
TYPES_INFO TypeInfo;
ZeroMemory(&TypeInfo, sizeof(TypeInfo));
TypeStatus = TypeInfoFound(g_Process->m_SymHandle,
g_Process->m_ImageHead,
&Param, &TypeInfo);
if (TypeStatus == NO_ERROR)
{
*Base = TypeInfo.ModBaseAddress;
}
Status = ResultFromTypeStatus(TypeStatus);
}
else
{
ImageInfo* Image;
Status = E_NOINTERFACE;
Len = (ULONG)(ModEnd - Symbol);
for (Image = g_Process->m_ImageHead;
Image != NULL;
Image = Image->m_Next)
{
if (strlen(Image->m_ModuleName) == Len &&
!_memicmp(Symbol, Image->m_ModuleName, Len))
{
*Base = Image->m_BaseOfImage;
Status = S_OK;
break;
}
}
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetTypeName(
THIS_
IN ULONG64 Module,
IN ULONG TypeId,
OUT OPTIONAL PSTR NameBuffer,
IN ULONG NameBufferSize,
OUT OPTIONAL PULONG NameSize
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
TYPES_INFO TypeInfo;
ANSI_STRING TypeName;
char TypeString[MAX_NAME];
ZeroMemory(&TypeInfo, sizeof(TypeInfo));
TypeInfo.TypeIndex = TypeId;
TypeInfo.hProcess = g_Process->m_SymHandle;
TypeInfo.ModBaseAddress = Module;
TypeName.Buffer = TypeString;
TypeName.Length = sizeof(TypeString);
TypeName.MaximumLength = sizeof(TypeString);
Status = ::GetTypeName(NULL, &TypeInfo, &TypeName);
if (Status == S_OK)
{
Status = FillStringBuffer(TypeName.Buffer, TypeName.Length,
NameBuffer, NameBufferSize, NameSize);
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetConstantName(
THIS_
IN ULONG64 Module,
IN ULONG TypeId,
IN ULONG64 Value,
OUT OPTIONAL PSTR NameBuffer,
IN ULONG NameBufferSize,
OUT OPTIONAL PULONG NameSize
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
TYPES_INFO TypeInfo;
ANSI_STRING TypeName;
char TypeString[MAX_NAME];
ZeroMemory(&TypeInfo, sizeof(TypeInfo));
TypeInfo.TypeIndex = TypeId;
TypeInfo.hProcess = g_Process->m_SymHandle;
TypeInfo.ModBaseAddress = Module;
TypeInfo.Flag = SYMFLAG_VALUEPRESENT;
TypeInfo.Value = Value;
TypeName.Buffer = TypeString;
TypeName.Length = sizeof(TypeString);
TypeName.MaximumLength = sizeof(TypeString);
Status = ::GetTypeName(NULL, &TypeInfo, &TypeName);
if (Status == S_OK)
{
Status = FillStringBuffer(TypeName.Buffer, TypeName.Length,
NameBuffer, NameBufferSize, NameSize);
}
}
LEAVE_ENGINE();
return Status;
}
typedef struct _COPY_FIELD_NAME_CONTEXT {
ULONG Called;
ULONG IndexToMatch;
PSTR NameBuffer;
ULONG NameBufferSize;
PULONG NameSize;
HRESULT Status;
} COPY_FIELD_NAME_CONTEXT;
ULONG
CopyFieldName(
PFIELD_INFO_EX pField,
PVOID Context
)
{
COPY_FIELD_NAME_CONTEXT* pInfo = (COPY_FIELD_NAME_CONTEXT *) Context;
if (pInfo->Called++ == pInfo->IndexToMatch)
{
pInfo->Status = FillStringBuffer((PSTR) pField->fName, strlen((PCHAR) pField->fName)+1,
pInfo->NameBuffer, pInfo->NameBufferSize, pInfo->NameSize);
return FALSE;
}
return TRUE;
}
STDMETHODIMP
DebugClient::GetFieldName(
THIS_
IN ULONG64 Module,
IN ULONG TypeId,
IN ULONG FieldIndex,
OUT OPTIONAL PSTR NameBuffer,
IN ULONG NameBufferSize,
OUT OPTIONAL PULONG NameSize
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ULONG TypeStatus;
COPY_FIELD_NAME_CONTEXT FieldInfo =
{
0, FieldIndex, NameBuffer, NameBufferSize,
NameSize, E_INVALIDARG
};
SYM_DUMP_PARAM_EX Param =
{
sizeof(Param), NULL, DBG_DUMP_NO_PRINT | DBG_DUMP_CALL_FOR_EACH, 0,
NULL, &FieldInfo, &CopyFieldName, 0, NULL
};
TYPES_INFO TypeInfo;
ZeroMemory(&TypeInfo, sizeof(TypeInfo));
TypeInfo.hProcess = g_Process->m_SymHandle;
TypeInfo.ModBaseAddress = Module;
TypeInfo.TypeIndex = TypeId;
DumpType(&TypeInfo, &Param, &TypeStatus);
if (TypeStatus == NO_ERROR)
{
Status = FieldInfo.Status;
} else
{
Status = ResultFromTypeStatus(TypeStatus);
}
}
LEAVE_ENGINE();
return Status;
}
#define ALL_TYPE_OPTIONS (DEBUG_TYPEOPTS_UNICODE_DISPLAY | DEBUG_TYPEOPTS_LONGSTATUS_DISPLAY)
STDMETHODIMP
DebugClient::GetTypeOptions(
THIS_
OUT PULONG Options
)
{
ENTER_ENGINE();
*Options = 0;
*Options |= g_EnableUnicode ? DEBUG_TYPEOPTS_UNICODE_DISPLAY : 0;
*Options |= g_EnableLongStatus ? DEBUG_TYPEOPTS_LONGSTATUS_DISPLAY : 0;
LEAVE_ENGINE();
return S_OK;
}
STDMETHODIMP
DebugClient::SetTypeOptions(
THIS_
IN ULONG Options
)
{
if (Options & ~ALL_TYPE_OPTIONS)
{
return E_INVALIDARG;
}
ENTER_ENGINE();
g_EnableUnicode = Options & DEBUG_TYPEOPTS_UNICODE_DISPLAY;
g_EnableLongStatus = Options & DEBUG_TYPEOPTS_LONGSTATUS_DISPLAY;
g_TypeOptions = Options;
NotifyChangeSymbolState(DEBUG_CSS_TYPE_OPTIONS, 0, NULL);
LEAVE_ENGINE();
return S_OK;
}
STDMETHODIMP
DebugClient::AddTypeOptions(
THIS_
IN ULONG Options
)
{
if (Options & ~ALL_TYPE_OPTIONS)
{
return E_INVALIDARG;
}
ENTER_ENGINE();
if (Options & DEBUG_TYPEOPTS_UNICODE_DISPLAY)
{
g_EnableUnicode = TRUE;
}
if (Options & DEBUG_TYPEOPTS_LONGSTATUS_DISPLAY)
{
g_EnableLongStatus = TRUE;
}
g_TypeOptions |= Options;
NotifyChangeSymbolState(DEBUG_CSS_TYPE_OPTIONS, 0, NULL);
LEAVE_ENGINE();
return S_OK;
}
STDMETHODIMP
DebugClient::RemoveTypeOptions(
THIS_
IN ULONG Options
)
{
if (Options & ~ALL_TYPE_OPTIONS)
{
return E_INVALIDARG;
}
ENTER_ENGINE();
if (Options & DEBUG_TYPEOPTS_UNICODE_DISPLAY)
{
g_EnableUnicode = FALSE;
}
if (Options & DEBUG_TYPEOPTS_LONGSTATUS_DISPLAY)
{
g_EnableLongStatus = FALSE;
}
g_TypeOptions &= ~Options;
NotifyChangeSymbolState(DEBUG_CSS_TYPE_OPTIONS, 0, NULL);
LEAVE_ENGINE();
return S_OK;
}
STDMETHODIMP
DebugClient::GetTypeId(
THIS_
IN ULONG64 Module,
IN PCSTR Name,
OUT PULONG TypeId
)
{
ULONG TypeStatus;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
TypeStatus = TYPE_E_UNEXPECTED;
}
else
{
SYM_DUMP_PARAM_EX Param =
{
sizeof(Param), (PUCHAR)Name, DBG_DUMP_NO_PRINT, 0,
NULL, NULL, NULL, 0, NULL
};
TYPES_INFO TypeInfo;
PCHAR QualName;
TypeStatus = CANNOT_ALLOCATE_MEMORY;
QualName = (PCHAR) malloc(strlen(Name) + 32);
if (QualName)
{
if (!strchr(Name, '!'))
{
if (GetAllModuleName(Module, QualName, 30))
{
strcat(QualName, "!");
}
}
else // Already qualified name
{
*QualName = 0;
}
strcat(QualName, Name);
TypeStatus = SYMBOL_TYPE_INFO_NOT_FOUND;
Param.sName = (PUCHAR) QualName;
ZeroMemory(&TypeInfo, sizeof(TypeInfo));
TypeStatus = TypeInfoFound(g_Process->m_SymHandle,
g_Process->m_ImageHead,
&Param, &TypeInfo);
if (TypeStatus == NO_ERROR)
{
*TypeId = TypeInfo.TypeIndex;
}
}
}
LEAVE_ENGINE();
return ResultFromTypeStatus(TypeStatus);
}
STDMETHODIMP
DebugClient::GetTypeSize(
THIS_
IN ULONG64 Module,
IN ULONG TypeId,
OUT PULONG Size
)
{
ULONG TypeStatus;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
TypeStatus = TYPE_E_UNEXPECTED;
}
else
{
SYM_DUMP_PARAM_EX Param =
{
sizeof(Param), NULL,
DBG_DUMP_NO_PRINT | DBG_DUMP_GET_SIZE_ONLY, 0,
NULL, NULL, NULL, 0, NULL
};
TYPES_INFO TypeInfo;
ZeroMemory(&TypeInfo, sizeof(TypeInfo));
TypeInfo.hProcess = g_Process->m_SymHandle;
TypeInfo.ModBaseAddress = Module;
TypeInfo.TypeIndex = TypeId;
*Size = DumpType(&TypeInfo, &Param, &TypeStatus);
}
LEAVE_ENGINE();
return ResultFromTypeStatus(TypeStatus);
}
STDMETHODIMP
DebugClient::GetFieldOffset(
THIS_
IN ULONG64 Module,
IN ULONG TypeId,
IN PCSTR Field,
OUT PULONG Offset
)
{
ULONG TypeStatus;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
TypeStatus = TYPE_E_UNEXPECTED;
}
else
{
FIELD_INFO_EX FieldInfo =
{
(PUCHAR)Field, NULL, 0, DBG_DUMP_FIELD_FULL_NAME, 0, NULL
};
SYM_DUMP_PARAM_EX Param =
{
sizeof(Param), NULL, DBG_DUMP_NO_PRINT, 0,
NULL, NULL, NULL, 1, &FieldInfo
};
TYPES_INFO TypeInfo;
ZeroMemory(&TypeInfo, sizeof(TypeInfo));
TypeInfo.hProcess = g_Process->m_SymHandle;
TypeInfo.ModBaseAddress = Module;
TypeInfo.TypeIndex = TypeId;
DumpType(&TypeInfo, &Param, &TypeStatus);
if (TypeStatus == NO_ERROR)
{
*Offset = (ULONG)FieldInfo.address;
}
}
LEAVE_ENGINE();
return ResultFromTypeStatus(TypeStatus);
}
STDMETHODIMP
DebugClient::GetSymbolTypeId(
THIS_
IN PCSTR Symbol,
OUT PULONG TypeId,
OUT OPTIONAL PULONG64 Module
)
{
ULONG TypeStatus;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
TypeStatus = TYPE_E_UNEXPECTED;
}
else
{
TYPES_INFO_ALL TypeInfo;
ZeroMemory(&TypeInfo, sizeof(TypeInfo));
TypeStatus = !GetExpressionTypeInfo((PCHAR) Symbol, &TypeInfo);
if (TypeStatus == NO_ERROR)
{
*TypeId = TypeInfo.TypeIndex;
if (Module != NULL)
{
*Module = TypeInfo.Module;
}
}
}
LEAVE_ENGINE();
return ResultFromTypeStatus(TypeStatus);
}
STDMETHODIMP
DebugClient::GetOffsetTypeId(
THIS_
IN ULONG64 Offset,
OUT PULONG TypeId,
OUT OPTIONAL PULONG64 Module
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
SYM_DUMP_PARAM_EX Param =
{
sizeof(Param), NULL, DBG_DUMP_NO_PRINT, Offset,
NULL, NULL, NULL, 0, NULL
};
ULONG TypeStatus;
TYPES_INFO TypeInfo;
ZeroMemory(&TypeInfo, sizeof(TypeInfo));
TypeStatus = TypeInfoFound(g_Process->m_SymHandle,
g_Process->m_ImageHead,
&Param, &TypeInfo);
if (TypeStatus == NO_ERROR)
{
*TypeId = TypeInfo.TypeIndex;
if (Module != NULL)
{
*Module = TypeInfo.ModBaseAddress;
}
}
Status = ResultFromTypeStatus(TypeStatus);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::ReadTypedDataVirtual(
THIS_
IN ULONG64 Offset,
IN ULONG64 Module,
IN ULONG TypeId,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BytesRead
)
{
HRESULT Status;
ULONG Size;
if ((Status = GetTypeSize(Module, TypeId, &Size)) != S_OK)
{
return Status;
}
if (Size < BufferSize)
{
BufferSize = Size;
}
if ((Status = ReadVirtual(Offset, Buffer, BufferSize,
BytesRead)) != S_OK)
{
return Status;
}
return Size > BufferSize ? S_FALSE : S_OK;
}
STDMETHODIMP
DebugClient::WriteTypedDataVirtual(
THIS_
IN ULONG64 Offset,
IN ULONG64 Module,
IN ULONG TypeId,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BytesWritten
)
{
HRESULT Status;
ULONG Size;
if ((Status = GetTypeSize(Module, TypeId, &Size)) != S_OK)
{
return Status;
}
if (Size < BufferSize)
{
BufferSize = Size;
}
if ((Status = WriteVirtual(Offset, Buffer, BufferSize,
BytesWritten)) != S_OK)
{
return Status;
}
return Size > BufferSize ? S_FALSE : S_OK;
}
#define ALL_OUTPUT_TYPE_FLAGS \
(DEBUG_OUTTYPE_NO_INDENT | \
DEBUG_OUTTYPE_NO_OFFSET | \
DEBUG_OUTTYPE_VERBOSE | \
DEBUG_OUTTYPE_COMPACT_OUTPUT | \
DEBUG_OUTTYPE_RECURSION_LEVEL(0xf) | \
DEBUG_OUTTYPE_ADDRESS_OF_FIELD | \
DEBUG_OUTTYPE_ADDRESS_AT_END | \
DEBUG_OUTTYPE_BLOCK_RECURSE )
ULONG
OutputTypeFlagsToDumpOptions(ULONG Flags)
{
ULONG Options = 0;
if (Flags & DEBUG_OUTTYPE_NO_INDENT)
{
Options |= DBG_DUMP_NO_INDENT;
}
if (Flags & DEBUG_OUTTYPE_NO_OFFSET)
{
Options |= DBG_DUMP_NO_OFFSET;
}
if (Flags & DEBUG_OUTTYPE_VERBOSE)
{
Options |= DBG_DUMP_VERBOSE;
}
if (Flags & DEBUG_OUTTYPE_COMPACT_OUTPUT)
{
Options |= DBG_DUMP_COMPACT_OUT;
}
if (Flags & DEBUG_OUTTYPE_ADDRESS_AT_END)
{
Options |= DBG_DUMP_ADDRESS_AT_END;
}
if (Flags & DEBUG_OUTTYPE_ADDRESS_OF_FIELD)
{
Options |= DBG_DUMP_ADDRESS_OF_FIELD;
}
if (Flags & DEBUG_OUTTYPE_BLOCK_RECURSE)
{
Options |= DBG_DUMP_BLOCK_RECURSE;
}
Options |= DBG_DUMP_RECUR_LEVEL(((Flags >> 4) & 0xf));
return Options;
}
STDMETHODIMP
DebugClient::OutputTypedDataVirtual(
THIS_
IN ULONG OutputControl,
IN ULONG64 Offset,
IN ULONG64 Module,
IN ULONG TypeId,
IN ULONG Flags
)
{
if (Flags & ~ALL_OUTPUT_TYPE_FLAGS)
{
return E_INVALIDARG;
}
HRESULT Status;
ENTER_ENGINE();
OutCtlSave OldCtl;
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else if (!PushOutCtl(OutputControl, this, &OldCtl))
{
Status = E_INVALIDARG;
}
else
{
SYM_DUMP_PARAM_EX Param =
{
sizeof(Param), NULL, OutputTypeFlagsToDumpOptions(Flags), Offset,
NULL, NULL, NULL, 0, NULL
};
ULONG TypeStatus;
TYPES_INFO TypeInfo;
ZeroMemory(&TypeInfo, sizeof(TypeInfo));
TypeInfo.hProcess = g_Process->m_SymHandle;
TypeInfo.ModBaseAddress = Module;
TypeInfo.TypeIndex = TypeId;
DumpType(&TypeInfo, &Param, &TypeStatus);
Status = ResultFromTypeStatus(TypeStatus);
PopOutCtl(&OldCtl);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::ReadTypedDataPhysical(
THIS_
IN ULONG64 Offset,
IN ULONG64 Module,
IN ULONG TypeId,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BytesRead
)
{
HRESULT Status;
ULONG Size;
if ((Status = GetTypeSize(Module, TypeId, &Size)) != S_OK)
{
return Status;
}
if (Size < BufferSize)
{
BufferSize = Size;
}
if ((Status = ReadPhysical(Offset, Buffer, BufferSize,
BytesRead)) != S_OK)
{
return Status;
}
return Size > BufferSize ? S_FALSE : S_OK;
}
STDMETHODIMP
DebugClient::WriteTypedDataPhysical(
THIS_
IN ULONG64 Offset,
IN ULONG64 Module,
IN ULONG TypeId,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BytesWritten
)
{
HRESULT Status;
ULONG Size;
if ((Status = GetTypeSize(Module, TypeId, &Size)) != S_OK)
{
return Status;
}
if (Size < BufferSize)
{
BufferSize = Size;
}
if ((Status = WritePhysical(Offset, Buffer, BufferSize,
BytesWritten)) != S_OK)
{
return Status;
}
return Size > BufferSize ? S_FALSE : S_OK;
}
STDMETHODIMP
DebugClient::OutputTypedDataPhysical(
THIS_
IN ULONG OutputControl,
IN ULONG64 Offset,
IN ULONG64 Module,
IN ULONG TypeId,
IN ULONG Flags
)
{
if (Flags & ~ALL_OUTPUT_TYPE_FLAGS)
{
return E_INVALIDARG;
}
HRESULT Status;
ENTER_ENGINE();
OutCtlSave OldCtl;
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else if (!PushOutCtl(OutputControl, this, &OldCtl))
{
Status = E_INVALIDARG;
}
else
{
SYM_DUMP_PARAM_EX Param =
{
sizeof(Param), NULL, OutputTypeFlagsToDumpOptions(Flags) |
DBG_DUMP_READ_PHYSICAL, Offset, NULL, NULL, NULL, 0, NULL
};
ULONG TypeStatus;
TYPES_INFO TypeInfo;
ZeroMemory(&TypeInfo, sizeof(TypeInfo));
TypeInfo.hProcess = g_Process->m_SymHandle;
TypeInfo.ModBaseAddress = Module;
TypeInfo.TypeIndex = TypeId;
DumpType(&TypeInfo, &Param, &TypeStatus);
Status = ResultFromTypeStatus(TypeStatus);
PopOutCtl(&OldCtl);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetScope(
THIS_
OUT OPTIONAL PULONG64 InstructionOffset,
OUT OPTIONAL PDEBUG_STACK_FRAME ScopeFrame,
OUT OPTIONAL PVOID ScopeContext,
IN ULONG ScopeContextSize
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
goto Exit;
}
Status = S_OK;
// windbg requests the scope IP and only the scope
// IP at every event. If the scope is a default lazy
// scope we can satisfy that request very efficiently.
if (g_ScopeBuffer.State == ScopeDefaultLazy &&
InstructionOffset &&
!ScopeFrame &&
(!ScopeContext ||
ScopeContextSize == 0))
{
ADDR Addr;
g_Machine->GetPC(&Addr);
*InstructionOffset = Flat(Addr);
goto Exit;
}
PDEBUG_SCOPE Scope;
Scope = GetCurrentScope();
if (InstructionOffset)
{
*InstructionOffset = Scope->Frame.InstructionOffset;
}
if (ScopeFrame)
{
*ScopeFrame = Scope->Frame;
}
if (ScopeContext)
{
if (Scope->State == ScopeFromContext)
{
memcpy(ScopeContext, &Scope->Context,
min(sizeof(Scope->Context), ScopeContextSize));
}
else if (g_Machine->GetContextState(MCTX_FULL) == S_OK)
{
memcpy(ScopeContext, &g_Machine->m_Context,
min(sizeof(g_Machine->m_Context), ScopeContextSize));
}
}
Exit:
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::SetScope(
THIS_
IN ULONG64 InstructionOffset,
IN OPTIONAL PDEBUG_STACK_FRAME ScopeFrame,
IN OPTIONAL PVOID ScopeContext,
IN ULONG ScopeContextSize
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
DEBUG_STACK_FRAME LocalFrame;
if (ScopeFrame)
{
LocalFrame = *ScopeFrame;
}
else
{
ZeroMemory(&LocalFrame, sizeof(LocalFrame));
LocalFrame.InstructionOffset = InstructionOffset;
}
Status = SetCurrentScope(&LocalFrame, ScopeContext, ScopeContextSize) ?
S_FALSE : S_OK;
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::ResetScope(
THIS
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
ResetCurrentScope();
Status = S_OK;
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetScopeSymbolGroup(
THIS_
IN ULONG Flags,
IN OPTIONAL PDEBUG_SYMBOL_GROUP Update,
OUT PDEBUG_SYMBOL_GROUP* Symbols
)
{
HRESULT Status;
if (Flags == 0 ||
(Flags & ~DEBUG_SCOPE_GROUP_ALL))
{
return E_INVALIDARG;
}
ENTER_ENGINE();
if (Update)
{
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else if (Flags != DEBUG_SCOPE_GROUP_LOCALS &&
Flags != DEBUG_SCOPE_GROUP_ARGUMENTS)
{
Status = E_INVALIDARG;
}
else
{
Status = ((DebugSymbolGroup *)Update)->AddCurrentLocals();
if (Status == S_OK)
{
*Symbols = Update;
}
}
}
else
{
*Symbols = new DebugSymbolGroup(this, Flags);
if (*Symbols != NULL)
{
Status = S_OK;
}
else
{
Status = E_OUTOFMEMORY;
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::CreateSymbolGroup(
THIS_
OUT PDEBUG_SYMBOL_GROUP* Group
)
{
HRESULT Status;
ENTER_ENGINE();
*Group = new DebugSymbolGroup(this, 0);
if (*Group == NULL)
{
Status = E_OUTOFMEMORY;
}
else
{
Status = S_OK;
}
LEAVE_ENGINE();
return Status;
}
struct SymbolMatch
{
ProcessInfo* Process;
BOOL SingleMod;
ImageInfo* Mod;
PCHAR Storage, StorageEnd;
PCHAR Cur, End;
char Pattern[1];
};
STDMETHODIMP
DebugClient::StartSymbolMatch(
THIS_
IN PCSTR Pattern,
OUT PULONG64 Handle
)
{
HRESULT Status;
ENTER_ENGINE();
if (g_Process == NULL)
{
Status = E_UNEXPECTED;
goto EH_Exit;
}
ImageInfo* Mod;
PCSTR Sym;
// Check for a module qualifier.
Sym = strchr(Pattern, '!');
if (Sym != NULL)
{
size_t ModLen = Sym - Pattern;
if (ModLen == 0)
{
Status = E_INVALIDARG;
goto EH_Exit;
}
Mod = g_Process->FindImageByName(Pattern, ModLen, INAME_MODULE, FALSE);
if (Mod == NULL)
{
Status = E_NOINTERFACE;
goto EH_Exit;
}
Sym++;
}
else
{
Sym = Pattern;
Mod = NULL;
}
ULONG SymLen;
SymLen = strlen(Sym);
SymbolMatch* Match;
Match = (SymbolMatch*)malloc(sizeof(SymbolMatch) + SymLen);
if (Match == NULL)
{
Status = E_OUTOFMEMORY;
goto EH_Exit;
}
if (Mod == NULL)
{
Match->Process = g_Process;
Match->Mod = Match->Process->m_ImageHead;
Match->SingleMod = FALSE;
}
else
{
Match->Process = g_Process;
Match->Mod = Mod;
Match->SingleMod = TRUE;
}
Match->Storage = NULL;
Match->StorageEnd = NULL;
Match->Cur = NULL;
strcpy(Match->Pattern, Sym);
_strupr(Match->Pattern);
*Handle = (ULONG64)Match;
Status = S_OK;
EH_Exit:
LEAVE_ENGINE();
return Status;
}
#define STORAGE_INC 16384
BOOL CALLBACK
FillMatchStorageCb(PSTR Name, ULONG64 Offset, ULONG Size, PVOID Context)
{
SymbolMatch* Match = (SymbolMatch*)Context;
ULONG NameLen = strlen(Name) + 1;
ULONG RecordLen = NameLen + sizeof(ULONG64);
if (Match->Cur + RecordLen > Match->StorageEnd)
{
PCHAR NewStore;
size_t NewLen;
NewLen = (Match->StorageEnd - Match->Storage) + STORAGE_INC;
NewStore = (PCHAR)realloc(Match->Storage, NewLen);
if (NewStore == NULL)
{
// Terminate the enumeration since there's no more room.
// This produces a silent failure but it's not
// important enough to warrant a true failure.
return FALSE;
}
Match->Cur = NewStore + (Match->Cur - Match->Storage);
Match->Storage = NewStore;
Match->StorageEnd = NewStore + NewLen;
DBG_ASSERT(Match->Cur + RecordLen <= Match->StorageEnd);
}
strcpy(Match->Cur, Name);
Match->Cur += NameLen;
*(ULONG64 UNALIGNED *)Match->Cur = Offset;
Match->Cur += sizeof(Offset);
return TRUE;
}
STDMETHODIMP
DebugClient::GetNextSymbolMatch(
THIS_
IN ULONG64 Handle,
OUT OPTIONAL PSTR Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG MatchSize,
OUT OPTIONAL PULONG64 Offset
)
{
ENTER_ENGINE();
SymbolMatch* Match = (SymbolMatch*)Handle;
HRESULT Status = E_NOINTERFACE;
// Loop until a matching symbol is found.
for (;;)
{
if (Match->Mod == NULL)
{
// Nothing more to enumerate.
// Status is already set.
break;
}
if (Match->Cur == NULL)
{
// Enumerate all symbols and stash them away.
Match->Cur = Match->Storage;
if (!SymEnumerateSymbols64(Match->Process->m_SymHandle,
Match->Mod->m_BaseOfImage,
FillMatchStorageCb, Match))
{
Status = WIN32_LAST_STATUS();
break;
}
Match->End = Match->Cur;
Match->Cur = Match->Storage;
}
while (Match->Cur < Match->End)
{
PCHAR Name;
ULONG64 Addr;
Name = Match->Cur;
Match->Cur += strlen(Name) + 1;
Addr = *(ULONG64 UNALIGNED *)Match->Cur;
Match->Cur += sizeof(Addr);
// If this symbol matches remember it for return.
if (MatchPattern(Name, Match->Pattern))
{
char Sym[MAX_MODULE + MAX_SYMBOL_LEN + 1];
CopyString(Sym, Match->Mod->m_ModuleName, DIMA(Sym));
CatString(Sym, "!", DIMA(Sym));
CatString(Sym, Name, DIMA(Sym));
Status = FillStringBuffer(Sym, 0, Buffer, BufferSize,
MatchSize);
if (Buffer == NULL)
{
// Do not advance the enumeration as this
// is just a size test.
Match->Cur = Name;
}
if (Offset != NULL)
{
*Offset = Addr;
}
break;
}
}
if (SUCCEEDED(Status))
{
break;
}
if (Match->SingleMod)
{
Match->Mod = NULL;
}
else
{
Match->Mod = Match->Mod->m_Next;
}
Match->Cur = NULL;
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::EndSymbolMatch(
THIS_
IN ULONG64 Handle
)
{
ENTER_ENGINE();
SymbolMatch* Match = (SymbolMatch*)Handle;
if (Match->Storage != NULL)
{
free(Match->Storage);
}
free(Match);
LEAVE_ENGINE();
return S_OK;
}
STDMETHODIMP
DebugClient::Reload(
THIS_
IN PCSTR Module
)
{
ENTER_ENGINE();
PCSTR ArgsRet;
HRESULT Status = g_Target->Reload(g_Thread, Module, &ArgsRet);
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetSymbolPath(
THIS_
OUT OPTIONAL PSTR Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG PathSize
)
{
ENTER_ENGINE();
HRESULT Status =
FillStringBuffer(g_SymbolSearchPath, 0,
Buffer, BufferSize, PathSize);
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::SetSymbolPath(
THIS_
IN PCSTR Path
)
{
ENTER_ENGINE();
HRESULT Status;
Status = ChangeSymPath(Path, FALSE, NULL, 0) ?
S_OK : E_OUTOFMEMORY;
if (Status == S_OK)
{
CheckPath(g_SymbolSearchPath);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::AppendSymbolPath(
THIS_
IN PCSTR Addition
)
{
ENTER_ENGINE();
HRESULT Status;
Status = ChangeSymPath(Addition, TRUE, NULL, 0) ?
S_OK : E_OUTOFMEMORY;
if (Status == S_OK)
{
CheckPath(g_SymbolSearchPath);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetImagePath(
THIS_
OUT OPTIONAL PSTR Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG PathSize
)
{
ENTER_ENGINE();
HRESULT Status = FillStringBuffer(g_ExecutableImageSearchPath, 0,
Buffer, BufferSize, PathSize);
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::SetImagePath(
THIS_
IN PCSTR Path
)
{
ENTER_ENGINE();
HRESULT Status = ChangePath(&g_ExecutableImageSearchPath, Path, FALSE,
DEBUG_CSS_PATHS);
if (Status == S_OK)
{
CheckPath(g_ExecutableImageSearchPath);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::AppendImagePath(
THIS_
IN PCSTR Addition
)
{
ENTER_ENGINE();
HRESULT Status = ChangePath(&g_ExecutableImageSearchPath, Addition, TRUE,
DEBUG_CSS_PATHS);
if (Status == S_OK)
{
CheckPath(g_ExecutableImageSearchPath);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetSourcePath(
THIS_
OUT OPTIONAL PSTR Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG PathSize
)
{
ENTER_ENGINE();
HRESULT Status = FillStringBuffer(g_SrcPath, 0,
Buffer, BufferSize, PathSize);
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetSourcePathElement(
THIS_
IN ULONG Index,
OUT OPTIONAL PSTR Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG ElementSize
)
{
ENTER_ENGINE();
HRESULT Status;
PSTR Elt, EltEnd;
Elt = FindPathElement(g_SrcPath, Index, &EltEnd);
if (Elt == NULL)
{
Status = E_NOINTERFACE;
goto EH_Exit;
}
CHAR Save;
Save = *EltEnd;
*EltEnd = 0;
Status = FillStringBuffer(Elt, (ULONG)(EltEnd - Elt) + 1,
Buffer, BufferSize, ElementSize);
*EltEnd = Save;
EH_Exit:
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::SetSourcePath(
THIS_
IN PCSTR Path
)
{
ENTER_ENGINE();
HRESULT Status = ChangePath(&g_SrcPath, Path, FALSE, DEBUG_CSS_PATHS);
if (Status == S_OK)
{
CheckPath(g_SrcPath);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::AppendSourcePath(
THIS_
IN PCSTR Addition
)
{
ENTER_ENGINE();
HRESULT Status = ChangePath(&g_SrcPath, Addition, TRUE, DEBUG_CSS_PATHS);
if (Status == S_OK)
{
CheckPath(g_SrcPath);
}
LEAVE_ENGINE();
return Status;
}
HRESULT
GetCanonicalPath(PCSTR Path, PSTR Canon, ULONG CanonSize)
{
// First make sure it's a full path.
// XXX drewb - Probably should also convert drive
// letters to unambiguous names.
if (!IS_SLASH(Path[0]) &&
!(((Path[0] >= 'a' && Path[0] <= 'z') ||
(Path[0] >= 'A' && Path[0] <= 'Z')) &&
Path[1] == ':') &&
!IsUrlPathComponent(Path))
{
DWORD FullLen;
PSTR FilePart;
FullLen = GetFullPathName(Path, CanonSize, Canon, &FilePart);
if (FullLen == 0 || FullLen >= CanonSize)
{
return WIN32_LAST_STATUS();
}
}
else
{
CopyString(Canon, Path, CanonSize);
}
// Now remove '.' and '..'. This is a full path with a filename
// at the end so all occurrences must be bracketed with
// path slashes.
PSTR Rd = Canon, Wr = Canon;
while (*Rd != 0)
{
if (IS_SLASH(*Rd))
{
if (Rd[1] == '.')
{
if (IS_SLASH(Rd[2]))
{
// Found /./, ignore leading /. and continue
// with /.
Rd += 2;
continue;
}
else if (Rd[2] == '.' && IS_SLASH(Rd[3]))
{
// Found /../ so back up one path component
// and continue with /.
do
{
Wr--;
}
while (Wr >= Canon && !IS_PATH_DELIM(*Wr));
DBG_ASSERT(Wr >= Canon);
Rd += 3;
continue;
}
}
}
*Wr++ = *Rd++;
}
*Wr = 0;
return S_OK;
}
STDMETHODIMP
DebugClient::FindSourceFile(
THIS_
IN ULONG StartElement,
IN PCSTR File,
IN ULONG Flags,
OUT OPTIONAL PULONG FoundElement,
OUT OPTIONAL PSTR Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG FoundSize
)
{
if (Flags & ~(DEBUG_FIND_SOURCE_DEFAULT |
DEBUG_FIND_SOURCE_FULL_PATH |
DEBUG_FIND_SOURCE_BEST_MATCH))
{
return E_INVALIDARG;
}
HRESULT Status;
char RwFile[MAX_SOURCE_PATH];
ULONG FileLen;
char Found[MAX_SOURCE_PATH];
PSTR MatchPart;
ULONG Elt;
// Make a read-write copy of the file as the searching
// modifies it.
FileLen = strlen(File) + 1;
if (FileLen > sizeof(RwFile))
{
return E_INVALIDARG;
}
memcpy(RwFile, File, FileLen);
ENTER_ENGINE();
if (FindSrcFileOnPath(StartElement, RwFile, Flags, Found, DIMA(Found),
&MatchPart, &Elt))
{
if (Flags & DEBUG_FIND_SOURCE_FULL_PATH)
{
Status = GetCanonicalPath(Found, RwFile, DIMA(RwFile));
if (Status != S_OK)
{
goto EH_Exit;
}
strcpy(Found, RwFile);
}
if (FoundElement != NULL)
{
*FoundElement = Elt;
}
Status = FillStringBuffer(Found, 0,
Buffer, BufferSize, FoundSize);
}
else
{
Status = E_NOINTERFACE;
}
EH_Exit:
LEAVE_ENGINE();
return Status;
}
// XXX drewb - This API is private for the moment due
// to uncertainty about what dbghelp's API is going to
// look like in the long term.
extern "C"
ULONG
IMAGEAPI
SymGetFileLineOffsets64(
IN HANDLE hProcess,
IN LPSTR ModuleName,
IN LPSTR FileName,
OUT PDWORD64 Buffer,
IN ULONG BufferLines
);
STDMETHODIMP
DebugClient::GetSourceFileLineOffsets(
THIS_
IN PCSTR File,
OUT OPTIONAL PULONG64 Buffer,
IN ULONG BufferLines,
OUT OPTIONAL PULONG FileLines
)
{
HRESULT Status;
ULONG Line;
if (Buffer != NULL)
{
// Initialize map to empty.
for (Line = 0; Line < BufferLines; Line++)
{
Buffer[Line] = DEBUG_INVALID_OFFSET;
}
}
ENTER_ENGINE();
if (g_Process == NULL)
{
Status = E_UNEXPECTED;
goto EH_Exit;
}
PSTR FilePart;
ULONG HighestLine;
// Request the line information from dbghelp.
FilePart = (PSTR)File;
HighestLine =
SymGetFileLineOffsets64(g_Process->m_SymHandle, NULL, FilePart,
Buffer, BufferLines);
if (HighestLine == 0xffffffff)
{
Status = WIN32_LAST_STATUS();
goto EH_Exit;
}
if (HighestLine == 0)
{
// Try again with just the filename because the path
// may be different than what's in the symbol information.
// XXX drewb - This can cause ambiguity problems.
FilePart = (PSTR)File + strlen(File) - 1;
while (FilePart >= File)
{
if (IS_PATH_DELIM(*FilePart))
{
break;
}
FilePart--;
}
FilePart++;
if (FilePart <= File)
{
// No path and no information was found for the
// given file so return not-found.
Status = E_NOINTERFACE;
goto EH_Exit;
}
HighestLine =
SymGetFileLineOffsets64(g_Process->m_SymHandle, NULL, FilePart,
Buffer, BufferLines);
if (HighestLine == 0xffffffff)
{
Status = WIN32_LAST_STATUS();
goto EH_Exit;
}
else if (HighestLine == 0)
{
Status = E_NOINTERFACE;
goto EH_Exit;
}
}
if (FileLines != NULL)
{
*FileLines = HighestLine;
}
// Return S_FALSE if lines were missed because of
// insufficient buffer space.
Status = HighestLine > BufferLines ? S_FALSE : S_OK;
EH_Exit:
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetModuleVersionInformation(
THIS_
IN ULONG Index,
IN ULONG64 Base,
IN PCSTR Item,
OUT OPTIONAL PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG VerInfoSize
)
{
HRESULT Status;
ENTER_ENGINE();
if (g_Process == NULL)
{
Status = E_UNEXPECTED;
}
else
{
ImageInfo* Image;
if (Index == DEBUG_ANY_ID)
{
Image = g_Process->FindImageByOffset(Base, FALSE);
}
else
{
Image = g_Process->FindImageByIndex(Index);
}
if (Image == NULL)
{
Status = E_NOINTERFACE;
}
else
{
Status = g_Target->
GetImageVersionInformation(g_Process, Image->m_ImagePath,
Image->m_BaseOfImage, Item,
Buffer, BufferSize, VerInfoSize);
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugClient::GetModuleNameString(
THIS_
IN ULONG Which,
IN ULONG Index,
IN ULONG64 Base,
OUT OPTIONAL PSTR Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG NameSize
)
{
HRESULT Status;
if (Which > DEBUG_MODNAME_MAPPED_IMAGE)
{
return E_INVALIDARG;
}
ENTER_ENGINE();
if (g_Process == NULL)
{
Status = E_UNEXPECTED;
}
else
{
ULONG Idx = 0;
Status = E_NOINTERFACE;
ImageInfo* Image = g_Process->m_ImageHead;
while (Image != NULL)
{
if ((Index != DEBUG_ANY_ID && Idx == Index) ||
(Index == DEBUG_ANY_ID && Base == Image->m_BaseOfImage))
{
PSTR Str;
IMAGEHLP_MODULE64 ModInfo;
switch(Which)
{
case DEBUG_MODNAME_IMAGE:
Str = Image->m_ImagePath;
break;
case DEBUG_MODNAME_MODULE:
Str = Image->m_ModuleName;
break;
case DEBUG_MODNAME_LOADED_IMAGE:
ModInfo.SizeOfStruct = sizeof(ModInfo);
if (!SymGetModuleInfo64(g_Process->m_SymHandle,
Image->m_BaseOfImage, &ModInfo))
{
Status = WIN32_LAST_STATUS();
goto Exit;
}
Str = ModInfo.LoadedImageName;
break;
case DEBUG_MODNAME_SYMBOL_FILE:
ModInfo.SizeOfStruct = sizeof(ModInfo);
if (!SymGetModuleInfo64(g_Process->m_SymHandle,
Image->m_BaseOfImage, &ModInfo))
{
Status = WIN32_LAST_STATUS();
goto Exit;
}
Str = ModInfoSymFile(&ModInfo);
break;
case DEBUG_MODNAME_MAPPED_IMAGE:
Str = Image->m_MappedImagePath;
break;
}
Status = FillStringBuffer(Str, 0,
Buffer, BufferSize, NameSize);
break;
}
Image = Image->m_Next;
Idx++;
}
}
Exit:
LEAVE_ENGINE();
return Status;
}
//----------------------------------------------------------------------------
//
// SymbolGroupEntry.
//
//----------------------------------------------------------------------------
SymbolGroupEntry::SymbolGroupEntry(void)
{
m_Parent = NULL;
m_Next = NULL;
m_Format = NULL;
m_Expr = NULL;
m_Cast = NULL;
ZeroMemory(&m_Params, sizeof(m_Params));
m_Flags = 0;
ZeroMemory(&m_BaseData, sizeof(m_BaseData));
m_BaseFormatKind = SGFORMAT_TYPED_DATA;
}
SymbolGroupEntry::~SymbolGroupEntry(void)
{
free(m_Expr);
free(m_Cast);
delete m_Format;
}
//----------------------------------------------------------------------------
//
// SymbolGroupFormat.
//
//----------------------------------------------------------------------------
SymbolGroupFormat::SymbolGroupFormat(SymbolGroupEntry* Entry,
SymbolGroupFormatKind Kind)
{
m_Entry = Entry;
m_Kind = Kind;
// There's no expression to start with so
// start in an error state.
m_ExprErr = NOTFOUND;
m_ValueErr = NOTFOUND;
}
SymbolGroupFormat::~SymbolGroupFormat(void)
{
}
void
SymbolGroupFormat::TestImages(void)
{
ImageInfo* Image;
if (m_Entry->m_Params.Module == 0)
{
Image = NULL;
}
else
{
Image = g_Process ?
g_Process->FindImageByOffset(m_Entry->m_Params.Module, FALSE) :
NULL;
}
if (Image != m_Entry->m_BaseData.m_Image)
{
// The module list has changed, avoid
// referencing a possibly invalid image.
m_Entry->m_BaseData.ReleaseImage();
}
}
//---------------------------------------------------------------------------
//
// TypedDataSymbolGroupFormat.
//
//----------------------------------------------------------------------------
struct TdccContext
{
DebugSymbolGroup* Group;
TypedDataSymbolGroupFormat* Format;
SymbolGroupEntry* AddAfter;
ULONG NumChildren;
};
ULONG
TypedDataSymbolGroupFormat::CreateChildren(DebugSymbolGroup* Group)
{
if (m_ExprErr)
{
return m_ExprErr;
}
if (!m_Entry->m_Params.SubElements)
{
return NOTFOUND;
}
ULONG Err;
TdccContext Context;
Context.Group = Group;
Context.Format = this;
Context.AddAfter = m_Entry;
Context.NumChildren = 0;
// We don't need the actual data for children here,
// just the children themselves. This allows nodes
// to be expanded even when they refer to invalid
// memory.
// The children's values will be updated on the next Refresh.
if (Err = m_CastData.GetChildren(g_Machine->m_Ptr64 ? 8 : 4,
CHLF_DEREF_UDT_POINTERS |
CHLF_DISALLOW_ACCESS,
CreateChildrenCb, &Context))
{
return Err;
}
// The estimated child count may have been different from
// the actual child count so update it.
m_Entry->m_Params.SubElements = Context.NumChildren;
return NO_ERROR;
}
ULONG
TypedDataSymbolGroupFormat::AddChild(SymbolGroupEntry** AddAfter,
PSTR Name, TypedData* Data)
{
SymbolGroupEntry* Child = new SymbolGroupEntry;
TypedDataSymbolGroupFormat* ChildFormat =
new TypedDataSymbolGroupFormat(Child);
if (!Child || !ChildFormat)
{
delete Child;
return NOMEMORY;
}
Child->m_Format = ChildFormat;
Child->m_Expr = _strdup(Name);
if (!Child->m_Expr)
{
delete Child;
return NOMEMORY;
}
Child->m_Parent = m_Entry;
Child->m_Params.Flags =
(m_Entry->m_Params.Flags & DEBUG_SYMBOL_EXPANSION_LEVEL_MASK) + 1;
Child->m_Next = (*AddAfter)->m_Next;
Child->m_BaseData = *Data;
Child->m_BaseFormatKind = SGFORMAT_TYPED_DATA;
ChildFormat->m_ExprErr = NO_ERROR;
ChildFormat->m_CastData = *Data;
ChildFormat->UpdateParams();
(*AddAfter)->m_Next = Child;
*AddAfter = Child;
return NO_ERROR;
}
ULONG
TypedDataSymbolGroupFormat::CreateChildrenCb(PVOID _Context,
PSTR Name, TypedData* Child)
{
ULONG Err;
TdccContext* Context = (TdccContext*)_Context;
Err = Context->Format->AddChild(&Context->AddAfter, Name, Child);
if (!Err)
{
Context->Group->m_NumEntries++;
Context->NumChildren++;
}
return Err;
}
ULONG
TypedDataSymbolGroupFormat::Refresh(TypedDataAccess AllowAccess)
{
ULONG64 OldPtr = m_CastData.m_Ptr;
if (m_ExprErr)
{
return m_ExprErr;
}
m_ValueErr = m_CastData.ReadData(AllowAccess);
if (m_ValueErr)
{
return m_ValueErr;
}
if (m_CastData.IsPointer() &&
m_CastData.m_Ptr != OldPtr)
{
RefreshChildren();
}
return NO_ERROR;
}
ULONG
TypedDataSymbolGroupFormat::Write(PCSTR Value)
{
if (m_ExprErr)
{
return m_ExprErr;
}
g_DisableErrorPrint++;
EvalExpression* RelChain = g_EvalReleaseChain;
g_EvalReleaseChain = NULL;
__try
{
TypedData Source;
EvalExpression* Eval = GetEvaluator(DEBUG_EXPR_CPLUSPLUS, FALSE);
Eval->Evaluate(Value, NULL, EXPRF_DEFAULT, &Source);
ReleaseEvaluator(Eval);
if (!(m_ValueErr = Source.ConvertToSource(&m_CastData)) &&
!(m_ValueErr = m_CastData.WriteData(&Source, TDACC_REQUIRE)))
{
m_CastData.CopyData(&Source);
}
}
__except(CommandExceptionFilter(GetExceptionInformation()))
{
m_ValueErr = GetExceptionCode() - COMMAND_EXCEPTION_BASE;
}
g_EvalReleaseChain = RelChain;
g_DisableErrorPrint--;
return m_ValueErr;
}
void
TypedDataSymbolGroupFormat::OutputValue(void)
{
ULONG Tag;
if (m_ValueErr)
{
// windbg puts dummy entries with no expression
// in symbol groups and expects no output from them,
// so special case that.
if (!m_Entry->m_Expr || *m_Entry->m_Expr)
{
dprintf("<%s error>", ErrorString(m_ValueErr));
}
return;
}
m_CastData.OutputSimpleValue();
// For symbol groups we automatically dereference
// pointers to UDTs for convenience. Display the
// UDT type in the value for UDT pointers as there won't be
// a normal pointer child to display the type.
if (m_CastData.IsPointer() &&
m_CastData.m_Image &&
SymGetTypeInfo(m_CastData.m_Image->m_Process->m_SymHandle,
m_CastData.m_Image->m_BaseOfImage,
m_CastData.m_NextType, TI_GET_SYMTAG, &Tag) &&
Tag == SymTagUDT)
{
TypedData Deref = m_CastData;
if (!Deref.ConvertToDereference(TDACC_NONE,
g_Machine->m_Ptr64 ? 8 : 4))
{
dprintf(" ");
Deref.OutputType();
dprintf(" *");
}
}
}
void
TypedDataSymbolGroupFormat::OutputOffset(void)
{
if (m_ExprErr)
{
// Error message will be shown elsewhere.
return;
}
if (m_CastData.m_DataSource == TDATA_NONE)
{
return;
}
if (m_CastData.m_DataSource & TDATA_REGISTER)
{
dprintf("@%s", RegNameFromIndex(m_CastData.m_SourceRegister));
}
else
{
ULONG Err;
ULONG64 Addr;
if (Err = m_CastData.GetAbsoluteAddress(&Addr))
{
// No address.
}
else
{
dprintf("%s", FormatAddr64(Addr));
if (m_CastData.m_DataSource & TDATA_BITFIELD)
{
dprintf(" %d..%d",
m_CastData.m_BitPos,
m_CastData.m_BitPos + m_CastData.m_BitSize - 1);
}
}
}
}
void
TypedDataSymbolGroupFormat::OutputType(void)
{
if (m_ExprErr)
{
// Error message will be shown elsewhere.
return;
}
m_CastData.OutputType();
}
struct TdrcContext
{
SymbolGroupEntry* Parent;
SymbolGroupEntry* Child;
};
SymbolGroupEntry*
TypedDataSymbolGroupFormat::RefreshChildren(void)
{
if (!(m_Entry->m_Params.Flags & DEBUG_SYMBOL_EXPANDED))
{
return m_Entry->m_Next;
}
TdrcContext Context;
Context.Parent = m_Entry;
Context.Child = m_Entry->m_Next;
m_CastData.GetChildren(g_Machine->m_Ptr64 ? 8 : 4,
CHLF_DEREF_UDT_POINTERS |
CHLF_DISALLOW_ACCESS,
RefreshChildrenCb, &Context);
return Context.Child;
}
ULONG
TypedDataSymbolGroupFormat::RefreshChildrenCb(PVOID _Context,
PSTR Name, TypedData* Child)
{
TdrcContext* Context = (TdrcContext*)_Context;
// The assumption is that the child information gathered
// by this enumeration will be the same as the original
// child information so every callback should have a
// matching child entry. Check just to be sure, though.
if (Context->Child &&
Context->Child->m_Parent == Context->Parent)
{
if (Context->Child->m_BaseFormatKind == SGFORMAT_TYPED_DATA &&
!strcmp(Name, Context->Child->m_Expr))
{
Context->Child->m_BaseData = *Child;
if (Context->Child->m_Format->m_Kind == SGFORMAT_TYPED_DATA)
{
((TypedDataSymbolGroupFormat*)Context->Child->m_Format)->
m_CastData.CopyDataSource(Child);
}
}
// Pass on the refresh to all children.
if (Context->Child->m_Format->m_Kind == SGFORMAT_TYPED_DATA)
{
Context->Child =
((TypedDataSymbolGroupFormat*)Context->Child->m_Format)->
RefreshChildren();
}
else
{
Context->Child = Context->Child->m_Next;
}
}
//
// Look for the next sibling.
//
ULONG ParentLevel =
Context->Parent->m_Params.Flags & DEBUG_SYMBOL_EXPANSION_LEVEL_MASK;
while (Context->Child &&
(Context->Child->m_Params.Flags &
DEBUG_SYMBOL_EXPANSION_LEVEL_MASK) > ParentLevel + 1)
{
Context->Child = Context->Child->m_Next;
}
return NO_ERROR;
}
void
TypedDataSymbolGroupFormat::UpdateParams(void)
{
ULONG NameUsed;
if (m_ExprErr)
{
m_Entry->m_Params.Module = 0;
m_Entry->m_Params.TypeId = 0;
m_Entry->m_Params.SubElements = 0;
m_Entry->m_Params.Flags &= ~(DEBUG_SYMBOL_IS_ARRAY |
DEBUG_SYMBOL_IS_FLOAT |
DEBUG_SYMBOL_READ_ONLY);
return;
}
m_Entry->m_Params.Module = m_CastData.m_Image ?
m_CastData.m_Image->m_BaseOfImage : 0;
m_Entry->m_Params.TypeId = m_CastData.m_Type;
// If this node was ever expanded the true child count has
// been determined and set. Otherwise make a quick guess.
// If the true child count is zero the estimate will be done
// repeatedly but should be fast as there are no children.
if (!m_Entry->m_Params.SubElements &&
m_CastData.EstimateChildrenCounts(CHLF_DEREF_UDT_POINTERS,
&m_Entry->m_Params.SubElements,
&NameUsed))
{
m_Entry->m_Params.SubElements = 0;
}
m_Entry->m_Params.Flags &= ~(DEBUG_SYMBOL_IS_ARRAY |
DEBUG_SYMBOL_IS_FLOAT |
DEBUG_SYMBOL_READ_ONLY);
if (m_CastData.IsArray())
{
m_Entry->m_Params.Flags |= DEBUG_SYMBOL_IS_ARRAY;
}
else if (m_CastData.IsFloat())
{
m_Entry->m_Params.Flags |= DEBUG_SYMBOL_IS_FLOAT;
}
if (!m_CastData.IsWritable())
{
m_Entry->m_Params.Flags |= DEBUG_SYMBOL_READ_ONLY;
}
}
void
TypedDataSymbolGroupFormat::TestImages(void)
{
ImageInfo* Image;
if (m_Entry->m_Params.Module == 0)
{
Image = NULL;
}
else
{
Image = g_Process ?
g_Process->FindImageByOffset(m_Entry->m_Params.Module, FALSE) :
NULL;
}
if (Image != m_CastData.m_Image)
{
// The module list has changed, avoid
// referencing a possibly invalid image.
m_Entry->m_BaseData.ReleaseImage();
m_CastType.ReleaseImage();
m_CastData.ReleaseImage();
}
}
//----------------------------------------------------------------------------
//
// ExprSymbolGroupFormat.
//
//----------------------------------------------------------------------------
ULONG
ExprSymbolGroupFormat::Refresh(TypedDataAccess AllowAccess)
{
g_DisableErrorPrint++;
EvalExpression* RelChain = g_EvalReleaseChain;
g_EvalReleaseChain = NULL;
__try
{
ULONG64 OldPtr;
EvalExpression* Eval = GetEvaluator(DEBUG_EXPR_CPLUSPLUS, FALSE);
if (AllowAccess == TDACC_NONE)
{
// Parse-only is automatically reset after evaluation.
Eval->m_ParseOnly++;
}
Eval->Evaluate(m_Entry->m_Expr, NULL, EXPRF_DEFAULT,
&m_Entry->m_BaseData);
ReleaseEvaluator(Eval);
m_ExprErr = NO_ERROR;
OldPtr = m_CastData.m_Ptr;
m_CastData = m_Entry->m_BaseData;
if (m_Entry->m_Cast)
{
m_ExprErr = m_CastData.CastTo(&m_CastType);
}
if (!m_ExprErr)
{
if (m_CastData.IsPointer() &&
m_CastData.m_Ptr != OldPtr)
{
RefreshChildren();
}
}
}
__except(CommandExceptionFilter(GetExceptionInformation()))
{
m_ExprErr = GetExceptionCode() - COMMAND_EXCEPTION_BASE;
}
g_EvalReleaseChain = RelChain;
g_DisableErrorPrint--;
m_ValueErr = m_ExprErr;
UpdateParams();
return m_ExprErr;
}
//----------------------------------------------------------------------------
//
// ExtSymbolGroupFormat.
//
//----------------------------------------------------------------------------
CHAR g_ExtensionOutputDataBuffer[MAX_NAME];
class ExtenOutputCallbacks : public IDebugOutputCallbacks
{
public:
// IUnknown.
STDMETHOD(QueryInterface)(
THIS_
IN REFIID InterfaceId,
OUT PVOID* Interface
);
STDMETHOD_(ULONG, AddRef)(
THIS
);
STDMETHOD_(ULONG, Release)(
THIS
);
// IDebugOutputCallbacks.
STDMETHOD(Output)(
THIS_
IN ULONG Mask,
IN PCSTR Text
);
};
STDMETHODIMP
ExtenOutputCallbacks::QueryInterface(
THIS_
IN REFIID InterfaceId,
OUT PVOID* Interface
)
{
*Interface = NULL;
if (IsEqualIID(InterfaceId, IID_IUnknown) ||
IsEqualIID(InterfaceId, IID_IDebugOutputCallbacks))
{
*Interface = (IDebugOutputCallbacks *)this;
AddRef();
return S_OK;
}
else
{
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG)
ExtenOutputCallbacks::AddRef(
THIS
)
{
// This class is designed to be static so
// there's no true refcount.
return 1;
}
STDMETHODIMP_(ULONG)
ExtenOutputCallbacks::Release(
THIS
)
{
// This class is designed to be static so
// there's no true refcount.
return 0;
}
STDMETHODIMP
ExtenOutputCallbacks::Output(
THIS_
IN ULONG Mask,
IN PCSTR Text
)
{
if ((strlen(Text) + strlen(g_ExtensionOutputDataBuffer)) <
sizeof(g_ExtensionOutputDataBuffer))
{
strcat(g_ExtensionOutputDataBuffer, Text);
}
return S_OK;
}
ExtenOutputCallbacks g_ExtensionOutputCallback;
ExtSymbolGroupFormat::ExtSymbolGroupFormat(SymbolGroupEntry* Entry,
DebugClient* Client)
: SymbolGroupFormat(Entry, SGFORMAT_EXTENSION)
{
m_Client = Client;
m_Output = NULL;
}
ExtSymbolGroupFormat::~ExtSymbolGroupFormat(void)
{
delete [] m_Output;
}
ULONG
ExtSymbolGroupFormat::CreateChildren(DebugSymbolGroup* Group)
{
if (m_ExprErr)
{
return m_ExprErr;
}
if (!m_Output || !m_Entry->m_Params.SubElements)
{
return NOTFOUND;
}
//
// Create a simple text child for each line past
// the first.
//
ULONG i;
PSTR Scan;
SymbolGroupEntry* AddAfter = m_Entry;
Scan = m_Output + strlen(m_Output) + 1;
for (i = 0; i < m_Entry->m_Params.SubElements; i++)
{
char Line[32];
sprintf(Line, "%d", i + 1);
SymbolGroupEntry* Child = new SymbolGroupEntry;
TextSymbolGroupFormat* ChildFormat =
new TextSymbolGroupFormat(Child, Scan, FALSE);
if (!Child || !ChildFormat)
{
delete Child;
return NOMEMORY;
}
Child->m_Format = ChildFormat;
Child->m_Expr = _strdup(Line);
if (!Child->m_Expr)
{
delete Child;
return NOMEMORY;
}
Child->m_Parent = m_Entry;
Child->m_Params.Flags = ((m_Entry->m_Params.Flags &
DEBUG_SYMBOL_EXPANSION_LEVEL_MASK) + 1) |
DEBUG_SYMBOL_READ_ONLY;
Child->m_Next = AddAfter->m_Next;
Child->m_BaseFormatKind = SGFORMAT_TEXT;
ChildFormat->m_ExprErr = NO_ERROR;
AddAfter->m_Next = Child;
AddAfter = Child;
Group->m_NumEntries++;
}
return NO_ERROR;
}
ULONG
ExtSymbolGroupFormat::Refresh(TypedDataAccess AllowAccess)
{
PDEBUG_OUTPUT_CALLBACKS OutCbSave;
m_Client->FlushCallbacks();
OutCbSave = m_Client->m_OutputCb;
OutCtlSave OldCtl;
PushOutCtl(DEBUG_OUTCTL_THIS_CLIENT |
DEBUG_OUTCTL_OVERRIDE_MASK | DEBUG_OUTCTL_NOT_LOGGED,
m_Client, &OldCtl);
g_ExtensionOutputDataBuffer[0] = 0;
m_Client->m_OutputCb = &g_ExtensionOutputCallback;
EvalExpression* RelChain = g_EvalReleaseChain;
g_EvalReleaseChain = NULL;
g_DisableErrorPrint++;
__try
{
char AddrStr[32];
ULONG64 Addr;
EvalExpression* Eval = GetEvaluator(DEBUG_EXPR_CPLUSPLUS, FALSE);
if (AllowAccess == TDACC_NONE)
{
// Parse-only is automatically reset after evaluation.
Eval->m_ParseOnly++;
}
Eval->Evaluate(m_Entry->m_Expr, NULL, EXPRF_DEFAULT,
&m_Entry->m_BaseData);
ReleaseEvaluator(Eval);
if (m_Entry->m_BaseData.GetAbsoluteAddress(&Addr))
{
m_ExprErr = MEMORY;
}
else
{
HRESULT ExtStatus;
sprintf(AddrStr, "0x%I64x", Addr);
CallAnyExtension(m_Client, NULL, m_Entry->m_Cast + 1, AddrStr,
FALSE, FALSE, &ExtStatus);
// Ignore the extension status as it's rarely meaningful.
m_ExprErr = NO_ERROR;
}
}
__except(CommandExceptionFilter(GetExceptionInformation()))
{
m_ExprErr = GetExceptionCode() - COMMAND_EXCEPTION_BASE;
}
PopOutCtl(&OldCtl);
m_Client->FlushCallbacks();
m_Client->m_OutputCb = OutCbSave;
g_EvalReleaseChain = RelChain;
g_DisableErrorPrint--;
m_ValueErr = m_ExprErr;
if (m_ExprErr)
{
return m_ExprErr;
}
if (m_Output)
{
delete [] m_Output;
}
m_Output = new CHAR[strlen(g_ExtensionOutputDataBuffer) + 1];
if (!m_Output)
{
return NOMEMORY;
}
strcpy(m_Output, g_ExtensionOutputDataBuffer);
//
// Convert newlines to terminators for convenient output.
// Number of children == number of newlines - 1.
//
// While scanning, also update any children of this
// node with refreshed pointers.
//
PSTR Scan = m_Output;
SymbolGroupEntry* Child = m_Entry->m_Next;
m_Entry->m_Params.SubElements = 0;
while (Scan = strchr(Scan, '\n'))
{
m_Entry->m_Params.SubElements++;
*Scan++ = 0;
if (Child && Child->m_Parent == m_Entry)
{
// It would be cleaner if the child's Refresh()
// looked up the text value from the parent,
// but all the per-line lookups would be a lot
// of wasted effort.
((TextSymbolGroupFormat*)Child->m_Format)->m_Text = Scan;
Child = Child->m_Next;
}
}
if (m_Entry->m_Params.SubElements)
{
m_Entry->m_Params.SubElements--;
}
//
// Any excess children are now blank.
//
while (Child && Child->m_Parent == m_Entry)
{
((TextSymbolGroupFormat*)Child->m_Format)->m_Text = "";
Child = Child->m_Next;
}
return NO_ERROR;
}
ULONG
ExtSymbolGroupFormat::Write(PCSTR Value)
{
// No modifications allowed.
return MEMORY;
}
void
ExtSymbolGroupFormat::OutputValue(void)
{
if (m_ValueErr)
{
dprintf("<%s error>", ErrorString(m_ValueErr));
}
else
{
dprintf("%s", m_Output);
}
}
void
ExtSymbolGroupFormat::OutputType(void)
{
dprintf("%s", m_Entry->m_Cast);
}
void
ExtSymbolGroupFormat::OutputOffset(void)
{
// No offset.
}
//----------------------------------------------------------------------------
//
// TextSymbolGroupFormat.
//
//----------------------------------------------------------------------------
TextSymbolGroupFormat::TextSymbolGroupFormat(SymbolGroupEntry* Entry,
PSTR Text, BOOL Own)
: SymbolGroupFormat(Entry, SGFORMAT_TEXT)
{
m_Text = Text;
m_Own = Own;
Entry->m_Flags |= DEBUG_SYMBOL_READ_ONLY;
}
TextSymbolGroupFormat::~TextSymbolGroupFormat(void)
{
if (m_Own)
{
delete [] m_Text;
}
}
ULONG
TextSymbolGroupFormat::CreateChildren(DebugSymbolGroup* Group)
{
return NOTFOUND;
}
ULONG
TextSymbolGroupFormat::Refresh(TypedDataAccess AllowAccess)
{
return NO_ERROR;
}
ULONG
TextSymbolGroupFormat::Write(PCSTR Value)
{
// No modifications allowed.
return MEMORY;
}
void
TextSymbolGroupFormat::OutputValue(void)
{
dprintf("%s", m_Text);
}
void
TextSymbolGroupFormat::OutputType(void)
{
// No type.
}
void
TextSymbolGroupFormat::OutputOffset(void)
{
// No offset.
}
//----------------------------------------------------------------------------
//
// IDebugSymbolGroup.
//
//----------------------------------------------------------------------------
DebugSymbolGroup::DebugSymbolGroup(DebugClient* Client, ULONG ScopeGroup)
{
m_Client = Client;
m_ScopeGroup = ScopeGroup;
m_Refs = 1;
m_NumEntries = 0;
m_Entries = NULL;
m_LastClassExpanded = TRUE;
}
DebugSymbolGroup::~DebugSymbolGroup(void)
{
SymbolGroupEntry* Next;
while (m_Entries)
{
Next = m_Entries->m_Next;
delete m_Entries;
m_Entries = Next;
}
}
STDMETHODIMP
DebugSymbolGroup::QueryInterface(
THIS_
IN REFIID InterfaceId,
OUT PVOID* Interface
)
{
HRESULT Status;
*Interface = NULL;
Status = S_OK;
if (DbgIsEqualIID(InterfaceId, IID_IUnknown) ||
DbgIsEqualIID(InterfaceId, IID_IDebugSymbolGroup))
{
AddRef();
*Interface = (IDebugSymbolGroup *)this;
}
else
{
Status = E_NOINTERFACE;
}
return Status;
}
STDMETHODIMP_(ULONG)
DebugSymbolGroup::AddRef(
THIS
)
{
return InterlockedIncrement((PLONG)&m_Refs);
}
STDMETHODIMP_(ULONG)
DebugSymbolGroup::Release(
THIS
)
{
LONG Refs = InterlockedDecrement((PLONG)&m_Refs);
if (Refs == 0)
{
ENTER_ENGINE();
delete this;
LEAVE_ENGINE();
}
return Refs;
}
STDMETHODIMP
DebugSymbolGroup::GetNumberSymbols(
THIS_
OUT PULONG Number
)
{
ENTER_ENGINE();
*Number = m_NumEntries;
LEAVE_ENGINE();
return S_OK;
}
STDMETHODIMP
DebugSymbolGroup::AddSymbol(
THIS_
IN PCSTR Name,
IN OUT PULONG Index
)
{
HRESULT Status;
ENTER_ENGINE();
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else
{
SymbolGroupEntry* Entry;
if ((Status = NewEntry(Name, NULL, &Entry)) == S_OK)
{
LinkEntry(Entry, Index);
}
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugSymbolGroup::RemoveSymbolByName(
THIS_
IN PCSTR Name
)
{
HRESULT Status;
ENTER_ENGINE();
// Don't delete children.
SymbolGroupEntry* Entry = FindEntryByExpr(NULL, NULL, Name);
if (Entry)
{
DeleteEntry(Entry);
Status = S_OK;
}
else
{
Status = E_INVALIDARG;
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugSymbolGroup::RemoveSymbolByIndex(
THIS_
IN ULONG Index
)
{
HRESULT Status;
ENTER_ENGINE();
// Don't delete children.
SymbolGroupEntry* Entry = FindEntryByIndex(Index);
if (Entry && !Entry->m_Parent)
{
DeleteEntry(Entry);
Status = S_OK;
}
else
{
Status = E_INVALIDARG;
}
#if DBG_SYMGROUP_ENABLED
dprintf("Deleted %lx\n", Index);
ShowAll();
#endif
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugSymbolGroup::GetSymbolName(
THIS_
IN ULONG Index,
OUT OPTIONAL PSTR Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG NameSize
)
{
HRESULT Status;
ENTER_ENGINE();
SymbolGroupEntry* Entry = FindEntryByIndex(Index);
if (Entry)
{
Status = FillStringBuffer(Entry->m_Expr, 0,
Buffer, BufferSize, NameSize);
}
else
{
Status = E_INVALIDARG;
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugSymbolGroup::GetSymbolParameters(
THIS_
IN ULONG Start,
IN ULONG Count,
OUT /* size_is(Count) */ PDEBUG_SYMBOL_PARAMETERS Params
)
{
HRESULT Status;
ENTER_ENGINE();
SymbolGroupEntry* Entry;
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
goto Exit;
}
else if (!(Entry = FindEntryByIndex(Start)))
{
Status = E_INVALIDARG;
goto Exit;
}
TestImages();
Status = S_OK;
#if DBG_SYMGROUP_ENABLED
dprintf("GetSymbolParameters: will return %lx sym params\n", Count);
ShowAll();
#endif
while (Count)
{
if (!Entry)
{
Status = E_INVALIDARG;
goto Exit;
}
*Params = Entry->m_Params;
// Update the parent index on demand so that it
// doesn't have to be tracked through all list updates.
Params->ParentSymbol = FindEntryIndex(Entry->m_Parent);
Params++;
Entry = Entry->m_Next;
Count--;
}
#if DBG_SYMGROUP_ENABLED
dprintf("End GetSymbolParameters\n");
ShowAll();
#endif
Exit:
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugSymbolGroup::ExpandSymbol(
THIS_
IN ULONG Index,
IN BOOL Expand
)
{
HRESULT Status;
ENTER_ENGINE();
SymbolGroupEntry* Entry;
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
}
else if (!(Entry = FindEntryByIndex(Index)))
{
Status = E_INVALIDARG;
}
else
{
TestImages();
Status = SetEntryExpansion(Entry, Expand);
}
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugSymbolGroup::OutputSymbols(
THIS_
IN ULONG OutputControl,
IN ULONG Flags,
IN ULONG Start,
IN ULONG Count
)
{
HRESULT Status;
if (Flags & ~(DEBUG_OUTPUT_SYMBOLS_NO_NAMES |
DEBUG_OUTPUT_SYMBOLS_NO_OFFSETS |
DEBUG_OUTPUT_SYMBOLS_NO_VALUES |
DEBUG_OUTPUT_SYMBOLS_NO_TYPES))
{
return E_INVALIDARG;
}
ENTER_ENGINE();
SymbolGroupEntry* Entry;
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
goto Exit;
}
else if (!(Entry = FindEntryByIndex(Start)))
{
Status = E_INVALIDARG;
goto Exit;
}
TestImages();
#if DBG_SYMGROUP_ENABLED
dprintf("Output\n");
ShowAll();
#endif
OutCtlSave OldCtl;
if (!PushOutCtl(OutputControl, m_Client, &OldCtl))
{
Status = E_INVALIDARG;
goto Exit;
}
Status = S_OK;
while (Count)
{
if (!Entry)
{
Status = E_INVALIDARG;
break;
}
if (!(Entry->m_Flags & SYMBOL_ECLIPSED))
{
Entry->m_Format->Refresh(TDACC_REQUIRE);
}
if (!(Flags & DEBUG_OUTPUT_SYMBOLS_NO_NAMES))
{
dprintf("%s%s", Entry->m_Expr, DEBUG_OUTPUT_NAME_END);
}
if (!(Flags & DEBUG_OUTPUT_SYMBOLS_NO_VALUES))
{
if (!(Entry->m_Flags & SYMBOL_ECLIPSED))
{
Entry->m_Format->OutputValue();
}
else
{
dprintf("<Eclipsed>");
}
dprintf(DEBUG_OUTPUT_VALUE_END);
}
if (!(Flags & DEBUG_OUTPUT_SYMBOLS_NO_OFFSETS))
{
Entry->m_Format->OutputOffset();
dprintf(DEBUG_OUTPUT_OFFSET_END);
}
if (!(Flags & DEBUG_OUTPUT_SYMBOLS_NO_TYPES))
{
Entry->m_Format->OutputType();
dprintf(DEBUG_OUTPUT_TYPE_END);
}
Entry = Entry->m_Next;
Count--;
}
PopOutCtl(&OldCtl);
Exit:
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugSymbolGroup::WriteSymbol(
THIS_
IN ULONG Index,
IN PCSTR Value
)
{
if (!Value)
{
return E_INVALIDARG;
}
HRESULT Status;
ENTER_ENGINE();
SymbolGroupEntry* Entry;
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
goto Exit;
}
else if (!(Entry = FindEntryByIndex(Index)))
{
Status = E_INVALIDARG;
goto Exit;
}
TestImages();
#if DBG_SYMGROUP_ENABLED
dprintf("WriteSymbol %lx : %s\n", Index, Value);
#endif
Status = Entry->m_Format->Write(Value) ? E_FAIL : S_OK;
Exit:
LEAVE_ENGINE();
return Status;
}
STDMETHODIMP
DebugSymbolGroup::OutputAsType(
THIS_
IN ULONG Index,
IN PCSTR Type
)
{
HRESULT Status;
ENTER_ENGINE();
SymbolGroupEntry* Entry;
PSTR Cast = NULL;
ULONG CastLen;
SymbolGroupFormat* NewFormat = NULL;
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
Status = E_UNEXPECTED;
goto Exit;
}
else if (!(Entry = FindEntryByIndex(Index)))
{
Status = E_INVALIDARG;
goto Exit;
}
TestImages();
if ((Entry->m_BaseFormatKind != SGFORMAT_TYPED_DATA &&
Entry->m_BaseFormatKind != SGFORMAT_EXPRESSION &&
Entry->m_BaseFormatKind != SGFORMAT_EXTENSION) ||
Entry->m_Format->m_ExprErr)
{
Status = E_INVALIDARG;
goto Exit;
}
if (Type && *Type)
{
// Allocate extra space for cast evaluation expression.
CastLen = strlen(Type) + 1;
Cast = (PSTR)malloc(CastLen + 3);
if (!Cast)
{
Status = E_OUTOFMEMORY;
goto Exit;
}
memcpy(Cast, Type, CastLen);
}
//
// Entries may need to be shifted between kinds of
// symbol group formats according to what the cast is.
//
if (Cast && *Cast == '!')
{
//
// Entry needs to use an extension format.
//
NewFormat = new ExtSymbolGroupFormat(Entry, m_Client);
if (!NewFormat)
{
Status = E_OUTOFMEMORY;
goto Exit;
}
NewFormat->m_ExprErr = NO_ERROR;
}
else
{
TypedDataSymbolGroupFormat* TdFormat;
//
// Entry is not an extension entry.
//
TdFormat = Entry->m_BaseFormatKind == SGFORMAT_EXPRESSION ?
new ExprSymbolGroupFormat(Entry) :
new TypedDataSymbolGroupFormat(Entry);
if (!TdFormat)
{
Status = E_OUTOFMEMORY;
goto Exit;
}
NewFormat = TdFormat;
TdFormat->m_CastData = Entry->m_BaseData;
if (Cast)
{
//
// Determine cast type.
//
memmove(Cast + 1, Cast, CastLen);
Cast[0] = '(';
Cast[CastLen] = ')';
Cast[CastLen + 1] = '0';
Cast[CastLen + 2] = 0;
g_DisableErrorPrint++;
EvalExpression* RelChain = g_EvalReleaseChain;
g_EvalReleaseChain = NULL;
__try
{
EvalExpression* Eval =
GetEvaluator(DEBUG_EXPR_CPLUSPLUS, FALSE);
Eval->Evaluate(Cast, NULL, EXPRF_DEFAULT,
&TdFormat->m_CastType);
ReleaseEvaluator(Eval);
}
__except(CommandExceptionFilter(GetExceptionInformation()))
{
g_DisableErrorPrint--;
Status = E_FAIL;
goto Exit;
}
g_EvalReleaseChain = RelChain;
g_DisableErrorPrint--;
memmove(Cast, Cast + 1, CastLen);
Cast[CastLen - 1] = 0;
if (TdFormat->m_CastData.CastTo(&TdFormat->m_CastType))
{
Status = E_FAIL;
goto Exit;
}
}
TdFormat->m_ExprErr = NO_ERROR;
TdFormat->m_ValueErr = TdFormat->m_ExprErr;
TdFormat->UpdateParams();
}
// The cast may have radically altered the subelements
// for this entry so collapse the entry.
SetEntryExpansion(Entry, FALSE);
free(Entry->m_Cast);
Entry->m_Cast = Cast;
Cast = NULL;
delete Entry->m_Format;
Entry->m_Format = NewFormat;
NewFormat = NULL;
Status = S_OK;
Exit:
free(Cast);
delete NewFormat;
LEAVE_ENGINE();
return Status;
}
//
// Private DebugSymbolGroup methods
//
SymbolGroupEntry*
DebugSymbolGroup::FindEntryByIndex(ULONG Index)
{
SymbolGroupEntry* Entry;
for (Entry = m_Entries; Entry; Entry = Entry->m_Next)
{
if (Index-- == 0)
{
return Entry;
}
}
return NULL;
}
SymbolGroupEntry*
DebugSymbolGroup::FindEntryByExpr(SymbolGroupEntry* Parent,
SymbolGroupEntry* After,
PCSTR Expr)
{
SymbolGroupEntry* Entry;
// Entries are sorted by parent/child so children will
// immediately follow a parent.
if (!After)
{
Entry = Parent ? Parent->m_Next : m_Entries;
}
else
{
Entry = After->m_Next;
}
while (Entry)
{
if (Entry->m_Parent == Parent &&
!strcmp(Expr, Entry->m_Expr))
{
return Entry;
}
Entry = Entry->m_Next;
}
return NULL;
}
ULONG
DebugSymbolGroup::FindEntryIndex(SymbolGroupEntry* Entry)
{
SymbolGroupEntry* Cur;
ULONG Index = 0;
for (Cur = m_Entries; Cur; Cur = Cur->m_Next)
{
if (Cur == Entry)
{
return Index;
}
Index++;
}
return DEBUG_ANY_ID;
}
void
DebugSymbolGroup::DeleteEntry(SymbolGroupEntry* Entry)
{
SymbolGroupEntry* Prev;
SymbolGroupEntry* Cur;
//
// Locate the entry.
//
Prev = NULL;
for (Cur = m_Entries; Cur; Cur = Cur->m_Next)
{
if (Cur == Entry)
{
break;
}
Prev = Cur;
}
if (!Cur)
{
return;
}
DeleteChildren(Entry);
if (!Prev)
{
m_Entries = Entry->m_Next;
}
else
{
Prev->m_Next = Entry->m_Next;
}
delete Entry;
m_NumEntries--;
}
void
DebugSymbolGroup::DeleteChildren(SymbolGroupEntry* Parent)
{
//
// The list of entries is sorted by parent/child
// relationship so any children follow the parent
// immediately.
//
SymbolGroupEntry* Cur;
SymbolGroupEntry* Del;
ULONG Level = Parent->m_Params.Flags & DEBUG_SYMBOL_EXPANSION_LEVEL_MASK;
Cur = Parent->m_Next;
while (Cur)
{
if ((Cur->m_Params.Flags & DEBUG_SYMBOL_EXPANSION_LEVEL_MASK) <= Level)
{
// Any entry of the same or less expansion than the parent
// cannot be a child so stop.
break;
}
else
{
// Found a child entry so delete.
Del = Cur;
Cur = Cur->m_Next;
delete Del;
m_NumEntries--;
}
}
Parent->m_Next = Cur;
}
void
DebugSymbolGroup::LinkEntry(IN SymbolGroupEntry* Entry,
IN OUT PULONG Index)
{
//
// Find insertion index point.
//
SymbolGroupEntry* Prev;
SymbolGroupEntry* Cur;
ULONG CurIdx = 0;
Prev = NULL;
for (Cur = m_Entries; Cur; Cur = Cur->m_Next)
{
if (CurIdx == *Index)
{
break;
}
CurIdx++;
Prev = Cur;
}
if (!Prev)
{
Entry->m_Next = m_Entries;
m_Entries = Entry;
}
else
{
Entry->m_Next = Prev->m_Next;
Prev->m_Next = Entry;
}
m_NumEntries++;
*Index = CurIdx;
#if DBG_SYMGROUP_ENABLED
dprintf("Added %s at %lx\n", Entry->m_Expr, *Index);
ShowAll();
#endif
}
HRESULT
DebugSymbolGroup::NewEntry(IN PCSTR Expr,
IN OPTIONAL PSYMBOL_INFO SymInfo,
OUT SymbolGroupEntry** EntryRet)
{
HRESULT Status;
SymbolGroupEntry* Entry = new SymbolGroupEntry;
TypedDataSymbolGroupFormat* Format = SymInfo ?
new TypedDataSymbolGroupFormat(Entry) :
new ExprSymbolGroupFormat(Entry);
if (!Entry || !Format)
{
delete Entry;
return E_OUTOFMEMORY;
}
Entry->m_Format = Format;
if (!(Entry->m_Expr = _strdup(Expr)))
{
Status = E_OUTOFMEMORY;
goto Error;
}
//
// We're just creating the entries here so there's
// no need to attempt to read content yet. Refresh
// will be called later.
//
if (SymInfo)
{
Format->m_ExprErr = Entry->m_BaseData.
SetToSymbol(g_Process, (PSTR)Expr, SymInfo,
TDACC_NONE, g_Machine->m_Ptr64 ? 8 : 4);
Entry->m_BaseFormatKind = SGFORMAT_TYPED_DATA;
Format->m_CastData = Entry->m_BaseData;
Format->UpdateParams();
}
else
{
// We have to evaluate in order to determine
// the result type, but evaluate without access
// to just evaluate the result type without
// requiring memory access.
Format->Refresh(TDACC_NONE);
Entry->m_BaseFormatKind = SGFORMAT_EXPRESSION;
Format->m_CastData = Entry->m_BaseData;
}
*EntryRet = Entry;
return S_OK;
Error:
delete Entry;
return Status;
}
HRESULT
DebugSymbolGroup::SetEntryExpansion(IN SymbolGroupEntry* Entry,
IN BOOL Expand)
{
HRESULT Status;
if (Expand &&
(Entry->m_Params.Flags & DEBUG_SYMBOL_EXPANSION_LEVEL_MASK) ==
DEBUG_SYMBOL_EXPANSION_LEVEL_MASK)
{
return E_INVALIDARG;
}
#if DBG_SYMGROUP_ENABLED
dprintf("Expanding %s (%lx to be %s)\n",
Entry->m_Expr, Entry->m_Params.SubElements,
Expand ? "created" : "deleted");
ShowAll();
#endif
//
// Special case - check and store if "this" is expanded/collapsed
//
if (!strcmp(Entry->m_Expr, "this"))
{
m_LastClassExpanded = Expand;
}
if (!Expand)
{
if (Entry->m_Params.Flags & DEBUG_SYMBOL_EXPANDED)
{
DeleteChildren(Entry);
Entry->m_Params.Flags &= ~DEBUG_SYMBOL_EXPANDED;
}
Status = S_OK;
}
else
{
if (Entry->m_Params.Flags & DEBUG_SYMBOL_EXPANDED)
{
Status = S_OK;
}
else
{
if (!Entry->m_Format->CreateChildren(this))
{
Entry->m_Params.Flags |= DEBUG_SYMBOL_EXPANDED;
Status = S_OK;
}
else
{
Status = E_FAIL;
}
}
}
#if DBG_SYMGROUP_ENABLED
dprintf("Expanded %s (%lx %s)\n",
Entry->m_Expr, Entry->m_Params.SubElements,
(Entry->m_Params.Flags & DEBUG_SYMBOL_EXPANDED) ?
"new" : "deleted");
ShowAll();
#endif
return Status;
}
HRESULT
DebugSymbolGroup::AddCurrentLocals(void)
{
// Always return success since this request is
// processed even if we didn't add anything.
HRESULT Status = S_OK;
RequireCurrentScope();
SymbolGroupEntry* Entry;
for (Entry = m_Entries; Entry; Entry = Entry->m_Next)
{
if (!Entry->m_Parent)
{
// Assume everything is visible now.
Entry->m_Flags &= ~(SYMBOL_ECLIPSED | SYMBOL_IN_SCOPE);
}
}
EnumerateLocals(AddAllScopedSymbols, (PVOID)this);
Restart:
#if DBG_SYMGROUP_ENABLED
dprintf("Enum locals loop:\n");
ShowAll();
#endif
for (Entry = m_Entries; Entry; Entry = Entry->m_Next)
{
if (!Entry->m_Parent)
{
if (!(Entry->m_Flags & SYMBOL_IN_SCOPE))
{
DeleteEntry(Entry);
// Restart scan as the list just changed.
goto Restart;
}
}
}
SymbolGroupEntry* ThisEntry = NULL;
for (Entry = m_Entries; Entry; Entry = Entry->m_Next)
{
if (!Entry->m_Parent)
{
if (Entry->m_Flags & SYMBOL_IN_SCOPE)
{
Entry->m_Flags &= ~SYMBOL_IN_SCOPE;
// Remember if there's a plain "this" reference
// for later expansion.
if (!strcmp(Entry->m_Expr, "this"))
{
ThisEntry = Entry;
}
}
}
}
if (ThisEntry && m_LastClassExpanded)
{
SetEntryExpansion(ThisEntry, TRUE);
}
return Status;
}
ULONG
DebugSymbolGroup::FindLocalInsertionIndex(SymbolGroupEntry* Entry)
{
ULONG Index = 0;
SymbolGroupEntry* Compare;
for (Compare = m_Entries; Compare; Compare = Compare->m_Next, Index++)
{
if (Compare->m_Parent)
{
continue;
}
//
// Sort arguments by address and locals by name.
//
if ((Compare->m_Params.Flags & DEBUG_SYMBOL_IS_ARGUMENT) ||
(Entry->m_Params.Flags & DEBUG_SYMBOL_IS_ARGUMENT))
{
if ((Compare->m_Params.Flags & DEBUG_SYMBOL_IS_ARGUMENT) &&
(Entry->m_Params.Flags & DEBUG_SYMBOL_IS_ARGUMENT))
{
// We can only meaningfully sort frame-relative
// arguments.
if (Compare->m_BaseFormatKind == SGFORMAT_TYPED_DATA &&
Entry->m_BaseFormatKind == SGFORMAT_TYPED_DATA &&
(Compare->m_BaseData.m_DataSource &
TDATA_FRAME_RELATIVE) &&
(Entry->m_BaseData.m_DataSource &
TDATA_FRAME_RELATIVE) &&
Compare->m_BaseData.m_SourceOffset >
Entry->m_BaseData.m_SourceOffset)
{
return Index;
}
}
else if (Entry->m_Params.Flags & DEBUG_SYMBOL_IS_ARGUMENT)
{
return Index;
}
}
else
{
if (_stricmp(Compare->m_Expr, Entry->m_Expr) > 0)
{
return Index;
}
}
}
// Place at the end.
return m_NumEntries;
}
BOOL CALLBACK
DebugSymbolGroup::AddAllScopedSymbols(PSYMBOL_INFO SymInfo,
ULONG Size,
PVOID Context)
{
DebugSymbolGroup* Caller = (DebugSymbolGroup*)Context;
BOOL SymbolEclipsed = FALSE;
SymbolGroupEntry* Entry;
//
// Ingore symbols which do not match Caller's scope
//
if (Caller->m_ScopeGroup == DEBUG_SCOPE_GROUP_ARGUMENTS)
{
if (!(SymInfo->Flags & SYMFLAG_PARAMETER))
{
return TRUE;
}
} else if (Caller->m_ScopeGroup != DEBUG_SCOPE_GROUP_LOCALS)
{
return TRUE;
}
Entry = Caller->FindEntryByExpr(NULL, NULL, SymInfo->Name);
while (Entry)
{
if (Entry->m_Format->m_Kind == SGFORMAT_TYPED_DATA &&
Entry->m_BaseData.m_Image &&
Entry->m_BaseData.m_Image->m_BaseOfImage == SymInfo->ModBase &&
Entry->m_BaseData.m_Type == SymInfo->TypeIndex &&
Entry->m_BaseData.EquivInfoSource(SymInfo,
Entry->m_BaseData.m_Image))
{
// The entry matches the enumerated symbol.
Entry->m_Flags |= SYMBOL_IN_SCOPE;
return TRUE;
}
else if (!(Entry->m_Flags & SYMBOL_IN_SCOPE))
{
// The enumerated symbol hasn't been
// processed by this routine yet, so it
// must be some old local var from previous scope.
Entry->m_Flags |= SYMBOL_ECLIPSED;
}
else
{
// The enumerated symbol is a newer same-named local,
// so the current entry is the symbol to be eclipsed.
SymbolEclipsed = TRUE;
}
Entry = Caller->FindEntryByExpr(NULL, Entry, SymInfo->Name);
}
if (Caller->NewEntry(SymInfo->Name, SymInfo, &Entry) != S_OK)
{
return FALSE;
}
Entry->m_Flags |= SYMBOL_IN_SCOPE;
Entry->m_Flags |= SymbolEclipsed ? SYMBOL_ECLIPSED : 0;
Entry->m_Params.Flags |=
(SymInfo->Flags & SYMFLAG_PARAMETER) ? DEBUG_SYMBOL_IS_ARGUMENT :
((SymInfo->Flags & SYMFLAG_LOCAL) ? DEBUG_SYMBOL_IS_LOCAL : 0);
ULONG Index = Caller->FindLocalInsertionIndex(Entry);
if (SymbolEclipsed && Index)
{
// Symbol at 'Index' is the symbol with same name,
// add this *before* 'Index' since order
// is important when checking for the inner-scope symbol
Index--;
}
Caller->LinkEntry(Entry, &Index);
#if DBG_SYMGROUP_ENABLED
dprintf("%lx : Adding local %s %s\n",
Index, (SymInfo->Flags & SYMFLAG_PARAMETER) ?
"arg" : " ", SymInfo->Name);
#endif
return TRUE;
}
void
DebugSymbolGroup::TestImages(void)
{
SymbolGroupEntry* Entry = m_Entries;
while (Entry)
{
Entry->m_Format->TestImages();
Entry = Entry->m_Next;
}
}
void
DebugSymbolGroup::ShowAll(void)
{
SymbolGroupEntry* Entry = m_Entries;
ULONG Index = 0;
dprintf("Total %d syms\n", m_NumEntries);
dprintf("Idx Sub ExFlags Par Flag Mod Expr (Cast)\n");
while (Entry)
{
dprintf64("%2lx:%4lx %8lx %8lx %4lx %p %s (%s)\n",
Index++,
Entry->m_Params.SubElements,
Entry->m_Params.Flags,
FindEntryIndex(Entry->m_Parent),
Entry->m_Flags,
Entry->m_Params.Module,
Entry->m_Expr,
Entry->m_Cast ? Entry->m_Cast : "<none>");
Entry = Entry->m_Next;
}
}