From 8e5e9585ebc3d97b0c65634d61f2b28ee379fca3 Mon Sep 17 00:00:00 2001 From: Igor Kulaychuk Date: Mon, 10 Jul 2017 13:45:56 +0300 Subject: [PATCH] Add varobj walking code --- src/debug/debugger/main.cpp | 14 +- src/debug/debugger/torelease.h | 1 + src/debug/debugger/varobj.cpp | 587 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 597 insertions(+), 5 deletions(-) diff --git a/src/debug/debugger/main.cpp b/src/debug/debugger/main.cpp index 7aeb845..b9d710a 100644 --- a/src/debug/debugger/main.cpp +++ b/src/debug/debugger/main.cpp @@ -126,6 +126,7 @@ HRESULT GetFrameLocation(ICorDebugFrame *pFrame, // Varobj HRESULT ListVariables(ICorDebugFrame *pFrame, std::string &output); +void NotifyEvalComplete(); // TypePrinter #include "typeprinter.h" @@ -566,12 +567,20 @@ public: virtual HRESULT STDMETHODCALLTYPE EvalComplete( /* [in] */ ICorDebugAppDomain *pAppDomain, /* [in] */ ICorDebugThread *pThread, - /* [in] */ ICorDebugEval *pEval) { return S_OK; } + /* [in] */ ICorDebugEval *pEval) + { + NotifyEvalComplete(); + return S_OK; + } virtual HRESULT STDMETHODCALLTYPE EvalException( /* [in] */ ICorDebugAppDomain *pAppDomain, /* [in] */ ICorDebugThread *pThread, - /* [in] */ ICorDebugEval *pEval) { return S_OK; } + /* [in] */ ICorDebugEval *pEval) + { + NotifyEvalComplete(); + return S_OK; + } virtual HRESULT STDMETHODCALLTYPE CreateProcess( /* [in] */ ICorDebugProcess *pProcess) @@ -586,6 +595,7 @@ public: { out_printf("*stopped,reason=\"exited\",exit-code=\"%i\"\n", 0); g_processExited = true; + NotifyEvalComplete(); return S_OK; } diff --git a/src/debug/debugger/torelease.h b/src/debug/debugger/torelease.h index e180659..771c0bc 100644 --- a/src/debug/debugger/torelease.h +++ b/src/debug/debugger/torelease.h @@ -67,6 +67,7 @@ public: } } + ToRelease(ToRelease&& that) noexcept : m_ptr(that.m_ptr) { that.m_ptr = nullptr; } private: ToRelease(const ToRelease& that) = delete; T* m_ptr; diff --git a/src/debug/debugger/varobj.cpp b/src/debug/debugger/varobj.cpp index 8b29c7d..67d851a 100644 --- a/src/debug/debugger/varobj.cpp +++ b/src/debug/debugger/varobj.cpp @@ -7,8 +7,10 @@ #include #include +#include #include #include +#include #include #include @@ -101,7 +103,7 @@ static HRESULT PrintStringValue(ICorDebugValue * pValue, std::string &output) return S_OK; } -HRESULT PrintValue(ICorDebugValue *pInputValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, std::string &output) +HRESULT PrintValue(ICorDebugValue *pInputValue, ICorDebugILFrame * pILFrame, std::string &output) { HRESULT Status; @@ -270,6 +272,571 @@ HRESULT PrintValue(ICorDebugValue *pInputValue, ICorDebugILFrame * pILFrame, IMe return S_OK; } +static HRESULT GetNumChild(ICorDebugValue *pInputValue, + ICorDebugType *pTypeCast, + ULONG &numstatic, + ULONG &numinstance) +{ + numstatic = 0; + numinstance = 0; + + HRESULT Status = S_OK; + + BOOL isNull = FALSE; + ToRelease pValue; + + IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull)); + + if (isNull) return S_OK; + + ToRelease pArrayValue; + if (SUCCEEDED(pValue->QueryInterface(IID_ICorDebugArrayValue, (LPVOID *) &pArrayValue))) + { + ULONG32 nRank; + IfFailRet(pArrayValue->GetRank(&nRank)); + + ULONG32 cElements; + IfFailRet(pArrayValue->GetCount(&cElements)); + + numinstance = cElements; + + return S_OK; + } + + mdTypeDef currentTypeDef; + ToRelease pClass; + ToRelease pValue2; + ToRelease pType; + ToRelease pModule; + IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2)); + if(pTypeCast == NULL) + IfFailRet(pValue2->GetExactType(&pType)); + else + { + pType = pTypeCast; + pType->AddRef(); + } + + CorElementType corElemType; + IfFailRet(pType->GetType(&corElemType)); + if (corElemType == ELEMENT_TYPE_STRING) + return S_OK; + + IfFailRet(pType->GetClass(&pClass)); + IfFailRet(pClass->GetModule(&pModule)); + IfFailRet(pClass->GetToken(¤tTypeDef)); + + ToRelease pMDUnknown; + ToRelease pMD; + IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown)); + IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD)); + + std::string baseTypeName; + ToRelease pBaseType; + if(SUCCEEDED(pType->GetBase(&pBaseType)) && pBaseType != NULL && SUCCEEDED(TypePrinter::GetTypeOfValue(pBaseType, baseTypeName))) + { + if(baseTypeName == "System.Enum") + return S_OK; + else if(baseTypeName != "System.Object" && baseTypeName != "System.ValueType") + { + // Add fields of base class + ULONG numstaticBase = 0; + ULONG numinstanceBase = 0; + IfFailRet(GetNumChild(pInputValue, pBaseType, numstaticBase, numinstanceBase)); + numstatic += numstaticBase; + numinstance += numinstanceBase; + } + } + + ULONG numFields = 0; + HCORENUM fEnum = NULL; + mdFieldDef fieldDef; + ULONG numstaticBack = 0; + ULONG numinstanceBack = 0; + while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0) + { + ULONG nameLen = 0; + DWORD fieldAttr = 0; + WCHAR mdName[mdNameLen] = {0}; + if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, NULL, NULL))) + { + bool is_back = (char)mdName[0] == '<'; + if(fieldAttr & fdLiteral) + continue; + + if (fieldAttr & fdStatic) + { + numstatic++; + numstaticBack += is_back ? 1 : 0; + } + else + { + numinstance++; + numinstanceBack += is_back ? 1 : 0; + } + } + } + pMD->CloseEnum(fEnum); + + mdProperty propertyDef; + ULONG numProperties = 0; + HCORENUM propEnum = NULL; + while(SUCCEEDED(pMD->EnumProperties(&propEnum, currentTypeDef, &propertyDef, 1, &numProperties)) && numProperties != 0) + { + mdTypeDef propertyClass; + + DWORD propFlags; + UVCP_CONSTANT pDefaultValue; + ULONG cchDefaultValue; + mdMethodDef mdGetter; + mdMethodDef rmdOtherMethod; + ULONG cOtherMethod; + WCHAR propertyName[mdNameLen] = W("\0"); + ULONG propertyNameLen = 0; + if (SUCCEEDED(pMD->GetPropertyProps(propertyDef, + &propertyClass, + propertyName, + mdNameLen, + &propertyNameLen, + &propFlags, + NULL, + NULL, + NULL, + &pDefaultValue, + &cchDefaultValue, + NULL, + &mdGetter, + NULL, + 0, + NULL))) + { + if (propFlags & fdStatic) + { + if (numstaticBack == 0) + numstatic++; + else + numstaticBack--; + } + else + { + if (numinstanceBack == 0) + numinstance++; + else + numinstanceBack--; + } + } + } + pMD->CloseEnum(propEnum); + + return S_OK; +} + +HRESULT GetNumChild(ICorDebugValue *pValue, + unsigned int &numchild, + bool static_members = false) +{ + HRESULT Status = S_OK; + numchild = 0; + + ULONG numstatic; + ULONG numinstance; + IfFailRet(GetNumChild(pValue, NULL, numstatic, numinstance)); + if (static_members) + { + numchild = numstatic; + } + else + { + numchild = (numstatic > 0) ? numinstance + 1 : numinstance; + } + return S_OK; +} + +extern std::mutex g_currentThreadMutex; +extern ICorDebugThread *g_currentThread; + +std::mutex g_evalMutex; +std::condition_variable g_evalCV; +bool g_evalComplete = false; + +void NotifyEvalComplete() +{ + std::lock_guard lock(g_evalMutex); + g_evalComplete = true; + g_evalMutex.unlock(); + g_evalCV.notify_one(); +} + +HRESULT EvalProperty( + mdMethodDef methodDef, + ICorDebugModule *pModule, + ICorDebugType *pType, + ICorDebugValue *pInputValue, + bool is_static, + ICorDebugValue **ppEvalResult) +{ + HRESULT Status = S_OK; + + ToRelease pEval; + + ToRelease pProcess; + { + // g_currentThreadMutex should be locked by caller function + //std::lock_guard lock(g_currentThreadMutex); + + IfFailRet(g_currentThread->GetProcess(&pProcess)); + IfFailRet(g_currentThread->CreateEval(&pEval)); + } + + ToRelease pFunc; + IfFailRet(pModule->GetFunctionFromToken(methodDef, &pFunc)); + + ToRelease pTypeEnum; + + std::vector< ToRelease > typeParams; + if(SUCCEEDED(pType->EnumerateTypeParameters(&pTypeEnum))) + { + ICorDebugType *curType; + ULONG fetched = 0; + while(SUCCEEDED(pTypeEnum->Next(1, &curType, &fetched)) && fetched == 1) + { + typeParams.emplace_back(curType); + } + } + + ToRelease pEval2; + IfFailRet(pEval->QueryInterface(IID_ICorDebugEval2, (LPVOID*) &pEval2)); + + IfFailRet(pEval2->CallParameterizedFunction( + pFunc, + typeParams.size(), + (ICorDebugType **)typeParams.data(), + is_static ? 0 : 1, + is_static ? NULL : &pInputValue + )); + + std::unique_lock lock(g_evalMutex); + g_evalComplete = false; + IfFailRet(pProcess->Continue(0)); + g_evalCV.wait(lock, []{return g_evalComplete;}); + + return pEval->GetResult(ppEvalResult); +} + +struct VarObjValue +{ + std::string name; + ICorDebugValue *value; + std::string owningType; + std::string typeName; + + std::string varobjName; + bool statics_only; + + unsigned int numchild; + + VarObjValue( + const std::string &n, + ICorDebugValue *v, + const std::string t = "") : name(n), value(v), owningType(t), + statics_only(false), numchild(0) {} + VarObjValue( + ICorDebugValue *v) : name("Static members"), value(v), + statics_only(true), numchild(0) {} +}; + +static HRESULT PrintFieldsAndProperties(ICorDebugValue *pInputValue, + ICorDebugType *pTypeCast, + ICorDebugILFrame *pILFrame, + std::vector &members, + bool static_members, + bool &has_static_members) +{ + has_static_members = false; + HRESULT Status = S_OK; + + BOOL isNull = FALSE; + ToRelease pValue; + + IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull)); + + if (isNull) return S_OK; + + ToRelease pArrayValue; + if (SUCCEEDED(pValue->QueryInterface(IID_ICorDebugArrayValue, (LPVOID *) &pArrayValue))) + { + ULONG32 nRank; + IfFailRet(pArrayValue->GetRank(&nRank)); + + // TODO: array elements + + return S_OK; + } + + mdTypeDef currentTypeDef; + ToRelease pClass; + ToRelease pValue2; + ToRelease pType; + ToRelease pModule; + IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2)); + if(pTypeCast == NULL) + IfFailRet(pValue2->GetExactType(&pType)); + else + { + pType = pTypeCast; + pType->AddRef(); + } + + CorElementType corElemType; + IfFailRet(pType->GetType(&corElemType)); + if (corElemType == ELEMENT_TYPE_STRING) + return S_OK; + + IfFailRet(pType->GetClass(&pClass)); + IfFailRet(pClass->GetModule(&pModule)); + IfFailRet(pClass->GetToken(¤tTypeDef)); + + ToRelease pMDUnknown; + ToRelease pMD; + IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown)); + IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD)); + + std::string baseTypeName; + ToRelease pBaseType; + if(SUCCEEDED(pType->GetBase(&pBaseType)) && pBaseType != NULL && SUCCEEDED(TypePrinter::GetTypeOfValue(pBaseType, baseTypeName))) + { + if(baseTypeName == "System.Enum") + return S_OK; + else if(baseTypeName != "System.Object" && baseTypeName != "System.ValueType") + { + // Add fields of base class + PrintFieldsAndProperties(pInputValue, pBaseType, pILFrame, members, static_members, has_static_members); + } + } + + std::string className; + TypePrinter::GetTypeOfValue(pType, className); + + std::unordered_set backedProperies; + + ULONG numFields = 0; + HCORENUM fEnum = NULL; + mdFieldDef fieldDef; + while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0) + { + ULONG nameLen = 0; + DWORD fieldAttr = 0; + WCHAR mdName[mdNameLen] = {0}; + if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, NULL, NULL))) + { + char cName[mdNameLen] = {0}; + WideCharToMultiByte(CP_UTF8, 0, mdName, (int)(nameLen + 1), cName, _countof(cName), NULL, NULL); + + if(fieldAttr & fdLiteral) + continue; + + bool is_static = (fieldAttr & fdStatic); + if (is_static) + has_static_members = true; + + bool add_member = static_members ? is_static : !is_static; + if (!add_member) + continue; + + ToRelease pFieldVal; + + if (fieldAttr & fdStatic) + { + pType->GetStaticFieldValue(fieldDef, pILFrame, &pFieldVal); + } + else + { + ToRelease pObjValue; + if (SUCCEEDED(pValue->QueryInterface(IID_ICorDebugObjectValue, (LPVOID*) &pObjValue))) + pObjValue->GetFieldValue(pClass, fieldDef, &pFieldVal); + } + + if(pFieldVal != NULL) + { + std::string name(cName); + if (cName[0] == '<') + { + size_t endOffset = name.rfind('>'); + name = name.substr(1, endOffset - 1); + backedProperies.insert(name); + } + + members.emplace_back(name, pFieldVal.Detach(), className); + } + else + { + // no need for backing field when we can not get its value + if (cName[0] == '<') + continue; + + members.emplace_back(cName, nullptr, className); + } + } + } + pMD->CloseEnum(fEnum); + + mdProperty propertyDef; + ULONG numProperties = 0; + HCORENUM propEnum = NULL; + while(SUCCEEDED(pMD->EnumProperties(&propEnum, currentTypeDef, &propertyDef, 1, &numProperties)) && numProperties != 0) + { + mdTypeDef propertyClass; + + ULONG propertyNameLen = 0; + DWORD propFlags; + PCCOR_SIGNATURE pvSig; + ULONG pbSig; + DWORD dwCPlusTypeFlag; + UVCP_CONSTANT pDefaultValue; + ULONG cchDefaultValue; + mdMethodDef mdSetter; + mdMethodDef mdGetter; + mdMethodDef rmdOtherMethod; + ULONG cOtherMethod; + WCHAR propertyName[mdNameLen] = W("\0"); + if (SUCCEEDED(pMD->GetPropertyProps(propertyDef, + &propertyClass, + propertyName, + mdNameLen, + &propertyNameLen, + &propFlags, + &pvSig, + &pbSig, + &dwCPlusTypeFlag, + &pDefaultValue, + &cchDefaultValue, + &mdSetter, + &mdGetter, + &rmdOtherMethod, + 1, + &cOtherMethod))) + { + char cName[mdNameLen] = {0}; + WideCharToMultiByte(CP_UTF8, 0, propertyName, (int)(propertyNameLen + 1), cName, _countof(cName), NULL, NULL); + + if (backedProperies.find(cName) != backedProperies.end()) + continue; + + bool is_static = (propFlags & fdStatic); + if (is_static) + has_static_members = true; + + bool add_member = static_members ? is_static : !is_static; + if (!add_member) + continue; + + ToRelease pResultValue; + std::string resultTypeName; + if (SUCCEEDED(EvalProperty(mdGetter, pModule, pType, pInputValue, is_static, &pResultValue))) + { + members.emplace_back(cName, pResultValue.Detach(), className); + } + } + } + pMD->CloseEnum(propEnum); + + return S_OK; +} + +void FixupInheritedFieldNames(std::vector &members) +{ + std::unordered_set names; + for (auto it = members.rbegin(); it != members.rend(); ++it) + { + auto r = names.insert(it->name); + if (!r.second) + { + it->name += " (" + it->owningType + ")"; + } + } +} + +void PrintChildren(std::vector &members, std::string &output) +{ + std::stringstream ss; + ss << "numchild=\"" << members.size() << "\""; + + if (members.empty()) + { + output = ss.str(); + return; + } + ss << ",children=["; + + const char *sep = ""; + for (auto m : members) + { + ss << sep; + sep = ","; + + ss << "child={name=\"" << m.varobjName << "\",exp=\"" << m.name << "\","; + ss << "numchild=\"" << m.numchild << "\",type=\"" << m.typeName << "\"}"; + //thread-id="452958",has_more="0"} + } + + ss << "]"; + output = ss.str(); +} + +HRESULT ListChildren(VarObjValue &objValue, ICorDebugFrame *pFrame, std::string &output) +{ + HRESULT Status; + + ToRelease pILFrame; + IfFailRet(pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame)); + + std::vector members; + + bool has_static_members; + + IfFailRet(PrintFieldsAndProperties(objValue.value, + NULL, + pILFrame, + members, + objValue.statics_only, + has_static_members)); + + if (!objValue.statics_only && has_static_members) + { + objValue.value->AddRef(); + members.emplace_back(objValue.value); + } + + FixupInheritedFieldNames(members); + + for (auto &m : members) + { + std::string className; + + if (!m.value) + continue; + + Status = GetNumChild(m.value, m.numchild, m.statics_only); + TypePrinter::GetTypeOfValue(m.value, m.typeName); + } + + PrintChildren(members, output); + + for (auto m : members) + { + if (m.value) + m.value->Release(); + } + + return S_OK; +} + +HRESULT ListChildren(ICorDebugValue *pInputValue, ICorDebugFrame *pFrame, std::string &output) +{ + VarObjValue val("?", pInputValue, ""); + return ListChildren(val, pFrame, output); +} + HRESULT ListVariables(ICorDebugFrame *pFrame, std::string &output) { bool printValues = true; @@ -344,7 +911,7 @@ HRESULT ListVariables(ICorDebugFrame *pFrame, std::string &output) if (printValues) { std::string strVal; - if (SUCCEEDED(PrintValue(pValue, pILFrame, pMD, strVal))) + if (SUCCEEDED(PrintValue(pValue, pILFrame, strVal))) ss << ",value=\"" << strVal << "\""; } if (printTypes) @@ -353,6 +920,11 @@ HRESULT ListVariables(ICorDebugFrame *pFrame, std::string &output) if (SUCCEEDED(TypePrinter::GetTypeOfValue(pValue, strVal))) ss << ",type=\"" << strVal << "\""; } + + std::string test; + ListChildren(pValue, pFrame, test); + ss << test; + ss << "}"; sep = ","; } @@ -382,7 +954,7 @@ HRESULT ListVariables(ICorDebugFrame *pFrame, std::string &output) if (printValues) { std::string strVal; - if (SUCCEEDED(PrintValue(pValue, pILFrame, pMD, strVal))) + if (SUCCEEDED(PrintValue(pValue, pILFrame, strVal))) ss << ",value=\"" << strVal << "\""; } if (printTypes) @@ -391,6 +963,15 @@ HRESULT ListVariables(ICorDebugFrame *pFrame, std::string &output) if (SUCCEEDED(TypePrinter::GetTypeOfValue(pValue, strVal))) ss << ",type=\"" << strVal << "\""; } + std::string children; + ListChildren(pValue, pFrame, children); + ss << "," << children; + + ULONG numchild; + GetNumChild(pValue, numchild); + + ss << "numchild=\"" << numchild << "\""; + ss << "}"; sep = ","; } -- 2.7.4