diff --git a/lldb/examples/python/scripted_process/scripted_process.py b/lldb/examples/python/scripted_process/scripted_process.py --- a/lldb/examples/python/scripted_process/scripted_process.py +++ b/lldb/examples/python/scripted_process/scripted_process.py @@ -70,7 +70,7 @@ tid (int): Thread ID to look for in the scripted process. Returns: - Dict: The thread represented as a dictionary, withr the + Dict: The thread represented as a dictionary, with the tid thread ID. None if tid doesn't match any of the scripted process threads. """ @@ -212,11 +212,12 @@ self.target = None self.process = None self.args = None - if isinstance(process, lldb.SBProcess) and process.IsValid(): - self.process = process - self.target = process.GetTarget() + if isinstance(process, ScriptedProcess): + self.target = process.target + self.process = self.target.GetProcess() self.id = None + self.idx = None self.name = None self.queue = None self.state = None diff --git a/lldb/include/lldb/Interpreter/ScriptedInterface.h b/lldb/include/lldb/Interpreter/ScriptedInterface.h --- a/lldb/include/lldb/Interpreter/ScriptedInterface.h +++ b/lldb/include/lldb/Interpreter/ScriptedInterface.h @@ -27,7 +27,8 @@ virtual StructuredData::GenericSP CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx, - StructuredData::DictionarySP args_sp) = 0; + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_obj = nullptr) = 0; template Ret ErrorWithMessage(llvm::StringRef caller_name, llvm::StringRef error_msg, diff --git a/lldb/include/lldb/Interpreter/ScriptedProcessInterface.h b/lldb/include/lldb/Interpreter/ScriptedProcessInterface.h --- a/lldb/include/lldb/Interpreter/ScriptedProcessInterface.h +++ b/lldb/include/lldb/Interpreter/ScriptedProcessInterface.h @@ -23,7 +23,8 @@ public: StructuredData::GenericSP CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx, - StructuredData::DictionarySP args_sp) override { + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_obj = nullptr) override { return nullptr; } @@ -77,7 +78,8 @@ public: StructuredData::GenericSP CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx, - StructuredData::DictionarySP args_sp) override { + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_obj = nullptr) override { return nullptr; } diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp --- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp +++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp @@ -164,9 +164,6 @@ SetPrivateState(eStateStopped); - UpdateThreadListIfNeeded(); - GetThreadList(); - return {}; } @@ -304,19 +301,55 @@ .str(), error); - lldb::ThreadSP thread_sp; - thread_sp = std::make_shared(*this, error); - - if (!thread_sp || error.Fail()) - return GetInterface().ErrorWithMessage(LLVM_PRETTY_FUNCTION, - error.AsCString(), error); + StructuredData::DictionarySP thread_info_sp = GetInterface().GetThreadsInfo(); - RegisterContextSP reg_ctx_sp = thread_sp->GetRegisterContext(); - if (!reg_ctx_sp) + if (!thread_info_sp) return GetInterface().ErrorWithMessage( - LLVM_PRETTY_FUNCTION, "Invalid Register Context", error); - - new_thread_list.AddThread(thread_sp); + LLVM_PRETTY_FUNCTION, + "Couldn't fetch thread list from Scripted Process.", error); + + auto create_scripted_thread = + [this, &old_thread_list, &error, + &new_thread_list](ConstString key, StructuredData::Object *val) -> bool { + if (!val) + return GetInterface().ErrorWithMessage( + LLVM_PRETTY_FUNCTION, "Invalid thread info object", error); + + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + if (!llvm::to_integer(key.AsCString(), tid)) + return GetInterface().ErrorWithMessage(LLVM_PRETTY_FUNCTION, + "Invalid thread id", error); + + if (ThreadSP thread_sp = + old_thread_list.FindThreadByID(tid, false /*=can_update*/)) { + // If the thread was already in the old_thread_list, + // just add it back to the new_thread_list. + new_thread_list.AddThread(thread_sp); + return true; + } + + lldb::ThreadSP thread_sp = + std::make_shared(*this, error, val->GetAsGeneric()); + + if (!thread_sp || error.Fail()) + return GetInterface().ErrorWithMessage(LLVM_PRETTY_FUNCTION, + error.AsCString(), error); + + RegisterContextSP reg_ctx_sp = thread_sp->GetRegisterContext(); + if (!reg_ctx_sp) + return GetInterface().ErrorWithMessage( + LLVM_PRETTY_FUNCTION, + llvm::Twine("Invalid Register Context for thread " + + llvm::Twine(key.AsCString())) + .str(), + error); + + new_thread_list.AddThread(thread_sp); + + return true; + }; + + thread_info_sp->ForEach(create_scripted_thread); return new_thread_list.GetSize(false) > 0; } diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.h b/lldb/source/Plugins/Process/scripted/ScriptedThread.h --- a/lldb/source/Plugins/Process/scripted/ScriptedThread.h +++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.h @@ -26,7 +26,8 @@ class ScriptedThread : public lldb_private::Thread { public: - ScriptedThread(ScriptedProcess &process, Status &error); + ScriptedThread(ScriptedProcess &process, Status &error, + StructuredData::Generic *script_object = nullptr); ~ScriptedThread() override; diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp --- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp +++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp @@ -28,7 +28,8 @@ lldbassert(GetInterface() && "Invalid Scripted Thread Interface."); } -ScriptedThread::ScriptedThread(ScriptedProcess &process, Status &error) +ScriptedThread::ScriptedThread(ScriptedProcess &process, Status &error, + StructuredData::Generic *script_object) : Thread(process, LLDB_INVALID_THREAD_ID), m_scripted_process(process), m_scripted_thread_interface_sp( m_scripted_process.GetInterface().CreateScriptedThreadInterface()) { @@ -54,18 +55,23 @@ ExecutionContext exe_ctx(process); - StructuredData::GenericSP object_sp = - scripted_thread_interface->CreatePluginObject( - class_name->c_str(), exe_ctx, - process.m_scripted_process_info.GetArgsSP()); - if (!object_sp || !object_sp->IsValid()) { - error.SetErrorString("Failed to create valid script object"); + m_script_object_sp = scripted_thread_interface->CreatePluginObject( + class_name->c_str(), exe_ctx, process.m_scripted_process_info.GetArgsSP(), + script_object); + + if (!m_script_object_sp) { + error.SetErrorString("Failed to create script object"); return; } - m_script_object_sp = object_sp; + if (!m_script_object_sp->IsValid()) { + m_script_object_sp = nullptr; + error.SetErrorString("Created script object is invalid"); + return; + } - SetID(scripted_thread_interface->GetThreadID()); + lldb::tid_t tid = scripted_thread_interface->GetThreadID(); + SetID(tid); } ScriptedThread::~ScriptedThread() { DestroyThread(); } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h @@ -25,7 +25,8 @@ StructuredData::GenericSP CreatePluginObject(const llvm::StringRef class_name, ExecutionContext &exe_ctx, - StructuredData::DictionarySP args_sp) override; + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_obj = nullptr) override; Status Launch() override; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp @@ -32,7 +32,7 @@ StructuredData::GenericSP ScriptedProcessPythonInterface::CreatePluginObject( llvm::StringRef class_name, ExecutionContext &exe_ctx, - StructuredData::DictionarySP args_sp) { + StructuredData::DictionarySP args_sp, StructuredData::Generic *script_obj) { if (class_name.empty()) return {}; @@ -47,9 +47,6 @@ class_name.str().c_str(), m_interpreter.GetDictionaryName(), target_sp, args_impl, error_string); - if (!ret_val) - return {}; - m_object_instance_sp = StructuredData::GenericSP(new StructuredPythonObject(std::move(ret_val))); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.h --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.h @@ -24,7 +24,8 @@ StructuredData::GenericSP CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx, - StructuredData::DictionarySP args_sp) override; + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_obj = nullptr) override; lldb::tid_t GetThreadID() override; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.cpp --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.cpp @@ -31,8 +31,7 @@ StructuredData::GenericSP ScriptedThreadPythonInterface::CreatePluginObject( const llvm::StringRef class_name, ExecutionContext &exe_ctx, - StructuredData::DictionarySP args_sp) { - + StructuredData::DictionarySP args_sp, StructuredData::Generic *script_obj) { if (class_name.empty()) return {}; @@ -43,9 +42,15 @@ Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); - PythonObject ret_val = LLDBSwigPythonCreateScriptedThread( - class_name.str().c_str(), m_interpreter.GetDictionaryName(), process_sp, - args_impl, error_string); + PythonObject ret_val; + + if (!script_obj) + ret_val = LLDBSwigPythonCreateScriptedThread( + class_name.str().c_str(), m_interpreter.GetDictionaryName(), process_sp, + args_impl, error_string); + else + ret_val = PythonObject(PyRefType::Borrowed, + static_cast(script_obj->GetValue())); if (!ret_val) return {}; diff --git a/lldb/test/API/functionalities/scripted_process/Makefile b/lldb/test/API/functionalities/scripted_process/Makefile --- a/lldb/test/API/functionalities/scripted_process/Makefile +++ b/lldb/test/API/functionalities/scripted_process/Makefile @@ -1,4 +1,4 @@ -C_SOURCES := main.c - +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES include Makefile.rules diff --git a/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py b/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py --- a/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py +++ b/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py @@ -130,7 +130,8 @@ def create_stack_skinny_corefile(self, file): self.build() - target, process, thread, _ = lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.c")) + target, process, thread, _ = lldbutil.run_to_source_breakpoint(self, "// break here", + lldb.SBFileSpec("main.cpp")) self.assertTrue(process.IsValid(), "Process is invalid.") # FIXME: Use SBAPI to save the process corefile. self.runCmd("process save-core -s stack " + file) @@ -186,14 +187,14 @@ self.assertTrue(process, PROCESS_IS_VALID) self.assertEqual(process.GetProcessID(), 42) - self.assertEqual(process.GetNumThreads(), 1) + self.assertEqual(process.GetNumThreads(), 3) thread = process.GetSelectedThread() self.assertTrue(thread, "Invalid thread.") - self.assertEqual(thread.GetName(), "StackCoreScriptedThread.thread-1") + self.assertEqual(thread.GetName(), "StackCoreScriptedThread.thread-0") - self.assertEqual(thread.GetNumFrames(), 3) + self.assertEqual(thread.GetNumFrames(), 2) frame = thread.GetSelectedFrame() self.assertTrue(frame, "Invalid frame.") - self.assertEqual(frame.GetFunctionName(), "bar") - self.assertEqual(int(frame.FindValue("i", lldb.eValueTypeVariableArgument).GetValue()), 42) - self.assertEqual(int(frame.FindValue("j", lldb.eValueTypeVariableLocal).GetValue()), 42 * 42) + # self.assertEqual(frame.GetFunctionName(), "bar") + # self.assertEqual(int(frame.FindValue("i", lldb.eValueTypeVariableArgument).GetValue()), 42) + # self.assertEqual(int(frame.FindValue("j", lldb.eValueTypeVariableLocal).GetValue()), 42 * 42) diff --git a/lldb/test/API/functionalities/scripted_process/invalid_scripted_process.py b/lldb/test/API/functionalities/scripted_process/invalid_scripted_process.py --- a/lldb/test/API/functionalities/scripted_process/invalid_scripted_process.py +++ b/lldb/test/API/functionalities/scripted_process/invalid_scripted_process.py @@ -9,6 +9,7 @@ class InvalidScriptedProcess(ScriptedProcess): def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData): super().__init__(target, args) + self.threads[0] = InvalidScriptedThread(self, None) def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo: return None @@ -81,4 +82,4 @@ InvalidScriptedProcess.__name__)) else: print("Name of the class that will manage the scripted process: '%s.%s'" - % (__name__, InvalidScriptedProcess.__name__)) \ No newline at end of file + % (__name__, InvalidScriptedProcess.__name__)) diff --git a/lldb/test/API/functionalities/scripted_process/main.c b/lldb/test/API/functionalities/scripted_process/main.c deleted file mode 100644 --- a/lldb/test/API/functionalities/scripted_process/main.c +++ /dev/null @@ -1,8 +0,0 @@ -int bar(int i) { - int j = i * i; - return j; // break here -} - -int foo(int i) { return bar(i); } - -int main() { return foo(42); } diff --git a/lldb/test/API/functionalities/scripted_process/main.cpp b/lldb/test/API/functionalities/scripted_process/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/scripted_process/main.cpp @@ -0,0 +1,34 @@ +#include +#include +#include + +int bar(int i) { + int j = i * i; + return j; // break here +} + +int foo(int i) { return bar(i); } + +void call_and_wait(int &n) { + std::cout << "waiting for computation!" << std::endl; + while (n != 42 * 42) + ; + std::cout << "finished computation!" << std::endl; +} + +void compute_pow(int &n) { n = foo(n); } + +int main() { + int n = 42; + std::mutex mutex; + std::unique_lock lock(mutex); + + std::thread thread_1(call_and_wait, std::ref(n)); + std::thread thread_2(compute_pow, std::ref(n)); + lock.unlock(); + + thread_1.join(); + thread_2.join(); + + return 0; +} 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 --- a/lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py +++ b/lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py @@ -1,4 +1,4 @@ -import os,struct,signal +import os,json,struct,signal from typing import Any, Dict @@ -21,6 +21,14 @@ idx = int(self.backing_target_idx.GetStringValue(100)) self.corefile_target = target.GetDebugger().GetTargetAtIndex(idx) self.corefile_process = self.corefile_target.GetProcess() + for corefile_thread in self.corefile_process: + structured_data = lldb.SBStructuredData() + structured_data.SetFromJSON(json.dumps({ + "backing_target_idx" : idx, + "thread_idx" : corefile_thread.GetIndexID() + })) + + self.threads[corefile_thread.GetThreadID()] = StackCoreScriptedThread(self, structured_data) def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo: mem_region = lldb.SBMemoryRegionInfo() @@ -70,23 +78,43 @@ class StackCoreScriptedThread(ScriptedThread): def __init__(self, process, args): super().__init__(process, args) - self.backing_target_idx = args.GetValueForKey("backing_target_idx") + backing_target_idx = args.GetValueForKey("backing_target_idx") + thread_idx = args.GetValueForKey("thread_idx") + + def extract_value_from_structured_data(data, default_val): + if data and data.IsValid(): + if data.GetType() == lldb.eStructuredDataTypeInteger: + return data.GetIntegerValue(default_val) + if data.GetType() == lldb.eStructuredDataTypeString: + return int(data.GetStringValue(100)) + return None + + #TODO: Change to Walrus operator (:=) with oneline if assignment + # Requires python 3.8 + val = extract_value_from_structured_data(thread_idx, 0) + if val is not None: + self.idx = val self.corefile_target = None self.corefile_process = None - if (self.backing_target_idx and self.backing_target_idx.IsValid()): - if self.backing_target_idx.GetType() == lldb.eStructuredDataTypeInteger: - idx = self.backing_target_idx.GetIntegerValue(42) - if self.backing_target_idx.GetType() == lldb.eStructuredDataTypeString: - idx = int(self.backing_target_idx.GetStringValue(100)) - self.corefile_target = self.target.GetDebugger().GetTargetAtIndex(idx) + self.corefile_thread = None + + #TODO: Change to Walrus operator (:=) with oneline if assignment + # Requires python 3.8 + val = extract_value_from_structured_data(backing_target_idx, 42) + if val is not None: + self.corefile_target = self.target.GetDebugger().GetTargetAtIndex(val) self.corefile_process = self.corefile_target.GetProcess() + self.corefile_thread = self.corefile_process.GetThreadByIndexID(self.idx) + + if self.corefile_thread: + self.id = self.corefile_thread.GetThreadID() def get_thread_id(self) -> int: - return 0x19 + return self.id def get_name(self) -> str: - return StackCoreScriptedThread.__name__ + ".thread-1" + return StackCoreScriptedThread.__name__ + ".thread-" + str(self.id) def get_stop_reason(self) -> Dict[str, Any]: return { "type": lldb.eStopReasonSignal, "data": { @@ -109,10 +137,9 @@ return self.frame_zero[0:0] def get_register_context(self) -> str: - thread = self.corefile_process.GetSelectedThread() - if not thread or thread.GetNumFrames() == 0: + if not self.corefile_thread or self.corefile_thread.GetNumFrames() == 0: return None - frame = thread.GetFrameAtIndex(0) + frame = self.corefile_thread.GetFrameAtIndex(0) GPRs = None registerSet = frame.registers # Returns an SBValueList.