From 70665844915eeb66273a84f131ffc33a202539d5 Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani Date: Wed, 9 Feb 2022 19:04:21 -0800 Subject: [PATCH] [lldb/Plugin] Add artificial stackframe loading in ScriptedThread This patch adds the ability for ScriptedThread to load artificial stack frames. To do so, the interpreter instance can create a list that will contain the frame index and its pc address. Then, when the Scripted Process plugin stops, it will refresh its Scripted Threads state by invalidating their register context and load to list from the interpreter object and reconstruct each frame. This patch also removes all of the default implementation for `get_stackframes` from the derived ScriptedThread classes, and add the interface code for the Scripted Thread Interface. rdar://88721095 Differential Revision: https://reviews.llvm.org/D119388 Signed-off-by: Med Ismail Bennani --- .../python/scripted_process/scripted_process.py | 4 +- lldb/include/lldb/Target/StackFrameList.h | 3 + .../Plugins/Process/scripted/ScriptedProcess.cpp | 1 + .../Plugins/Process/scripted/ScriptedThread.cpp | 68 ++++++++++++++++++++++ .../Plugins/Process/scripted/ScriptedThread.h | 2 + .../Python/ScriptedPythonInterface.cpp | 8 +++ .../Python/ScriptedPythonInterface.h | 5 ++ .../Python/ScriptedThreadPythonInterface.cpp | 9 ++- .../scripted_process/TestScriptedProcess.py | 3 + .../scripted_process/dummy_scripted_process.py | 18 +----- .../scripted_process/invalid_scripted_process.py | 15 ----- .../stack_core_scripted_process.py | 15 ----- 12 files changed, 102 insertions(+), 49 deletions(-) diff --git a/lldb/examples/python/scripted_process/scripted_process.py b/lldb/examples/python/scripted_process/scripted_process.py index 88df731..2d3092d 100644 --- a/lldb/examples/python/scripted_process/scripted_process.py +++ b/lldb/examples/python/scripted_process/scripted_process.py @@ -306,9 +306,9 @@ class ScriptedThread: containing for each entry, the frame index, the canonical frame address, the program counter value for that frame and a symbol context. - None if the list is empty. + The list can be empty. """ - return 0 + return self.frames def get_register_info(self): if self.register_info is None: diff --git a/lldb/include/lldb/Target/StackFrameList.h b/lldb/include/lldb/Target/StackFrameList.h index c98995c..e05a398 100644 --- a/lldb/include/lldb/Target/StackFrameList.h +++ b/lldb/include/lldb/Target/StackFrameList.h @@ -17,6 +17,8 @@ namespace lldb_private { +class ScriptedThread; + class StackFrameList { public: // Constructors and Destructors @@ -86,6 +88,7 @@ public: protected: friend class Thread; + friend class ScriptedThread; bool SetFrameAtIndex(uint32_t idx, lldb::StackFrameSP &frame_sp); diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp index f98c415..1abf0f14 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp +++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp @@ -359,6 +359,7 @@ bool ScriptedProcess::DoUpdateThreadList(ThreadList &old_thread_list, void ScriptedProcess::RefreshStateAfterStop() { // Let all threads recover from stopping and do any clean up based on the // previous thread state (if any). + m_thread_list.RefreshStateAfterStop(); } bool ScriptedProcess::GetProcessInfo(ProcessInstanceInfo &info) { diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp index 173f503..8174a8b 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp +++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp @@ -147,6 +147,73 @@ ScriptedThread::CreateRegisterContextForFrame(StackFrame *frame) { return m_reg_context_sp; } +bool ScriptedThread::LoadArtificialStackFrames() { + StructuredData::ArraySP arr_sp = GetInterface()->GetStackFrames(); + + Status error; + if (!arr_sp) + return GetInterface()->ErrorWithMessage( + LLVM_PRETTY_FUNCTION, "Failed to get scripted thread stackframes.", + error, LLDBLog::Thread); + + size_t arr_size = arr_sp->GetSize(); + if (arr_size > std::numeric_limits::max()) + return GetInterface()->ErrorWithMessage( + LLVM_PRETTY_FUNCTION, + llvm::Twine( + "StackFrame array size (" + llvm::Twine(arr_size) + + llvm::Twine( + ") is greater than maximum autorized for a StackFrameList.")) + .str(), + error, LLDBLog::Thread); + + StackFrameListSP frames = GetStackFrameList(); + + for (size_t idx = 0; idx < arr_size; idx++) { + + StructuredData::Dictionary *dict; + + if (!arr_sp->GetItemAtIndexAsDictionary(idx, dict) || !dict) + return GetInterface()->ErrorWithMessage( + LLVM_PRETTY_FUNCTION, + llvm::Twine( + "Couldn't get artificial stackframe dictionary at index (" + + llvm::Twine(idx) + llvm::Twine(") from stackframe array.")) + .str(), + error, LLDBLog::Thread); + + lldb::addr_t pc; + if (!dict->GetValueForKeyAsInteger("pc", pc)) + return ScriptedInterface::ErrorWithMessage( + LLVM_PRETTY_FUNCTION, + "Couldn't find value for key 'pc' in stackframe dictionary.", error, + LLDBLog::Thread); + + Address symbol_addr; + symbol_addr.SetLoadAddress(pc, &this->GetProcess()->GetTarget()); + + lldb::addr_t cfa = LLDB_INVALID_ADDRESS; + bool cfa_is_valid = false; + const bool behaves_like_zeroth_frame = false; + SymbolContext sc; + symbol_addr.CalculateSymbolContext(&sc); + + StackFrameSP synth_frame_sp = std::make_shared( + this->shared_from_this(), idx, idx, cfa, cfa_is_valid, pc, + StackFrame::Kind::Artificial, behaves_like_zeroth_frame, &sc); + + if (!frames->SetFrameAtIndex(static_cast(idx), synth_frame_sp)) + return GetInterface()->ErrorWithMessage( + LLVM_PRETTY_FUNCTION, + llvm::Twine("Couldn't add frame (" + llvm::Twine(idx) + + llvm::Twine(") to ScriptedThread StackFrameList.")) + .str(), + error, LLDBLog::Thread); + } + + return true; +} + bool ScriptedThread::CalculateStopInfo() { StructuredData::DictionarySP dict_sp = GetInterface()->GetStopReason(); @@ -216,6 +283,7 @@ bool ScriptedThread::CalculateStopInfo() { void ScriptedThread::RefreshStateAfterStop() { GetRegisterContext()->InvalidateIfNeeded(/*force=*/false); + LoadArtificialStackFrames(); } lldb::ScriptedThreadInterfaceSP ScriptedThread::GetInterface() const { diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.h b/lldb/source/Plugins/Process/scripted/ScriptedThread.h index 8d8a7c2..959f498 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedThread.h +++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.h @@ -42,6 +42,8 @@ public: lldb::RegisterContextSP CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + bool LoadArtificialStackFrames(); + bool CalculateStopInfo() override; const char *GetInfo() override { return nullptr; } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedPythonInterface.cpp index 7c1f640..d5c527c 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedPythonInterface.cpp @@ -35,6 +35,14 @@ ScriptedPythonInterface::GetStatusFromMethod(llvm::StringRef method_name) { } template <> +StructuredData::ArraySP +ScriptedPythonInterface::ExtractValueFromPythonObject( + python::PythonObject &p, Status &error) { + python::PythonList result_list(python::PyRefType::Borrowed, p.get()); + return result_list.CreateStructuredArray(); +} + +template <> StructuredData::DictionarySP ScriptedPythonInterface::ExtractValueFromPythonObject< StructuredData::DictionarySP>(python::PythonObject &p, Status &error) { diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedPythonInterface.h index da112eb..f2ac306 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedPythonInterface.h @@ -127,6 +127,11 @@ protected: }; template <> +StructuredData::ArraySP +ScriptedPythonInterface::ExtractValueFromPythonObject( + python::PythonObject &p, Status &error); + +template <> StructuredData::DictionarySP ScriptedPythonInterface::ExtractValueFromPythonObject< StructuredData::DictionarySP>(python::PythonObject &p, Status &error); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.cpp index 8321d3e..3ff592f 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.cpp @@ -112,7 +112,14 @@ StructuredData::DictionarySP ScriptedThreadPythonInterface::GetStopReason() { } StructuredData::ArraySP ScriptedThreadPythonInterface::GetStackFrames() { - return nullptr; + Status error; + StructuredData::ArraySP arr = + Dispatch("get_stackframes", error); + + if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, arr, error)) + return {}; + + return arr; } StructuredData::DictionarySP ScriptedThreadPythonInterface::GetRegisterInfo() { diff --git a/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py b/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py index d213263..a622722 100644 --- a/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py +++ b/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py @@ -133,3 +133,6 @@ class ScriptedProcesTestCase(TestBase): break self.assertEqual(idx, int(reg.value, 16)) + self.assertTrue(frame.IsArtificial(), "Frame is not artificial") + pc = frame.GetPCAddress().GetLoadAddress(target) + self.assertEqual(pc, 0x0100001b00) diff --git a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py index 67850cf..a687870 100644 --- a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py +++ b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py @@ -46,6 +46,7 @@ class DummyScriptedProcess(ScriptedProcess): class DummyScriptedThread(ScriptedThread): def __init__(self, process, args): super().__init__(process, args) + self.frames.append({"pc": 0x0100001b00 }) def get_thread_id(self) -> int: return 0x19 @@ -61,21 +62,6 @@ class DummyScriptedThread(ScriptedThread): "signal": signal.SIGINT } } - def get_stackframes(self): - class ScriptedStackFrame: - def __init__(idx, cfa, pc, symbol_ctx): - self.idx = idx - self.cfa = cfa - self.pc = pc - self.symbol_ctx = symbol_ctx - - - symbol_ctx = lldb.SBSymbolContext() - frame_zero = ScriptedStackFrame(0, 0x42424242, 0x5000000, symbol_ctx) - self.frames.append(frame_zero) - - return self.frame_zero[0:0] - def get_register_context(self) -> str: return struct.pack( '21Q', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21) @@ -88,4 +74,4 @@ def __lldb_init_module(debugger, dict): DummyScriptedProcess.__name__)) else: print("Name of the class that will manage the scripted process: '%s.%s'" - % (__name__, DummyScriptedProcess.__name__)) \ No newline at end of file + % (__name__, DummyScriptedProcess.__name__)) diff --git a/lldb/test/API/functionalities/scripted_process/invalid_scripted_process.py b/lldb/test/API/functionalities/scripted_process/invalid_scripted_process.py index b5e8ece..62623d5 100644 --- a/lldb/test/API/functionalities/scripted_process/invalid_scripted_process.py +++ b/lldb/test/API/functionalities/scripted_process/invalid_scripted_process.py @@ -57,21 +57,6 @@ class InvalidScriptedThread(ScriptedThread): "signal": signal.SIGTRAP } } - def get_stackframes(self): - class ScriptedStackFrame: - def __init__(idx, cfa, pc, symbol_ctx): - self.idx = idx - self.cfa = cfa - self.pc = pc - self.symbol_ctx = symbol_ctx - - - symbol_ctx = lldb.SBSymbolContext() - frame_zero = ScriptedStackFrame(0, 0x42424242, 0x5000000, symbol_ctx) - self.frames.append(frame_zero) - - return self.frame_zero[0:0] - def get_register_context(self) -> str: return None diff --git a/lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py b/lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py index b0ea6f6..5ac4e8d 100644 --- a/lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py +++ b/lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py @@ -139,21 +139,6 @@ class StackCoreScriptedThread(ScriptedThread): return stop_reason - def get_stackframes(self): - class ScriptedStackFrame: - def __init__(idx, cfa, pc, symbol_ctx): - self.idx = idx - self.cfa = cfa - self.pc = pc - self.symbol_ctx = symbol_ctx - - - symbol_ctx = lldb.SBSymbolContext() - frame_zero = ScriptedStackFrame(0, 0x42424242, 0x5000000, symbol_ctx) - self.frames.append(frame_zero) - - return self.frame_zero[0:0] - def get_register_context(self) -> str: if not self.corefile_thread or self.corefile_thread.GetNumFrames() == 0: return None -- 2.7.4