return m_uniqueBreakpoints->AllBreakpointsActivate(act);
}
-static HRESULT InternalGetFrameLocation(ICorDebugFrame *pFrame, Modules *pModules, bool hotReload, ThreadId threadId, FrameLevel level, StackFrame &stackFrame)
+static HRESULT InternalGetFrameLocation(ICorDebugFrame *pFrame, Modules *pModules, bool hotReload, ThreadId threadId, FrameLevel level, StackFrame &stackFrame, bool hotReloadAwareCaller)
{
HRESULT Status;
ToRelease<ICorDebugModule> pModule;
IfFailRet(pFunc->GetModule(&pModule));
+ ULONG32 methodVersion = 1;
+ ULONG32 currentVersion = 1;
if (hotReload)
{
// In case current (top) code version is 1, executed in this frame method version can't be not 1.
- ULONG32 currentVersion = 1;
- ULONG32 methodVersion = 1;
if (SUCCEEDED(pFunc->GetCurrentVersionNumber(¤tVersion)) && currentVersion != 1)
{
ToRelease<ICorDebugCode> pCode;
IfFailRet(pCode->GetVersionNumber(&methodVersion));
}
- if (methodVersion != currentVersion)
+ if (!hotReloadAwareCaller && methodVersion != currentVersion)
{
std::string moduleNamePrefix;
WCHAR name[mdNameLen];
stackFrame.clrAddr.methodToken = methodToken;
stackFrame.clrAddr.ilOffset = ilOffset;
stackFrame.clrAddr.nativeOffset = nOffset;
+ stackFrame.clrAddr.methodVersion = methodVersion;
stackFrame.addr = GetFrameAddr(pFrame);
+ if (stackFrame.clrAddr.ilOffset != 0)
+ stackFrame.activeStatementFlags |= StackFrame::ActiveStatementFlags::PartiallyExecuted;
+ if (methodVersion == currentVersion)
+ stackFrame.activeStatementFlags |= StackFrame::ActiveStatementFlags::MethodUpToDate;
+ else
+ stackFrame.activeStatementFlags |= StackFrame::ActiveStatementFlags::Stale;
+
TypePrinter::GetMethodName(pFrame, stackFrame.name);
return S_OK;
HRESULT ManagedDebugger::GetFrameLocation(ICorDebugFrame *pFrame, ThreadId threadId, FrameLevel level, StackFrame &stackFrame)
{
- return InternalGetFrameLocation(pFrame, m_sharedModules.get(), m_hotReload, threadId, level, stackFrame);
+ return InternalGetFrameLocation(pFrame, m_sharedModules.get(), m_hotReload, threadId, level, stackFrame, false);
}
-static HRESULT InternalGetStackTrace(Modules *pModules, bool hotReload, ICorDebugThread *pThread, FrameLevel startFrame, unsigned maxFrames, std::vector<StackFrame> &stackFrames, int &totalFrames)
+static HRESULT InternalGetStackTrace(Modules *pModules, bool hotReload, ICorDebugThread *pThread, FrameLevel startFrame,
+ unsigned maxFrames, std::vector<StackFrame> &stackFrames, int &totalFrames, bool hotReloadAwareCaller)
{
LogFuncEntry();
int currentFrame = -1;
+ auto AddFrameStatementFlag = [&] ()
+ {
+ if (currentFrame == 0)
+ stackFrames.back().activeStatementFlags |= StackFrame::ActiveStatementFlags::LeafFrame;
+ else
+ stackFrames.back().activeStatementFlags |= StackFrame::ActiveStatementFlags::NonLeafFrame;
+ };
+
IfFailRet(WalkFrames(pThread, [&](
FrameType frameType,
ICorDebugFrame *pFrame,
case FrameUnknown:
stackFrames.emplace_back(threadId, FrameLevel{currentFrame}, "?");
stackFrames.back().addr = GetFrameAddr(pFrame);
+ AddFrameStatementFlag();
break;
case FrameNative:
stackFrames.emplace_back(threadId, FrameLevel{currentFrame}, pNative->symbol);
stackFrames.back().addr = pNative->addr;
stackFrames.back().source = Source(pNative->file);
stackFrames.back().line = pNative->linenum;
+ AddFrameStatementFlag();
break;
case FrameCLRNative:
stackFrames.emplace_back(threadId, FrameLevel{currentFrame}, "[Native Frame]");
stackFrames.back().addr = GetFrameAddr(pFrame);
+ AddFrameStatementFlag();
break;
case FrameCLRInternal:
{
name += "]";
stackFrames.emplace_back(threadId, FrameLevel{currentFrame}, name);
stackFrames.back().addr = GetFrameAddr(pFrame);
+ AddFrameStatementFlag();
}
break;
case FrameCLRManaged:
{
StackFrame stackFrame;
- InternalGetFrameLocation(pFrame, pModules, hotReload, threadId, FrameLevel{currentFrame}, stackFrame);
+ InternalGetFrameLocation(pFrame, pModules, hotReload, threadId, FrameLevel{currentFrame}, stackFrame, hotReloadAwareCaller);
stackFrames.push_back(stackFrame);
+ AddFrameStatementFlag();
}
break;
}
+
return S_OK;
}));
return S_OK;
}
-HRESULT ManagedDebugger::GetStackTrace(ThreadId threadId, FrameLevel startFrame, unsigned maxFrames, std::vector<StackFrame> &stackFrames, int &totalFrames)
+HRESULT ManagedDebugger::GetStackTrace(ThreadId threadId, FrameLevel startFrame, unsigned maxFrames, std::vector<StackFrame> &stackFrames, int &totalFrames, bool hotReloadAwareCaller)
{
LogFuncEntry();
ToRelease<ICorDebugThread> pThread;
IfFailRet(m_iCorProcess->GetThread(int(threadId), &pThread));
- return InternalGetStackTrace(m_sharedModules.get(), m_hotReload, pThread, startFrame, maxFrames, stackFrames, totalFrames);
+ return InternalGetStackTrace(m_sharedModules.get(), m_hotReload, pThread, startFrame, maxFrames, stackFrames, totalFrames, hotReloadAwareCaller);
}
int ManagedDebugger::GetNamedVariables(uint32_t variablesReference)
HRESULT BreakpointActivate(int id, bool act) override;
void EnumerateBreakpoints(std::function<bool (const IDebugger::BreakpointInfo&)>&& callback) override;
HRESULT AllBreakpointsActivate(bool act) override;
- HRESULT GetStackTrace(ThreadId threadId, FrameLevel startFrame, unsigned maxFrames, std::vector<StackFrame> &stackFrames, int &totalFrames) override;
+ HRESULT GetStackTrace(ThreadId threadId, FrameLevel startFrame, unsigned maxFrames, std::vector<StackFrame> &stackFrames, int &totalFrames, bool hotReloadAwareCaller = false) override;
HRESULT StepCommand(ThreadId threadId, StepType stepType) override;
HRESULT GetScopes(FrameId frameId, std::vector<Scope> &scopes) override;
HRESULT GetVariables(uint32_t variablesReference, VariablesFilter filter, int start, int count, std::vector<Variable> &variables) override;
virtual HRESULT BreakpointActivate(int id, bool act) = 0;
virtual void EnumerateBreakpoints(std::function<bool (const BreakpointInfo&)>&& callback) = 0;
virtual HRESULT AllBreakpointsActivate(bool act) = 0;
- virtual HRESULT GetStackTrace(ThreadId threadId, FrameLevel startFrame, unsigned maxFrames, std::vector<StackFrame> &stackFrames, int &totalFrames) = 0;
+ virtual HRESULT GetStackTrace(ThreadId threadId, FrameLevel startFrame, unsigned maxFrames, std::vector<StackFrame> &stackFrames, int &totalFrames, bool hotReloadAwareCaller = false) = 0;
virtual HRESULT StepCommand(ThreadId threadId, StepType stepType) = 0;
virtual HRESULT GetScopes(FrameId frameId, std::vector<Scope> &scopes) = 0;
virtual HRESULT GetVariables(uint32_t variablesReference, VariablesFilter filter, int start, int count, std::vector<Variable> &variables) = 0;
uint32_t ilOffset;
uint32_t nativeOffset;
uint32_t methodToken;
+ ULONG32 methodVersion; // EnC
- ClrAddr() : ilOffset(0), nativeOffset(0), methodToken(0) {}
+ // Note, initial/default method code version is 1 (not zero!).
+ ClrAddr() : ilOffset(0), nativeOffset(0), methodToken(0), methodVersion(1) {}
bool IsNull() const { return methodToken == 0; }
};
ClrAddr clrAddr; // exposed for MI protocol
uint64_t addr; // exposed for MI protocol
+ enum ActiveStatementFlags : uint16_t
+ {
+ None = 0x00,
+ LeafFrame = 0x01,
+ PartiallyExecuted = 0x02,
+ MethodUpToDate = 0x08,
+ NonLeafFrame = 0x10,
+ Stale = 0x20
+ };
+ uint16_t activeStatementFlags; // EnC
+
StackFrame() :
thread(ThreadId{}, true), level(FrameLevel{}, true), id(),
- line(0), column(0), endLine(0), endColumn(0), addr(0) {}
+ line(0), column(0), endLine(0), endColumn(0), addr(0), activeStatementFlags(0) {}
StackFrame(ThreadId threadId, FrameLevel level, const std::string& name) :
thread(threadId, true), level(level, true), id(FrameId(threadId, level)),
- name(name), line(0), column(0), endLine(0), endColumn(0), addr(0)
+ name(name), line(0), column(0), endLine(0), endColumn(0), addr(0), activeStatementFlags(0)
{}
StackFrame(FrameId id) :
thread(ThreadId{}, false), level(FrameLevel{}, false), id(id),
- line(0), column(0), endLine(0), endColumn(0), addr(0)
+ line(0), column(0), endLine(0), endColumn(0), addr(0), activeStatementFlags(0)
{}
FrameLevel GetLevel() const
ss << "clr-addr={module-id=\"{" << stackFrame.moduleId << "}\","
<< "method-token=\"0x"
<< std::setw(8) << std::setfill('0') << std::hex << stackFrame.clrAddr.methodToken << "\","
+ << "method-version=\"" << std::dec << stackFrame.clrAddr.methodVersion << "\","
<< "il-offset=\"" << std::dec << stackFrame.clrAddr.ilOffset
<< "\",native-offset=\"" << stackFrame.clrAddr.nativeOffset << "\"},";
}
if (stackFrame.id)
ss << ",addr=\"" << ProtocolUtils::AddrToString(stackFrame.addr) << "\"";
+ ss << ",active-statement-flags=\"";
+ if (stackFrame.activeStatementFlags == StackFrame::ActiveStatementFlags::None)
+ {
+ ss << "None";
+ }
+ else
+ {
+ struct flag_t
+ {
+ StackFrame::ActiveStatementFlags bit;
+ std::string name;
+ flag_t(StackFrame::ActiveStatementFlags bit_, const std::string &name_) : bit(bit_), name(name_) {}
+ };
+ static const std::vector<flag_t> flagsMap
+ {{StackFrame::ActiveStatementFlags::LeafFrame, "LeafFrame"},
+ {StackFrame::ActiveStatementFlags::NonLeafFrame, "NonLeafFrame"},
+ {StackFrame::ActiveStatementFlags::PartiallyExecuted, "PartiallyExecuted"},
+ {StackFrame::ActiveStatementFlags::MethodUpToDate, "MethodUpToDate"},
+ {StackFrame::ActiveStatementFlags::Stale, "Stale"}};
+ bool first = true;
+ for (auto &flag : flagsMap)
+ {
+ if ((stackFrame.activeStatementFlags & flag.bit) == flag.bit)
+ {
+ ss << (first ? "":",") << flag.name;
+ first = false;
+ }
+ }
+ }
+ ss << "\"";
+
output = ss.str();
return stackFrame.source.IsNull() ? S_FALSE : S_OK;
}
-static HRESULT PrintFrames(std::shared_ptr<IDebugger> &sharedDebugger, ThreadId threadId, std::string &output, FrameLevel lowFrame, FrameLevel highFrame)
+static HRESULT PrintFrames(std::shared_ptr<IDebugger> &sharedDebugger, ThreadId threadId, std::string &output, FrameLevel lowFrame, FrameLevel highFrame, bool hotReloadAwareCaller)
{
HRESULT Status;
std::ostringstream ss;
int totalFrames = 0;
std::vector<StackFrame> stackFrames;
- IfFailRet(sharedDebugger->GetStackTrace(threadId, lowFrame, int(highFrame) - int(lowFrame), stackFrames, totalFrames));
+ IfFailRet(sharedDebugger->GetStackTrace(threadId, lowFrame, int(highFrame) - int(lowFrame), stackFrames, totalFrames, hotReloadAwareCaller));
int currentFrame = int(lowFrame);
{ "stack-list-frames", [&](const std::vector<std::string> &args_orig, std::string &output) -> HRESULT {
std::vector<std::string> args = args_orig;
ThreadId threadId { ProtocolUtils::GetIntArg(args, "--thread", int(sharedDebugger->GetLastStoppedThreadId())) };
+ bool hotReloadAwareCaller = ProtocolUtils::FindAndEraseArg(args, "--hot-reload");
int lowFrame = 0;
int highFrame = FrameLevel::MaxFrameLevel;
ProtocolUtils::StripArgs(args);
ProtocolUtils::GetIndices(args, lowFrame, highFrame);
- return PrintFrames(sharedDebugger, threadId, output, FrameLevel{lowFrame}, FrameLevel{highFrame});
+ return PrintFrames(sharedDebugger, threadId, output, FrameLevel{lowFrame}, FrameLevel{highFrame}, hotReloadAwareCaller);
}},
{ "stack-list-variables", [&](const std::vector<std::string> &args, std::string &output) -> HRESULT {
HRESULT Status;
return ok ? val : defaultValue;
}
+// Return `true` in case arg was found and erased.
+bool FindAndEraseArg(std::vector<std::string> &args, const std::string& name)
+{
+ auto it = args.begin();
+ while (it != args.end())
+ {
+ if (*it == name)
+ {
+ it = args.erase(it);
+ return true;
+ }
+ else
+ ++it;
+ }
+ return false;
+}
+
bool GetIndices(const std::vector<std::string> &args, int &index1, int &index2)
{
if (args.size() < 2)
int ParseInt(const std::string &s, bool &ok);
void StripArgs(std::vector<std::string> &args);
int GetIntArg(const std::vector<std::string> &args, const std::string& name, int defaultValue);
+ bool FindAndEraseArg(std::vector<std::string> &args, const std::string &name);
bool GetIndices(const std::vector<std::string> &args, int &index1, int &index2);
BreakType GetBreakpointType(const std::vector<std::string> &args);
std::string GetConditionPrepareArgs(std::vector<std::string> &args);
Assert.True(MIDebugger.IsEventReceived(filter), @"__FILE__:__LINE__"+"\n"+caller_trace);\r
}\r
\r
+ public void CheckStackFrames(string caller_trace)\r
+ {\r
+ var res = MIDebugger.Request("-stack-list-frames 0 1000");\r
+ Assert.Equal(MIResultClass.Done, res.Class, @"__FILE__:__LINE__");\r
+\r
+ var stack = (MIList)res["stack"];\r
+ var frame = (MITuple)((MIResult)stack[0]).Value;\r
+ var level = ((MIConst)frame["level"]).CString;\r
+ var func = ((MIConst)frame["func"]).CString;\r
+\r
+ if (level == "0" && func.StartsWith("[Outdated Code]")) {\r
+ return;\r
+ }\r
+\r
+ throw new ResultNotSuccessException(@"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
+ public void CheckHotReloadAwareStackFrames(string caller_trace)\r
+ {\r
+ var res = MIDebugger.Request("-stack-list-frames 0 1000 --hot-reload");\r
+ Assert.Equal(MIResultClass.Done, res.Class, @"__FILE__:__LINE__");\r
+\r
+ var stack = (MIList)res["stack"];\r
+ var frame = (MITuple)((MIResult)stack[0]).Value;\r
+ var level = ((MIConst)frame["level"]).CString;\r
+ var func = ((MIConst)frame["func"]).CString;\r
+ var clr_addr = (MITuple)frame["clr-addr"];\r
+ var method_version = ((MIConst)clr_addr["method-version"]).CString;\r
+\r
+ if (level == "0" && func.StartsWith("TestAppHotReload") && method_version == "2") {\r
+ return;\r
+ }\r
+\r
+ throw new ResultNotSuccessException(@"__FILE__:__LINE__"+"\n"+caller_trace);\r
+ }\r
+\r
public void DebuggerExit(string caller_trace)\r
{\r
Assert.Equal(MIResultClass.Exit,\r
\r
Context.StepOver(@"__FILE__:__LINE__");\r
Context.WasStepInOutdatedCode(@"__FILE__:__LINE__");\r
+\r
+ Context.CheckStackFrames(@"__FILE__:__LINE__");\r
+ Context.CheckHotReloadAwareStackFrames(@"__FILE__:__LINE__");\r
+\r
Context.StepOver(@"__FILE__:__LINE__");\r
Context.WasStepInOutdatedCode(@"__FILE__:__LINE__");\r
+\r
Context.StepOver(@"__FILE__:__LINE__");\r
Context.WasStepInOutdatedCode(@"__FILE__:__LINE__");\r
\r