forked from OSchip/llvm-project
[lldb/Plugins] Add support of multiple ScriptedThreads in a ScriptedProcess
This patch adds support of multiple Scripted Threads in a ScriptedProcess. This is done by fetching the Scripted Threads info dictionary at every ScriptedProcess::DoUpdateThreadList and iterate over each element to create a new ScriptedThread using the object instance, if it was not already available. This patch also adds the ability to pass a pointer of a script interpreter object instance to initialize a ScriptedInterface instead of having to call the script object initializer in the ScriptedInterface constructor. This is used to instantiate the ScriptedThreadInterface from the ScriptedThread constructor, to be able to perform call on that script interpreter object instance. Finally, the patch also updates the scripted process test to check for multiple threads. rdar://84507704 Differential Revision: https://reviews.llvm.org/D117071 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
This commit is contained in:
parent
1b86344fa8
commit
d3e0f7e150
|
@ -70,7 +70,7 @@ class ScriptedProcess:
|
|||
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 @@ class ScriptedThread:
|
|||
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
|
||||
|
|
|
@ -27,7 +27,8 @@ public:
|
|||
|
||||
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 <typename Ret>
|
||||
Ret ErrorWithMessage(llvm::StringRef caller_name, llvm::StringRef error_msg,
|
||||
|
|
|
@ -23,7 +23,8 @@ class ScriptedProcessInterface : virtual public ScriptedInterface {
|
|||
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 @@ class ScriptedThreadInterface : virtual public ScriptedInterface {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -164,9 +164,6 @@ Status ScriptedProcess::DoLaunch(Module *exe_module,
|
|||
|
||||
SetPrivateState(eStateStopped);
|
||||
|
||||
UpdateThreadListIfNeeded();
|
||||
GetThreadList();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -304,8 +301,35 @@ bool ScriptedProcess::DoUpdateThreadList(ThreadList &old_thread_list,
|
|||
.str(),
|
||||
error);
|
||||
|
||||
lldb::ThreadSP thread_sp;
|
||||
thread_sp = std::make_shared<ScriptedThread>(*this, error);
|
||||
StructuredData::DictionarySP thread_info_sp = GetInterface().GetThreadsInfo();
|
||||
|
||||
if (!thread_info_sp)
|
||||
return GetInterface().ErrorWithMessage<bool>(
|
||||
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<bool>(
|
||||
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<bool>(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<ScriptedThread>(*this, error, val->GetAsGeneric());
|
||||
|
||||
if (!thread_sp || error.Fail())
|
||||
return GetInterface().ErrorWithMessage<bool>(LLVM_PRETTY_FUNCTION,
|
||||
|
@ -314,10 +338,19 @@ bool ScriptedProcess::DoUpdateThreadList(ThreadList &old_thread_list,
|
|||
RegisterContextSP reg_ctx_sp = thread_sp->GetRegisterContext();
|
||||
if (!reg_ctx_sp)
|
||||
return GetInterface().ErrorWithMessage<bool>(
|
||||
LLVM_PRETTY_FUNCTION, "Invalid Register Context", error);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,8 @@ void ScriptedThread::CheckInterpreterAndScriptObject() const {
|
|||
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 @@ ScriptedThread::ScriptedThread(ScriptedProcess &process, Status &error)
|
|||
|
||||
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(); }
|
||||
|
|
|
@ -26,7 +26,8 @@ namespace lldb_private {
|
|||
|
||||
class ScriptedThread : public lldb_private::Thread {
|
||||
public:
|
||||
ScriptedThread(ScriptedProcess &process, Status &error);
|
||||
ScriptedThread(ScriptedProcess &process, Status &error,
|
||||
StructuredData::Generic *script_object = nullptr);
|
||||
|
||||
~ScriptedThread() override;
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ ScriptedProcessPythonInterface::ScriptedProcessPythonInterface(
|
|||
|
||||
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 @@ StructuredData::GenericSP ScriptedProcessPythonInterface::CreatePluginObject(
|
|||
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)));
|
||||
|
||||
|
|
|
@ -25,7 +25,8 @@ public:
|
|||
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;
|
||||
|
||||
|
|
|
@ -31,8 +31,7 @@ ScriptedThreadPythonInterface::ScriptedThreadPythonInterface(
|
|||
|
||||
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 @@ StructuredData::GenericSP ScriptedThreadPythonInterface::CreatePluginObject(
|
|||
Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
|
||||
Locker::FreeLock);
|
||||
|
||||
PythonObject ret_val = LLDBSwigPythonCreateScriptedThread(
|
||||
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<PyObject *>(script_obj->GetValue()));
|
||||
|
||||
if (!ret_val)
|
||||
return {};
|
||||
|
|
|
@ -24,7 +24,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;
|
||||
|
||||
lldb::tid_t GetThreadID() override;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
C_SOURCES := main.c
|
||||
|
||||
CXX_SOURCES := main.cpp
|
||||
ENABLE_THREADS := YES
|
||||
include Makefile.rules
|
||||
|
||||
|
|
|
@ -130,7 +130,8 @@ class ScriptedProcesTestCase(TestBase):
|
|||
|
||||
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 @@ class ScriptedProcesTestCase(TestBase):
|
|||
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)
|
||||
|
|
|
@ -9,6 +9,7 @@ from lldb.plugins.scripted_process import ScriptedThread
|
|||
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
|
||||
|
|
|
@ -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); }
|
|
@ -0,0 +1,34 @@
|
|||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
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<std::mutex> 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;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import os,struct,signal
|
||||
import os,json,struct,signal
|
||||
|
||||
from typing import Any, Dict
|
||||
|
||||
|
@ -21,6 +21,14 @@ class StackCoreScriptedProcess(ScriptedProcess):
|
|||
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 StackCoreScriptedProcess(ScriptedProcess):
|
|||
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 @@ class StackCoreScriptedThread(ScriptedThread):
|
|||
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.
|
||||
|
|
Loading…
Reference in New Issue