1 // Copyright (c) 2017 Samsung Electronics Co., LTD
2 // Distributed under the MIT License.
3 // See the LICENSE file in the project root for more information.
5 #include "manageddebugger.h"
7 #include <unordered_set>
10 #include "typeprinter.h"
11 #include "valueprint.h"
15 HRESULT Variables::GetNumChild(
16 ICorDebugValue *pValue,
17 unsigned int &numchild,
20 HRESULT Status = S_OK;
24 ULONG numinstance = 0;
26 if (pValue == nullptr)
29 IfFailRet(m_evaluator.WalkMembers(pValue, nullptr, nullptr, [&numstatic, &numinstance](
50 numchild = (numstatic > 0) ? numinstance + 1 : numinstance;
55 struct Variables::Member
58 std::string ownerType;
59 ToRelease<ICorDebugValue> value;
60 Member(const std::string &name, const std::string ownerType, ToRelease<ICorDebugValue> value) :
63 value(std::move(value))
65 Member(Member &&that) = default;
66 Member(const Member &that) = delete;
69 HRESULT Variables::FetchFieldsAndProperties(
70 ICorDebugValue *pInputValue,
71 ICorDebugThread *pThread,
72 ICorDebugILFrame *pILFrame,
73 std::vector<Member> &members,
75 bool &hasStaticMembers,
79 hasStaticMembers = false;
83 IfFailRet(pThread->GetID(&threadId));
85 int currentIndex = -1;
87 IfFailRet(m_evaluator.WalkMembers(pInputValue, pThread, pILFrame, [&](
89 ICorDebugModule *pModule,
91 ICorDebugValue *pValue,
93 const std::string &name)
96 hasStaticMembers = true;
98 bool addMember = fetchOnlyStatic ? is_static : !is_static;
103 if (currentIndex < childStart)
105 if (currentIndex >= childEnd)
108 std::string className;
110 TypePrinter::GetTypeOfValue(pType, className);
112 ToRelease<ICorDebugValue> pResultValue;
114 if (mdGetter != mdMethodDefNil)
116 ToRelease<ICorDebugFunction> pFunc;
117 if (SUCCEEDED(pModule->GetFunctionFromToken(mdGetter, &pFunc)))
118 m_evaluator.EvalFunction(pThread, pFunc, pType, is_static ? nullptr : pInputValue, &pResultValue);
124 pResultValue = pValue;
127 members.emplace_back(name, className, std::move(pResultValue));
134 int ManagedDebugger::GetNamedVariables(uint32_t variablesReference)
136 return m_variables.GetNamedVariables(variablesReference);
139 int Variables::GetNamedVariables(uint32_t variablesReference)
141 auto it = m_variables.find(variablesReference);
142 if (it == m_variables.end())
144 return it->second.namedVariables;
147 HRESULT ManagedDebugger::GetVariables(
148 uint32_t variablesReference,
149 VariablesFilter filter,
152 std::vector<Variable> &variables)
154 return m_variables.GetVariables(m_pProcess, variablesReference, filter, start, count, variables);
157 HRESULT Variables::GetVariables(
158 ICorDebugProcess *pProcess,
159 uint32_t variablesReference,
160 VariablesFilter filter,
163 std::vector<Variable> &variables)
165 if (pProcess == nullptr)
168 auto it = m_variables.find(variablesReference);
169 if (it == m_variables.end())
172 VariableReference &ref = it->second;
176 StackFrame stackFrame(ref.frameId);
177 ToRelease<ICorDebugThread> pThread;
178 IfFailRet(pProcess->GetThread(stackFrame.GetThreadId(), &pThread));
179 ToRelease<ICorDebugFrame> pFrame;
180 IfFailRet(GetFrameAt(pThread, stackFrame.GetLevel(), &pFrame));
182 // Named and Indexed variables are in the same index (internally), Named variables go first
183 if (filter == VariablesNamed && (start + count > ref.namedVariables || count == 0))
184 count = ref.namedVariables - start;
185 if (filter == VariablesIndexed)
186 start += ref.namedVariables;
190 IfFailRet(GetStackVariables(ref.frameId, pThread, pFrame, start, count, variables));
192 IfFailRet(GetChildren(ref, pThread, pFrame, start, count, variables));
197 void Variables::AddVariableReference(Variable &variable, uint64_t frameId, ICorDebugValue *value, ValueKind valueKind)
200 unsigned int numChild = 0;
201 GetNumChild(value, numChild, valueKind == ValueIsClass);
205 variable.namedVariables = numChild;
206 variable.variablesReference = m_nextVariableReference++;
208 VariableReference variableReference(variable, frameId, value, valueKind);
209 m_variables.emplace(std::make_pair(variable.variablesReference, std::move(variableReference)));
212 HRESULT Variables::GetStackVariables(
214 ICorDebugThread *pThread,
215 ICorDebugFrame *pFrame,
218 std::vector<Variable> &variables)
222 int currentIndex = -1;
224 ToRelease<ICorDebugValue> pExceptionValue;
225 if (SUCCEEDED(pThread->GetCurrentException(&pExceptionValue)) && pExceptionValue != nullptr)
228 bool outOfRange = currentIndex < start || (count != 0 && currentIndex >= start + count);
232 var.name = "$exception";
233 var.evaluateName = var.name;
235 PrintValue(pExceptionValue, var.value, escape);
236 TypePrinter::GetTypeOfValue(pExceptionValue, var.type);
237 AddVariableReference(var, frameId, pExceptionValue, ValueIsVariable);
238 variables.push_back(var);
242 IfFailRet(m_evaluator.WalkStackVars(pFrame, [&](
243 ICorDebugILFrame *pILFrame,
244 ICorDebugValue *pValue,
245 const std::string &name) -> HRESULT
248 if (currentIndex < start || (count != 0 && currentIndex >= start + count))
252 var.evaluateName = var.name;
254 PrintValue(pValue, var.value, escape);
255 TypePrinter::GetTypeOfValue(pValue, var.type);
256 AddVariableReference(var, frameId, pValue, ValueIsVariable);
257 variables.push_back(var);
264 HRESULT ManagedDebugger::GetScopes(uint64_t frameId, std::vector<Scope> &scopes)
266 return m_variables.GetScopes(m_pProcess, frameId, scopes);
269 HRESULT Variables::GetScopes(ICorDebugProcess *pProcess, uint64_t frameId, std::vector<Scope> &scopes)
271 if (pProcess == nullptr)
276 StackFrame stackFrame(frameId);
277 ToRelease<ICorDebugThread> pThread;
278 IfFailRet(pProcess->GetThread(stackFrame.GetThreadId(), &pThread));
279 ToRelease<ICorDebugFrame> pFrame;
280 IfFailRet(GetFrameAt(pThread, stackFrame.GetLevel(), &pFrame));
282 int namedVariables = 0;
283 uint32_t variablesReference = 0;
285 ToRelease<ICorDebugValue> pExceptionValue;
286 if (SUCCEEDED(pThread->GetCurrentException(&pExceptionValue)) && pExceptionValue != nullptr)
289 IfFailRet(m_evaluator.WalkStackVars(pFrame, [&](
290 ICorDebugILFrame *pILFrame,
291 ICorDebugValue *pValue,
292 const std::string &name) -> HRESULT
298 if (namedVariables > 0)
300 variablesReference = m_nextVariableReference++;
301 VariableReference scopeReference(variablesReference, frameId, namedVariables);
302 m_variables.emplace(std::make_pair(variablesReference, std::move(scopeReference)));
305 scopes.emplace_back(variablesReference, "Locals", namedVariables);
310 void Variables::FixupInheritedFieldNames(std::vector<Member> &members)
312 std::unordered_set<std::string> names;
313 for (auto &it : members)
315 auto r = names.insert(it.name);
318 it.name += " (" + it.ownerType + ")";
323 HRESULT Variables::GetChildren(
324 VariableReference &ref,
325 ICorDebugThread *pThread,
326 ICorDebugFrame *pFrame,
329 std::vector<Variable> &variables)
336 ToRelease<ICorDebugILFrame> pILFrame;
338 IfFailRet(pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame));
340 std::vector<Member> members;
342 bool hasStaticMembers = false;
347 IfFailRet(FetchFieldsAndProperties(ref.value,
351 ref.valueKind == ValueIsClass,
354 count == 0 ? INT_MAX : start + count));
356 FixupInheritedFieldNames(members);
358 for (auto &it : members)
362 bool isIndex = !it.name.empty() && it.name.at(0) == '[';
363 if (var.name.find('(') == std::string::npos) // expression evaluator does not support typecasts
364 var.evaluateName = ref.evaluateName + (isIndex ? "" : ".") + var.name;
366 if (it.value == nullptr)
368 var.value = "<error>";
372 PrintValue(it.value, var.value, escape);
373 TypePrinter::GetTypeOfValue(it.value, var.type);
375 AddVariableReference(var, ref.frameId, it.value, ValueIsVariable);
376 variables.push_back(var);
379 if (ref.valueKind == ValueIsVariable && hasStaticMembers)
381 bool staticsInRange = start < ref.namedVariables && (count == 0 || start + count >= ref.namedVariables);
384 m_evaluator.RunClassConstructor(pThread, ref.value);
387 var.name = "Static members";
388 TypePrinter::GetTypeOfValue(ref.value, var.evaluateName); // do not expose type for this fake variable
389 AddVariableReference(var, ref.frameId, ref.value, ValueIsClass);
390 variables.push_back(var);
397 HRESULT ManagedDebugger::Evaluate(uint64_t frameId, const std::string &expression, Variable &variable)
399 return m_variables.Evaluate(m_pProcess, frameId, expression, variable);
402 HRESULT Variables::Evaluate(
403 ICorDebugProcess *pProcess,
405 const std::string &expression,
408 if (pProcess == nullptr)
413 StackFrame stackFrame(frameId);
414 ToRelease<ICorDebugThread> pThread;
415 IfFailRet(pProcess->GetThread(stackFrame.GetThreadId(), &pThread));
416 ToRelease<ICorDebugFrame> pFrame;
417 IfFailRet(GetFrameAt(pThread, stackFrame.GetLevel(), &pFrame));
419 ToRelease<ICorDebugValue> pResultValue;
420 IfFailRet(m_evaluator.EvalExpr(pThread, pFrame, expression, &pResultValue));
422 variable.evaluateName = expression;
425 PrintValue(pResultValue, variable.value, escape);
426 TypePrinter::GetTypeOfValue(pResultValue, variable.type);
427 AddVariableReference(variable, frameId, pResultValue, ValueIsVariable);