Skip to content

Commit

Permalink
[lldb/Plugins] Add support of multiple ScriptedThreads in a ScriptedP…
Browse files Browse the repository at this point in the history
…rocess

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>
  • Loading branch information
medismailben committed Jan 24, 2022
1 parent 1b86344 commit d3e0f7e
Show file tree
Hide file tree
Showing 16 changed files with 176 additions and 73 deletions.
9 changes: 5 additions & 4 deletions lldb/examples/python/scripted_process/scripted_process.py
Expand Up @@ -70,7 +70,7 @@ def get_thread_with_id(self, tid):
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.
"""
Expand Down Expand Up @@ -212,11 +212,12 @@ def __init__(self, process, args):
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
Expand Down
3 changes: 2 additions & 1 deletion lldb/include/lldb/Interpreter/ScriptedInterface.h
Expand Up @@ -27,7 +27,8 @@ class ScriptedInterface {

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,
Expand Down
6 changes: 4 additions & 2 deletions lldb/include/lldb/Interpreter/ScriptedProcessInterface.h
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand Down
61 changes: 47 additions & 14 deletions lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
Expand Up @@ -164,9 +164,6 @@ Status ScriptedProcess::DoLaunch(Module *exe_module,

SetPrivateState(eStateStopped);

UpdateThreadListIfNeeded();
GetThreadList();

return {};
}

Expand Down Expand Up @@ -304,19 +301,55 @@ bool ScriptedProcess::DoUpdateThreadList(ThreadList &old_thread_list,
.str(),
error);

lldb::ThreadSP thread_sp;
thread_sp = std::make_shared<ScriptedThread>(*this, error);

if (!thread_sp || error.Fail())
return GetInterface().ErrorWithMessage<bool>(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<bool>(
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<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,
error.AsCString(), error);

RegisterContextSP reg_ctx_sp = thread_sp->GetRegisterContext();
if (!reg_ctx_sp)
return GetInterface().ErrorWithMessage<bool>(
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;
}
Expand Down
24 changes: 15 additions & 9 deletions lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
Expand Up @@ -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()) {
Expand All @@ -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(); }
Expand Down
3 changes: 2 additions & 1 deletion lldb/source/Plugins/Process/scripted/ScriptedThread.h
Expand Up @@ -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;

Expand Down
Expand Up @@ -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 {};

Expand All @@ -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)));

Expand Down
Expand Up @@ -25,7 +25,8 @@ class ScriptedProcessPythonInterface : public ScriptedProcessInterface,
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;

Expand Down
Expand Up @@ -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 {};

Expand All @@ -43,9 +42,15 @@ StructuredData::GenericSP ScriptedThreadPythonInterface::CreatePluginObject(
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<PyObject *>(script_obj->GetValue()));

if (!ret_val)
return {};
Expand Down
Expand Up @@ -24,7 +24,8 @@ class ScriptedThreadPythonInterface : public ScriptedThreadInterface,

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;

Expand Down
4 changes: 2 additions & 2 deletions 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

Expand Up @@ -130,7 +130,8 @@ def cleanup():

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)
Expand Down Expand Up @@ -186,14 +187,14 @@ def cleanup():
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)
Expand Up @@ -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
Expand Down Expand Up @@ -81,4 +82,4 @@ def __lldb_init_module(debugger, dict):
InvalidScriptedProcess.__name__))
else:
print("Name of the class that will manage the scripted process: '%s.%s'"
% (__name__, InvalidScriptedProcess.__name__))
% (__name__, InvalidScriptedProcess.__name__))
8 changes: 0 additions & 8 deletions lldb/test/API/functionalities/scripted_process/main.c

This file was deleted.

34 changes: 34 additions & 0 deletions lldb/test/API/functionalities/scripted_process/main.cpp
@@ -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;
}

0 comments on commit d3e0f7e

Please sign in to comment.