[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:
Med Ismail Bennani
2022-01-18 12:45:57 +01:00
parent 1b86344fa8
commit d3e0f7e150
16 changed files with 175 additions and 72 deletions

View File

@@ -1,4 +1,4 @@
C_SOURCES := main.c
CXX_SOURCES := main.cpp
ENABLE_THREADS := YES
include Makefile.rules

View File

@@ -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)

View File

@@ -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
@@ -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__))

View File

@@ -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); }

View File

@@ -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;
}

View File

@@ -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.