12 changes: 10 additions & 2 deletions lldb/source/Plugins/Process/scripted/ScriptedThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,18 @@ class ScriptedProcess;
namespace lldb_private {

class ScriptedThread : public lldb_private::Thread {

public:
ScriptedThread(ScriptedProcess &process, Status &error);
ScriptedThread(ScriptedProcess &process,
lldb::ScriptedThreadInterfaceSP interface_sp, lldb::tid_t tid,
StructuredData::GenericSP script_object_sp = nullptr);

~ScriptedThread() override;

static llvm::Expected<std::shared_ptr<ScriptedThread>>
Create(ScriptedProcess &process,
StructuredData::Generic *script_object = nullptr);

lldb::RegisterContextSP GetRegisterContext() override;

lldb::RegisterContextSP
Expand Down Expand Up @@ -59,8 +66,9 @@ class ScriptedThread : public lldb_private::Thread {
std::shared_ptr<DynamicRegisterInfo> GetDynamicRegisterInfo();

const ScriptedProcess &m_scripted_process;
lldb::ScriptedThreadInterfaceSP m_scripted_thread_interface_sp = nullptr;
lldb_private::StructuredData::GenericSP m_script_object_sp = nullptr;
std::shared_ptr<DynamicRegisterInfo> m_register_info_sp = nullptr;
lldb_private::StructuredData::ObjectSP m_script_object_sp = nullptr;
};

} // namespace lldb_private
Expand Down
Original file line number Diff line number Diff line change
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 @@ -92,6 +89,17 @@ ScriptedProcessPythonInterface::GetMemoryRegionContainingAddress(
return mem_region;
}

StructuredData::DictionarySP ScriptedProcessPythonInterface::GetThreadsInfo() {
Status error;
StructuredData::DictionarySP dict =
Dispatch<StructuredData::DictionarySP>("get_threads_info", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, error))
return {};

return dict;
}

StructuredData::DictionarySP
ScriptedProcessPythonInterface::GetThreadWithID(lldb::tid_t tid) {
Status error;
Expand Down Expand Up @@ -154,12 +162,8 @@ ScriptedProcessPythonInterface::GetScriptedThreadPluginName() {
}

lldb::ScriptedThreadInterfaceSP
ScriptedProcessPythonInterface::GetScriptedThreadInterface() {
if (!m_scripted_thread_interface_sp)
m_scripted_thread_interface_sp =
std::make_shared<ScriptedThreadPythonInterface>(m_interpreter);

return m_scripted_thread_interface_sp;
ScriptedProcessPythonInterface::CreateScriptedThreadInterface() {
return std::make_shared<ScriptedThreadPythonInterface>(m_interpreter);
}

#endif
Original file line number Diff line number Diff line change
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 All @@ -39,6 +40,8 @@ class ScriptedProcessPythonInterface : public ScriptedProcessInterface,
GetMemoryRegionContainingAddress(lldb::addr_t address,
Status &error) override;

StructuredData::DictionarySP GetThreadsInfo() override;

StructuredData::DictionarySP GetThreadWithID(lldb::tid_t tid) override;

StructuredData::DictionarySP GetRegistersForThread(lldb::tid_t tid) override;
Expand All @@ -55,7 +58,7 @@ class ScriptedProcessPythonInterface : public ScriptedProcessInterface,
llvm::Optional<std::string> GetScriptedThreadPluginName() override;

private:
lldb::ScriptedThreadInterfaceSP GetScriptedThreadInterface() override;
lldb::ScriptedThreadInterfaceSP CreateScriptedThreadInterface() override;
};
} // namespace lldb_private

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ ScriptedThreadPythonInterface::ScriptedThreadPythonInterface(

StructuredData::GenericSP ScriptedThreadPythonInterface::CreatePluginObject(
const llvm::StringRef class_name, ExecutionContext &exe_ctx,
StructuredData::DictionarySP args_sp) {

if (class_name.empty())
StructuredData::DictionarySP args_sp, StructuredData::Generic *script_obj) {
if (class_name.empty() && !script_obj)
return {};

ProcessSP process_sp = exe_ctx.GetProcessSP();
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
C_SOURCES := main.c

CXX_SOURCES := main.cpp
ENABLE_THREADS := YES
include Makefile.rules

Original file line number Diff line number Diff line change
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)
thread = process.GetSelectedThread()
self.assertEqual(process.GetNumThreads(), 3)
thread = process.GetThreadAtIndex(2)
self.assertTrue(thread, "Invalid thread.")
self.assertEqual(thread.GetName(), "StackCoreScriptedThread.thread-1")
self.assertEqual(thread.GetName(), "StackCoreScriptedThread.thread-2")

self.assertEqual(thread.GetNumFrames(), 3)
self.assertEqual(thread.GetNumFrames(), 6)
frame = thread.GetSelectedFrame()
self.assertTrue(frame, "Invalid frame.")
self.assertEqual(frame.GetFunctionName(), "bar")
self.assertIn("bar", frame.GetFunctionName())
self.assertEqual(int(frame.FindValue("i", lldb.eValueTypeVariableArgument).GetValue()), 42)
self.assertEqual(int(frame.FindValue("j", lldb.eValueTypeVariableLocal).GetValue()), 42 * 42)
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import os,struct,signal
import os,json,struct,signal

from typing import Any, Dict

Expand All @@ -21,6 +21,14 @@ def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
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()
Expand Down Expand Up @@ -70,28 +78,60 @@ def get_scripted_thread_plugin(self):
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": {
"signal": signal.SIGINT
} }
stop_reason = { "type": lldb.eStopReasonInvalid, "data": { }}

if self.corefile_thread and self.corefile_thread.IsValid:
stop_reason["type"] = self.corefile_thread.GetStopReason()

if self.corefile_thread.GetStopReasonDataCount() > 0:
if stop_reason["type"] == lldb.eStopReasonBreakpoint:
stop_reason["data"]["break_id"] = self.corefile_thread.GetStopReasonDataAtIndex(0)
stop_reason["data"]["break_loc_id"] = self.corefile_thread.GetStopReasonDataAtIndex(1)
elif stop_reason["type"] == lldb.eStopReasonSignal:
stop_reason["data"]["signal"] = signal.SIGINT
elif stop_reason["type"] == lldb.eStopReasonException:
stop_reason["data"]["desc"] = self.corefile_thread.GetStopDescription(100)

return stop_reason

def get_stackframes(self):
class ScriptedStackFrame:
Expand All @@ -109,10 +149,9 @@ def __init__(idx, cfa, pc, symbol_ctx):
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.
Expand Down