diff --git a/lldb/bindings/interface/SBMemoryRegionInfo.i b/lldb/bindings/interface/SBMemoryRegionInfo.i --- a/lldb/bindings/interface/SBMemoryRegionInfo.i +++ b/lldb/bindings/interface/SBMemoryRegionInfo.i @@ -20,6 +20,9 @@ SBMemoryRegionInfo (const lldb::SBMemoryRegionInfo &rhs); + SBMemoryRegionInfo::SBMemoryRegionInfo(const char *name, lldb::addr_t begin, + lldb::addr_t end, uint32_t permissions, bool mapped, bool stack_memory); + ~SBMemoryRegionInfo (); void diff --git a/lldb/bindings/interface/SBMemoryRegionInfoList.i b/lldb/bindings/interface/SBMemoryRegionInfoList.i --- a/lldb/bindings/interface/SBMemoryRegionInfoList.i +++ b/lldb/bindings/interface/SBMemoryRegionInfoList.i @@ -24,6 +24,9 @@ uint32_t GetSize () const; + bool + GetMemoryRegionContainingAddress (lldb::addr_t addr, SBMemoryRegionInfo ®ion_info); + bool GetMemoryRegionAtIndex (uint32_t idx, SBMemoryRegionInfo ®ion_info); diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig --- a/lldb/bindings/python/python-wrapper.swig +++ b/lldb/bindings/python/python-wrapper.swig @@ -340,6 +340,63 @@ Py_RETURN_NONE; } +SWIGEXPORT void* +LLDBSwigPythonCreateScriptedThread +( + const char *python_class_name, + const char *session_dictionary_name, + const lldb::TargetSP& target_sp, + std::string &error_string +) +{ + if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name) + Py_RETURN_NONE; + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary(python_class_name, dict); + + if (!pfunc.IsAllocated()) { + error_string.append("could not find script class: "); + error_string.append(python_class_name); + return nullptr; + } + + // I do not want the SBTarget to be deallocated when going out of scope + // because python has ownership of it and will manage memory for this + // object by itself + PythonObject target_arg(PyRefType::Owned, SBTypeToSWIGWrapper(new lldb::SBTarget(target_sp))); + + if (!target_arg.IsAllocated()) + Py_RETURN_NONE; + + llvm::Expected arg_info = pfunc.GetArgInfo(); + if (!arg_info) { + llvm::handleAllErrors( + arg_info.takeError(), + [&](PythonException &E) { + error_string.append(E.ReadBacktrace()); + }, + [&](const llvm::ErrorInfoBase &E) { + error_string.append(E.message()); + }); + Py_RETURN_NONE; + } + + PythonObject result = {}; + if (arg_info.get().max_positional_args == 1) { + result = pfunc(target_arg); + } else { + error_string.assign("wrong number of arguments in __init__, should be 2 or 3 (not including self)"); + Py_RETURN_NONE; + } + + if (result.IsAllocated()) + return result.release(); + Py_RETURN_NONE; +} + SWIGEXPORT void* LLDBSwigPythonCreateScriptedThreadPlan ( diff --git a/lldb/examples/python/scripted_process/main.stack-dump b/lldb/examples/python/scripted_process/main.stack-dump new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ bool: return True + def get_scripted_thread_plugin(self): + return MyScriptedThread.__module__ + "." + MyScriptedThread.__name__ + + +class MyScriptedThread(ScriptedThread): + def __init__(self, target): + super().__init__(target) + + def get_thread_id(self) -> int: + return 0x19 + + def get_name(self) -> str: + return MyScriptedThread.__name__ + ".thread-1" + + def get_state(self) -> int: + return lldb.eStateStopped + + def get_stop_reason(self) -> Dict[str, Any]: + return { "type": lldb.eStopReasonSignal, "data": { + "signal": signal.SIGINT + } } + + def get_stackframes(self): + class ScriptedStackFrame: + def __init__(idx, cfa, pc, symbol_ctx): + self.idx = idx + self.cfa = cfa + self.pc = pc + self.symbol_ctx = symbol_ctx + + + symbol_ctx = lldb.SBSymbolContext() + frame_zero = ScriptedStackFrame(0, 0x42424242, 0x5000000, symbol_ctx) + self.frames.append(frame_zero) + + return self.frame_zero[0:0] + + def get_register_context(self) -> str: + return struct.pack( + '21Q', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21) + + def __lldb_init_module(debugger, dict): if not 'SKIP_SCRIPTED_PROCESS_LAUNCH' in os.environ: debugger.HandleCommand( 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 @@ -163,3 +163,163 @@ bool: True if scripted process is alive. False otherwise. """ pass + + @abstractmethod + def get_scripted_thread_plugin(self): + """ Get scripted thread plugin name. + + Returns: + str: Name of the scripted thread plugin. + """ + return None + +@six.add_metaclass(ABCMeta) +class ScriptedThread: + + """ + The base class for a scripted thread. + + Most of the base class methods are `@abstractmethod` that need to be + overwritten by the inheriting class. + + DISCLAIMER: THIS INTERFACE IS STILL UNDER DEVELOPMENT AND NOT STABLE. + THE METHODS EXPOSED MIGHT CHANGE IN THE FUTURE. + """ + + @abstractmethod + def __init__(self, target): + """ Construct a scripted thread. + + Args: + target (lldb.SBTarget): The target launching the scripted process. + args (lldb.SBStructuredData): A Dictionary holding arbitrary + key/value pairs used by the scripted process. + """ + self.target = None + self.args = None + if isinstance(target, lldb.SBTarget) and target.IsValid(): + self.target = target + + self.id = None + self.name = None + self.queue = None + self.state = None + self.stop_reason = None + self.register_info = None + self.register_ctx = [] + self.frames = [] + + @abstractmethod + def get_thread_id(self): + """ Get the scripted thread identifier. + + Returns: + int: The identifier of the scripted thread. + """ + pass + + @abstractmethod + def get_name(self): + """ Get the scripted thread name. + + Returns: + str: The name of the scripted thread. + """ + pass + + @abstractmethod + def get_state(self): + """ Get the scripted thread state type. + + eStateStopped, ///< Process or thread is stopped and can be examined. + eStateRunning, ///< Process or thread is running and can't be examined. + eStateStepping, ///< Process or thread is in the process of stepping and can + /// not be examined. + + Returns: + int: The state type of the scripted thread. + """ + pass + + # @abstractmethod + def get_queue(self): + """ Get the scripted thread associated queue name. + + Returns: + str: The queue name associated with the scripted thread. + """ + pass + + # @abstractmethod + def get_stop_reason(self): + """ Get the dictionary describing the stop reason type with some data. + + Returns: + Dict: The dictionary holding the stop reason type and the possibly + the stop reason data. + """ + pass + + # @abstractmethod + def get_stackframes(self): + """ Get the list of stack frames for the scripted thread. + + ``` + class ScriptedStackFrame: + def __init__(idx, cfa, pc, symbol_ctx): + self.idx = idx + self.cfa = cfa + self.pc = pc + self.symbol_ctx = symbol_ctx + ``` + + Returns: + List[ScriptedFrame]: A list of `ScriptedStackFrame` + containing for each entry, the frame index, the canonical + frame address, the program counter value for that frame + and a symbol context. + None if the list is empty. + """ + return 0 + + def get_register_info(self): + if self.register_info is None: + self.register_info = dict() + triple = self.target.triple + if triple: + arch = triple.split('-')[0] + if arch == 'x86_64': + self.register_info['sets'] = ['GPR', 'FPU', 'EXC'] + self.register_info['registers'] = [ + {'name': 'rax', 'bitsize': 64, 'offset': 0, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 0, 'dwarf': 0}, + {'name': 'rbx', 'bitsize': 64, 'offset': 8, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 3, 'dwarf': 3}, + {'name': 'rcx', 'bitsize': 64, 'offset': 16, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 2, 'dwarf': 2, 'generic': 'arg4', 'alt-name': 'arg4', }, + {'name': 'rdx', 'bitsize': 64, 'offset': 24, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 1, 'dwarf': 1, 'generic': 'arg3', 'alt-name': 'arg3', }, + {'name': 'rdi', 'bitsize': 64, 'offset': 32, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 5, 'dwarf': 5, 'generic': 'arg1', 'alt-name': 'arg1', }, + {'name': 'rsi', 'bitsize': 64, 'offset': 40, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 4, 'dwarf': 4, 'generic': 'arg2', 'alt-name': 'arg2', }, + {'name': 'rbp', 'bitsize': 64, 'offset': 48, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 6, 'dwarf': 6, 'generic': 'fp', 'alt-name': 'fp', }, + {'name': 'rsp', 'bitsize': 64, 'offset': 56, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 7, 'dwarf': 7, 'generic': 'sp', 'alt-name': 'sp', }, + {'name': 'r8', 'bitsize': 64, 'offset': 64, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 8, 'dwarf': 8, 'generic': 'arg5', 'alt-name': 'arg5', }, + {'name': 'r9', 'bitsize': 64, 'offset': 72, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 9, 'dwarf': 9, 'generic': 'arg6', 'alt-name': 'arg6', }, + {'name': 'r10', 'bitsize': 64, 'offset': 80, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 10, 'dwarf': 10}, + {'name': 'r11', 'bitsize': 64, 'offset': 88, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 11, 'dwarf': 11}, + {'name': 'r12', 'bitsize': 64, 'offset': 96, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 12, 'dwarf': 12}, + {'name': 'r13', 'bitsize': 64, 'offset': 104, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 13, 'dwarf': 13}, + {'name': 'r14', 'bitsize': 64, 'offset': 112, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 14, 'dwarf': 14}, + {'name': 'r15', 'bitsize': 64, 'offset': 120, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 15, 'dwarf': 15}, + {'name': 'rip', 'bitsize': 64, 'offset': 128, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 16, 'dwarf': 16, 'generic': 'pc', 'alt-name': 'pc'}, + {'name': 'rflags', 'bitsize': 64, 'offset': 136, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'generic': 'flags', 'alt-name': 'flags'}, + {'name': 'cs', 'bitsize': 64, 'offset': 144, 'encoding': 'uint', 'format': 'hex', 'set': 0}, + {'name': 'fs', 'bitsize': 64, 'offset': 152, 'encoding': 'uint', 'format': 'hex', 'set': 0}, + {'name': 'gs', 'bitsize': 64, 'offset': 160, 'encoding': 'uint', 'format': 'hex', 'set': 0}, + ] + return self.register_info + + @abstractmethod + def get_register_context(self): + """ Get the scripted thread register context + + Returns: + str: A byte representing all register's value. + """ + pass diff --git a/lldb/include/lldb/API/SBMemoryRegionInfo.h b/lldb/include/lldb/API/SBMemoryRegionInfo.h --- a/lldb/include/lldb/API/SBMemoryRegionInfo.h +++ b/lldb/include/lldb/API/SBMemoryRegionInfo.h @@ -20,6 +20,10 @@ SBMemoryRegionInfo(const lldb::SBMemoryRegionInfo &rhs); + SBMemoryRegionInfo(const char *name, lldb::addr_t begin, lldb::addr_t end, + uint32_t permissions, bool mapped, + bool stack_memory = false); + ~SBMemoryRegionInfo(); const lldb::SBMemoryRegionInfo & @@ -117,6 +121,8 @@ friend class SBProcess; friend class SBMemoryRegionInfoList; + friend class lldb_private::ScriptInterpreter; + lldb_private::MemoryRegionInfo &ref(); const lldb_private::MemoryRegionInfo &ref() const; @@ -124,7 +130,7 @@ // Unused. SBMemoryRegionInfo(const lldb_private::MemoryRegionInfo *lldb_object_ptr); - lldb::MemoryRegionInfoUP m_opaque_up; + lldb::MemoryRegionInfoSP m_opaque_sp; }; } // namespace lldb diff --git a/lldb/include/lldb/API/SBMemoryRegionInfoList.h b/lldb/include/lldb/API/SBMemoryRegionInfoList.h --- a/lldb/include/lldb/API/SBMemoryRegionInfoList.h +++ b/lldb/include/lldb/API/SBMemoryRegionInfoList.h @@ -27,6 +27,9 @@ uint32_t GetSize() const; + bool GetMemoryRegionContainingAddress(lldb::addr_t addr, + SBMemoryRegionInfo ®ion_info); + bool GetMemoryRegionAtIndex(uint32_t idx, SBMemoryRegionInfo ®ion_info); void Append(lldb::SBMemoryRegionInfo ®ion); diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -11,6 +11,7 @@ #include "lldb/API/SBData.h" #include "lldb/API/SBError.h" +#include "lldb/API/SBMemoryRegionInfo.h" #include "lldb/Breakpoint/BreakpointOptions.h" #include "lldb/Core/Communication.h" #include "lldb/Core/PluginInterface.h" @@ -564,6 +565,9 @@ Status GetStatusFromSBError(const lldb::SBError &error) const; + lldb::MemoryRegionInfoSP GetOpaquePtrFromSBMemoryRegionInfo( + const lldb::SBMemoryRegionInfo &mem_region) const; + protected: Debugger &m_debugger; lldb::ScriptLanguage m_script_lang; 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 @@ -57,6 +57,46 @@ virtual lldb::pid_t GetProcessID() { return LLDB_INVALID_PROCESS_ID; } virtual bool IsAlive() { return true; } + + virtual llvm::Optional GetScriptedThreadPluginName() { + return llvm::None; + } + +protected: + friend class ScriptedThread; + virtual lldb::ScriptedThreadInterfaceSP GetScriptedThreadInterface() { + return nullptr; + } + + lldb::ScriptedThreadInterfaceSP m_scripted_thread_interface_sp = nullptr; +}; + +class ScriptedThreadInterface : virtual public ScriptedInterface { +public: + StructuredData::GenericSP + CreatePluginObject(const llvm::StringRef class_name, + ExecutionContext &exe_ctx, + StructuredData::DictionarySP args_sp) override { + return nullptr; + } + + virtual lldb::tid_t GetThreadID() { return LLDB_INVALID_THREAD_ID; } + + virtual llvm::Optional GetName() { return llvm::None; } + + virtual lldb::StateType GetState() { return lldb::eStateInvalid; } + + virtual llvm::Optional GetQueue() { return llvm::None; } + + virtual StructuredData::DictionarySP GetStopReason() { return nullptr; } + + virtual StructuredData::ArraySP GetStackFrames() { return nullptr; } + + virtual StructuredData::DictionarySP GetRegisterInfo() { return nullptr; } + + virtual llvm::Optional GetRegisterContext() { + return llvm::None; + } }; } // namespace lldb_private diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -175,6 +175,7 @@ class ScriptInterpreter; class ScriptInterpreterLocker; class ScriptedProcessInterface; +class ScriptedThreadInterface; class ScriptedSyntheticChildren; class SearchFilter; class Section; @@ -395,6 +396,8 @@ typedef std::unique_ptr ScriptInterpreterUP; typedef std::unique_ptr ScriptedProcessInterfaceUP; +typedef std::shared_ptr + ScriptedThreadInterfaceSP; typedef std::shared_ptr SectionSP; typedef std::unique_ptr SectionListUP; typedef std::weak_ptr SectionWP; diff --git a/lldb/source/API/SBMemoryRegionInfo.cpp b/lldb/source/API/SBMemoryRegionInfo.cpp --- a/lldb/source/API/SBMemoryRegionInfo.cpp +++ b/lldb/source/API/SBMemoryRegionInfo.cpp @@ -18,21 +18,39 @@ using namespace lldb; using namespace lldb_private; -SBMemoryRegionInfo::SBMemoryRegionInfo() : m_opaque_up(new MemoryRegionInfo()) { +SBMemoryRegionInfo::SBMemoryRegionInfo() : m_opaque_sp(new MemoryRegionInfo()) { LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBMemoryRegionInfo); } +SBMemoryRegionInfo::SBMemoryRegionInfo(const char *name, lldb::addr_t begin, + lldb::addr_t end, uint32_t permissions, + bool mapped, bool stack_memory) + : SBMemoryRegionInfo() { + LLDB_RECORD_CONSTRUCTOR( + SBMemoryRegionInfo, + (const char *, lldb::addr_t, lldb::addr_t, uint32_t, bool, bool), name, + begin, end, permissions, mapped, stack_memory); + m_opaque_sp->SetName(name); + m_opaque_sp->GetRange().SetRangeBase(begin); + m_opaque_sp->GetRange().SetRangeEnd(end); + m_opaque_sp->SetLLDBPermissions(permissions); + m_opaque_sp->SetMapped(mapped ? MemoryRegionInfo::eYes + : MemoryRegionInfo::eNo); + m_opaque_sp->SetIsStackMemory(stack_memory ? MemoryRegionInfo::eYes + : MemoryRegionInfo::eNo); +} + SBMemoryRegionInfo::SBMemoryRegionInfo(const MemoryRegionInfo *lldb_object_ptr) - : m_opaque_up(new MemoryRegionInfo()) { + : m_opaque_sp(new MemoryRegionInfo()) { if (lldb_object_ptr) ref() = *lldb_object_ptr; } SBMemoryRegionInfo::SBMemoryRegionInfo(const SBMemoryRegionInfo &rhs) - : m_opaque_up() { + : m_opaque_sp() { LLDB_RECORD_CONSTRUCTOR(SBMemoryRegionInfo, (const lldb::SBMemoryRegionInfo &), rhs); - m_opaque_up = clone(rhs.m_opaque_up); + m_opaque_sp = clone(rhs.m_opaque_sp); } const SBMemoryRegionInfo &SBMemoryRegionInfo:: @@ -42,7 +60,7 @@ SBMemoryRegionInfo, operator=,(const lldb::SBMemoryRegionInfo &), rhs); if (this != &rhs) - m_opaque_up = clone(rhs.m_opaque_up); + m_opaque_sp = clone(rhs.m_opaque_sp); return LLDB_RECORD_RESULT(*this); } @@ -51,7 +69,7 @@ void SBMemoryRegionInfo::Clear() { LLDB_RECORD_METHOD_NO_ARGS(void, SBMemoryRegionInfo, Clear); - m_opaque_up->Clear(); + m_opaque_sp->Clear(); } bool SBMemoryRegionInfo::operator==(const SBMemoryRegionInfo &rhs) const { @@ -70,56 +88,56 @@ return ref() != rhs.ref(); } -MemoryRegionInfo &SBMemoryRegionInfo::ref() { return *m_opaque_up; } +MemoryRegionInfo &SBMemoryRegionInfo::ref() { return *m_opaque_sp; } -const MemoryRegionInfo &SBMemoryRegionInfo::ref() const { return *m_opaque_up; } +const MemoryRegionInfo &SBMemoryRegionInfo::ref() const { return *m_opaque_sp; } lldb::addr_t SBMemoryRegionInfo::GetRegionBase() { LLDB_RECORD_METHOD_NO_ARGS(lldb::addr_t, SBMemoryRegionInfo, GetRegionBase); - return m_opaque_up->GetRange().GetRangeBase(); + return m_opaque_sp->GetRange().GetRangeBase(); } lldb::addr_t SBMemoryRegionInfo::GetRegionEnd() { LLDB_RECORD_METHOD_NO_ARGS(lldb::addr_t, SBMemoryRegionInfo, GetRegionEnd); - return m_opaque_up->GetRange().GetRangeEnd(); + return m_opaque_sp->GetRange().GetRangeEnd(); } bool SBMemoryRegionInfo::IsReadable() { LLDB_RECORD_METHOD_NO_ARGS(bool, SBMemoryRegionInfo, IsReadable); - return m_opaque_up->GetReadable() == MemoryRegionInfo::eYes; + return m_opaque_sp->GetReadable() == MemoryRegionInfo::eYes; } bool SBMemoryRegionInfo::IsWritable() { LLDB_RECORD_METHOD_NO_ARGS(bool, SBMemoryRegionInfo, IsWritable); - return m_opaque_up->GetWritable() == MemoryRegionInfo::eYes; + return m_opaque_sp->GetWritable() == MemoryRegionInfo::eYes; } bool SBMemoryRegionInfo::IsExecutable() { LLDB_RECORD_METHOD_NO_ARGS(bool, SBMemoryRegionInfo, IsExecutable); - return m_opaque_up->GetExecutable() == MemoryRegionInfo::eYes; + return m_opaque_sp->GetExecutable() == MemoryRegionInfo::eYes; } bool SBMemoryRegionInfo::IsMapped() { LLDB_RECORD_METHOD_NO_ARGS(bool, SBMemoryRegionInfo, IsMapped); - return m_opaque_up->GetMapped() == MemoryRegionInfo::eYes; + return m_opaque_sp->GetMapped() == MemoryRegionInfo::eYes; } const char *SBMemoryRegionInfo::GetName() { LLDB_RECORD_METHOD_NO_ARGS(const char *, SBMemoryRegionInfo, GetName); - return m_opaque_up->GetName().AsCString(); + return m_opaque_sp->GetName().AsCString(); } bool SBMemoryRegionInfo::HasDirtyMemoryPageList() { LLDB_RECORD_METHOD_NO_ARGS(bool, SBMemoryRegionInfo, HasDirtyMemoryPageList); - return m_opaque_up->GetDirtyPageList().hasValue(); + return m_opaque_sp->GetDirtyPageList().hasValue(); } uint32_t SBMemoryRegionInfo::GetNumDirtyPages() { @@ -127,7 +145,7 @@ uint32_t num_dirty_pages = 0; llvm::Optional> dirty_page_list = - m_opaque_up->GetDirtyPageList(); + m_opaque_sp->GetDirtyPageList(); if (dirty_page_list.hasValue()) num_dirty_pages = dirty_page_list.getValue().size(); @@ -140,7 +158,7 @@ addr_t dirty_page_addr = LLDB_INVALID_ADDRESS; const llvm::Optional> &dirty_page_list = - m_opaque_up->GetDirtyPageList(); + m_opaque_sp->GetDirtyPageList(); if (dirty_page_list.hasValue() && idx < dirty_page_list.getValue().size()) dirty_page_addr = dirty_page_list.getValue()[idx]; @@ -150,7 +168,7 @@ int SBMemoryRegionInfo::GetPageSize() { LLDB_RECORD_METHOD_NO_ARGS(int, SBMemoryRegionInfo, GetPageSize); - return m_opaque_up->GetPageSize(); + return m_opaque_sp->GetPageSize(); } bool SBMemoryRegionInfo::GetDescription(SBStream &description) { @@ -158,13 +176,13 @@ (lldb::SBStream &), description); Stream &strm = description.ref(); - const addr_t load_addr = m_opaque_up->GetRange().base; + const addr_t load_addr = m_opaque_sp->GetRange().base; strm.Printf("[0x%16.16" PRIx64 "-0x%16.16" PRIx64 " ", load_addr, - load_addr + m_opaque_up->GetRange().size); - strm.Printf(m_opaque_up->GetReadable() ? "R" : "-"); - strm.Printf(m_opaque_up->GetWritable() ? "W" : "-"); - strm.Printf(m_opaque_up->GetExecutable() ? "X" : "-"); + load_addr + m_opaque_sp->GetRange().size); + strm.Printf(m_opaque_sp->GetReadable() ? "R" : "-"); + strm.Printf(m_opaque_sp->GetWritable() ? "W" : "-"); + strm.Printf(m_opaque_sp->GetExecutable() ? "X" : "-"); strm.Printf("]"); return true; @@ -178,6 +196,9 @@ LLDB_REGISTER_CONSTRUCTOR(SBMemoryRegionInfo, ()); LLDB_REGISTER_CONSTRUCTOR(SBMemoryRegionInfo, (const lldb::SBMemoryRegionInfo &)); + LLDB_REGISTER_CONSTRUCTOR( + SBMemoryRegionInfo, + (const char *, lldb::addr_t, lldb::addr_t, uint32_t, bool, bool)); LLDB_REGISTER_METHOD( const lldb::SBMemoryRegionInfo &, SBMemoryRegionInfo, operator=,(const lldb::SBMemoryRegionInfo &)); diff --git a/lldb/source/API/SBMemoryRegionInfoList.cpp b/lldb/source/API/SBMemoryRegionInfoList.cpp --- a/lldb/source/API/SBMemoryRegionInfoList.cpp +++ b/lldb/source/API/SBMemoryRegionInfoList.cpp @@ -48,6 +48,17 @@ void Clear() { m_regions.clear(); } + bool GetMemoryRegionContainingAddress(lldb::addr_t addr, + MemoryRegionInfo ®ion_info) { + for (auto ®ion : m_regions) { + if (region.GetRange().Contains(addr)) { + region_info = region; + return true; + } + } + return false; + } + bool GetMemoryRegionInfoAtIndex(size_t index, MemoryRegionInfo ®ion_info) { if (index >= GetSize()) @@ -103,6 +114,15 @@ return m_opaque_up->GetSize(); } +bool SBMemoryRegionInfoList::GetMemoryRegionContainingAddress( + lldb::addr_t addr, SBMemoryRegionInfo ®ion_info) { + LLDB_RECORD_METHOD( + bool, SBMemoryRegionInfoList, GetMemoryRegionContainingAddress, + (lldb::addr_t, lldb::SBMemoryRegionInfo &), addr, region_info); + + return m_opaque_up->GetMemoryRegionContainingAddress(addr, region_info.ref()); +} + bool SBMemoryRegionInfoList::GetMemoryRegionAtIndex( uint32_t idx, SBMemoryRegionInfo ®ion_info) { LLDB_RECORD_METHOD(bool, SBMemoryRegionInfoList, GetMemoryRegionAtIndex, @@ -153,6 +173,9 @@ SBMemoryRegionInfoList, operator=,( const lldb::SBMemoryRegionInfoList &)); LLDB_REGISTER_METHOD_CONST(uint32_t, SBMemoryRegionInfoList, GetSize, ()); + LLDB_REGISTER_METHOD(bool, SBMemoryRegionInfoList, + GetMemoryRegionContainingAddress, + (lldb::addr_t, lldb::SBMemoryRegionInfo &)); LLDB_REGISTER_METHOD(bool, SBMemoryRegionInfoList, GetMemoryRegionAtIndex, (uint32_t, lldb::SBMemoryRegionInfo &)); LLDB_REGISTER_METHOD(void, SBMemoryRegionInfoList, Clear, ()); diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -83,6 +83,11 @@ return Status(); } +lldb::MemoryRegionInfoSP ScriptInterpreter::GetOpaquePtrFromSBMemoryRegionInfo( + const lldb::SBMemoryRegionInfo &mem_region) const { + return mem_region.m_opaque_sp; +} + lldb::ScriptLanguage ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) { if (language.equals_insensitive(LanguageToString(eScriptLanguageNone))) diff --git a/lldb/source/Plugins/Process/scripted/CMakeLists.txt b/lldb/source/Plugins/Process/scripted/CMakeLists.txt --- a/lldb/source/Plugins/Process/scripted/CMakeLists.txt +++ b/lldb/source/Plugins/Process/scripted/CMakeLists.txt @@ -1,5 +1,6 @@ add_lldb_library(lldbPluginScriptedProcess PLUGIN ScriptedProcess.cpp + ScriptedThread.cpp LINK_LIBS lldbCore diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h --- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h +++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h @@ -13,6 +13,8 @@ #include "lldb/Utility/ConstString.h" #include "lldb/Utility/Status.h" +#include "ScriptedThread.h" + #include namespace lldb_private { @@ -103,6 +105,8 @@ ThreadList &new_thread_list) override; private: + friend class ScriptedThread; + void CheckInterpreterAndScriptObject() const; ScriptedProcessInterface &GetInterface() const; static bool IsScriptLanguageSupported(lldb::ScriptLanguage language); 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 @@ -294,6 +294,33 @@ // This is supposed to get the current set of threads, if any of them are in // old_thread_list then they get copied to new_thread_list, and then any // actually new threads will get added to new_thread_list. + + CheckInterpreterAndScriptObject(); + + auto error_with_message = [](llvm::StringRef message) { + LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS), + "ScriptedProcess::%s ERROR = %s", __FUNCTION__, message.data()); + return false; + }; + + ScriptLanguage language = m_interpreter->GetLanguage(); + + if (language != eScriptLanguagePython) + return error_with_message( + (llvm::Twine("ScriptInterpreter language (") + + llvm::Twine(m_interpreter->LanguageToString(language)) + + llvm::Twine(") not supported.")) + .str()); + + Status error; + lldb::ThreadSP thread_sp; + thread_sp = std::make_shared(*this, error); + + if (!thread_sp || error.Fail()) + return error_with_message(error.AsCString()); + + new_thread_list.AddThread(thread_sp); + 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 new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.h @@ -0,0 +1,71 @@ +//===-- ScriptedThread.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SCRIPTED_THREAD_H +#define LLDB_SOURCE_PLUGINS_SCRIPTED_THREAD_H + +#include + +#include "ScriptedProcess.h" + +#include "Plugins/Process/Utility/DynamicRegisterInfo.h" +#include "Plugins/Process/Utility/RegisterContextMemory.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Target/Thread.h" + +namespace lldb_private { +class ScriptedProcess; +} + +namespace lldb_private { + +class ScriptedThread : public lldb_private::Thread { +public: + ScriptedThread(ScriptedProcess &process, Status &error); + + ~ScriptedThread() override; + + lldb::RegisterContextSP GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + + bool CalculateStopInfo() override; + + const char *GetInfo() override { return nullptr; } + + const char *GetName() override; + + const char *GetQueueName() override; + + void WillResume(lldb::StateType resume_state) override; + + void RefreshStateAfterStop() override; + + void ClearStackFrames() override; + +private: + void CheckInterpreterAndScriptObject() const; + lldb::ScriptedThreadInterfaceSP GetInterface() const; + + ScriptedThread(const ScriptedThread &) = delete; + const ScriptedThread &operator=(const ScriptedThread &) = delete; + + std::shared_ptr GetDynamicRegisterInfo(); + + const ScriptedProcess &m_scripted_process; + std::string m_name; + std::string m_queue; + lldb::StopInfoSP m_cached_stop_info_sp = nullptr; + std::shared_ptr m_register_info_sp = nullptr; + lldb_private::StructuredData::ObjectSP m_script_object_sp = nullptr; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SCRIPTED_THREAD_H diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp @@ -0,0 +1,230 @@ +//===-- ScriptedThread.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ScriptedThread.h" + +#include "Plugins/Process/Utility/RegisterContextThreadMemory.h" +#include "lldb/Target/OperatingSystem.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Logging.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +void ScriptedThread::CheckInterpreterAndScriptObject() const { + lldbassert(m_script_object_sp && "Invalid Script Object."); + lldbassert(GetInterface() && "Invalid Scripted Thread Interface."); +} + +ScriptedThread::ScriptedThread(ScriptedProcess &process, Status &error) + : Thread(process, LLDB_INVALID_THREAD_ID), m_scripted_process(process), + m_name(), m_queue() { + + auto error_with_message = [&](llvm::StringRef message) { + error.SetErrorStringWithFormat("ScriptedThread::%s () - ERROR: %s", + __FUNCTION__, message.data()); + }; + + if (!process.IsValid()) { + error_with_message("Invalid scripted process"); + return; + } + + process.CheckInterpreterAndScriptObject(); + + auto scripted_thread_interface = GetInterface(); + if (!scripted_thread_interface) { + error_with_message("Failed to get scripted thread interface."); + return; + } + + llvm::Optional class_name = + process.GetInterface().GetScriptedThreadPluginName(); + if (!class_name || class_name->empty()) { + error_with_message("Failed to get scripted thread class name."); + return; + } + + ExecutionContext exe_ctx(process); + + StructuredData::GenericSP object_sp = + scripted_thread_interface->CreatePluginObject( + class_name->c_str(), exe_ctx, + process.m_scripted_process_info.GetDictionarySP()); + if (!object_sp || !object_sp->IsValid()) { + error_with_message("Failed to create valid script object"); + return; + } + + m_script_object_sp = object_sp; + + SetID(scripted_thread_interface->GetThreadID()); + + llvm::Optional reg_data = + scripted_thread_interface->GetRegisterContext(); + if (!reg_data) { + error_with_message("Failed to get scripted thread registers data."); + return; + } + + DataBufferSP data_sp( + std::make_shared(reg_data->c_str(), reg_data->size())); + + if (!data_sp->GetByteSize()) { + error_with_message("Failed to copy raw registers data."); + return; + } + + std::shared_ptr reg_ctx_memory = + std::make_shared( + *this, 0, *GetDynamicRegisterInfo(), LLDB_INVALID_ADDRESS); + if (!reg_ctx_memory) { + error_with_message("Failed to create a register context."); + return; + } + + reg_ctx_memory->SetAllRegisterData(data_sp); + m_reg_context_sp = reg_ctx_memory; +} + +ScriptedThread::~ScriptedThread() { DestroyThread(); } + +const char *ScriptedThread::GetName() { + if (m_name.empty()) { + CheckInterpreterAndScriptObject(); + llvm::Optional thread_name = GetInterface()->GetName(); + if (!thread_name) + return nullptr; + m_name = *thread_name; + } + return m_name.c_str(); +} + +const char *ScriptedThread::GetQueueName() { + if (m_queue.empty()) { + CheckInterpreterAndScriptObject(); + llvm::Optional queue_name = GetInterface()->GetQueue(); + if (!queue_name) + return nullptr; + m_queue = *queue_name; + } + return m_queue.c_str(); +} + +void ScriptedThread::WillResume(StateType resume_state) {} + +void ScriptedThread::ClearStackFrames() { Thread::ClearStackFrames(); } + +RegisterContextSP ScriptedThread::GetRegisterContext() { + if (!m_reg_context_sp) { + m_reg_context_sp = std::make_shared( + *this, LLDB_INVALID_ADDRESS); + GetInterface()->GetRegisterContext(); + } + return m_reg_context_sp; +} + +RegisterContextSP +ScriptedThread::CreateRegisterContextForFrame(StackFrame *frame) { + uint32_t concrete_frame_idx = 0; + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + if (concrete_frame_idx == 0) + return GetRegisterContext(); + return GetUnwinder().CreateRegisterContextForFrame(frame); +} + +bool ScriptedThread::CalculateStopInfo() { + auto error_with_message = [](llvm::StringRef message) { + LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS), + "ScriptedProcess::%s ERROR = %s", __FUNCTION__, message.data()); + return false; + }; + + if (m_cached_stop_info_sp) { + SetStopInfo(m_cached_stop_info_sp); + } else { + StructuredData::DictionarySP dict_sp = GetInterface()->GetStopReason(); + + lldb::StopReason stop_reason_type; + + if (!dict_sp->GetValueForKeyAsInteger("type", stop_reason_type)) + return error_with_message( + "Couldn't find value for key 'type' in stop reason dictionary."); + + StructuredData::Dictionary *data_dict; + if (!dict_sp->GetValueForKeyAsDictionary("data", data_dict)) + return error_with_message( + "Couldn't find value for key 'type' in stop reason dictionary."); + + switch (stop_reason_type) { + case lldb::eStopReasonNone: + m_cached_stop_info_sp.reset(); + break; + case lldb::eStopReasonBreakpoint: { + lldb::break_id_t break_id; + data_dict->GetValueForKeyAsInteger("break_id", break_id, + LLDB_INVALID_BREAK_ID); + m_cached_stop_info_sp = + StopInfo::CreateStopReasonWithBreakpointSiteID(*this, break_id); + } break; + case lldb::eStopReasonSignal: { + int signal; + llvm::StringRef description; + data_dict->GetValueForKeyAsInteger("signal", signal, + LLDB_INVALID_SIGNAL_NUMBER); + data_dict->GetValueForKeyAsString("desc", description); + m_cached_stop_info_sp = StopInfo::CreateStopReasonWithSignal( + *this, signal, description.data()); + } break; + default: + return error_with_message(llvm::Twine("Unsupported stop reason type (" + + llvm::Twine(stop_reason_type) + + llvm::Twine(").")) + .str()); + } + } + SetStopInfo(m_cached_stop_info_sp); + return true; +} + +void ScriptedThread::RefreshStateAfterStop() { + // TODO: Implement + if (m_reg_context_sp) + m_reg_context_sp->InvalidateAllRegisters(); +} + +lldb::ScriptedThreadInterfaceSP ScriptedThread::GetInterface() const { + return m_scripted_process.GetInterface().GetScriptedThreadInterface(); +} + +std::shared_ptr ScriptedThread::GetDynamicRegisterInfo() { + CheckInterpreterAndScriptObject(); + + if (!m_register_info_sp) { + StructuredData::DictionarySP reg_info = GetInterface()->GetRegisterInfo(); + if (!reg_info) + return nullptr; + + m_register_info_sp = std::make_shared( + *reg_info, m_scripted_process.GetTarget().GetArchitecture()); + assert(m_register_info_sp->GetNumRegisters() > 0); + assert(m_register_info_sp->GetNumRegisterSets() > 0); + } + + return m_register_info_sp; +} diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt --- a/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt +++ b/lldb/source/Plugins/ScriptInterpreter/Python/CMakeLists.txt @@ -13,6 +13,7 @@ ScriptInterpreterPython.cpp ScriptedPythonInterface.cpp ScriptedProcessPythonInterface.cpp + ScriptedThreadPythonInterface.cpp SWIGPythonBridge.cpp LINK_LIBS diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h --- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h @@ -46,6 +46,10 @@ const lldb::TargetSP &target_sp, StructuredDataImpl *args_impl, std::string &error_string); +extern "C" void *LLDBSwigPythonCreateScriptedThread( + const char *python_class_name, const char *session_dictionary_name, + const lldb::TargetSP &target_sp, std::string &error_string); + extern "C" void *LLDBSWIGPython_CastPyObjectToSBData(void *data); extern "C" void *LLDBSWIGPython_CastPyObjectToSBError(void *data); extern "C" void *LLDBSWIGPython_CastPyObjectToSBValue(void *data); 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 @@ -50,6 +50,11 @@ lldb::pid_t GetProcessID() override; bool IsAlive() override; + + llvm::Optional GetScriptedThreadPluginName() override; + +private: + lldb::ScriptedThreadInterfaceSP GetScriptedThreadInterface() override; }; } // namespace lldb_private 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 @@ -19,6 +19,7 @@ #include "SWIGPythonBridge.h" #include "ScriptInterpreterPythonImpl.h" #include "ScriptedProcessPythonInterface.h" +#include "ScriptedThreadPythonInterface.h" using namespace lldb; using namespace lldb_private; @@ -68,15 +69,15 @@ } bool ScriptedProcessPythonInterface::ShouldStop() { - Status error; - StructuredData::ObjectSP obj = Dispatch("is_alive", error); - auto error_with_message = [](llvm::StringRef message) { LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS), "ScriptedProcess::%s ERROR = %s", __FUNCTION__, message.data()); return false; }; + Status error; + StructuredData::ObjectSP obj = Dispatch("is_alive", error); + if (!obj || !obj->IsValid() || error.Fail()) { return error_with_message(llvm::Twine("Null or invalid object (" + llvm::Twine(error.AsCString()) + @@ -100,9 +101,6 @@ StructuredData::DictionarySP ScriptedProcessPythonInterface::GetThreadWithID(lldb::tid_t tid) { - Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, - Locker::FreeLock); - auto error_with_message = [](llvm::StringRef message) { LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS), "ScriptedProcess::%s ERROR = %s", __FUNCTION__, message.data()); @@ -181,4 +179,34 @@ return obj->GetBooleanValue(); } +llvm::Optional +ScriptedProcessPythonInterface::GetScriptedThreadPluginName() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_scripted_thread_plugin", error); + + auto error_with_message = [](llvm::StringRef message) { + LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS), + "ScriptedProcess::%s ERROR = %s", __FUNCTION__, message.data()); + return llvm::None; + }; + + if (!obj || !obj->IsValid() || error.Fail()) { + return error_with_message(llvm::Twine("Null or invalid object (" + + llvm::Twine(error.AsCString()) + + llvm::Twine(").")) + .str()); + } + + return obj->GetStringValue().str(); +} + +lldb::ScriptedThreadInterfaceSP +ScriptedProcessPythonInterface::GetScriptedThreadInterface() { + if (!m_scripted_thread_interface_sp) + m_scripted_thread_interface_sp = + std::make_shared(m_interpreter); + + return m_scripted_thread_interface_sp; +} + #endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedPythonInterface.h --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedPythonInterface.h @@ -33,6 +33,14 @@ return p.CreateStructuredObject(); } + template <> + StructuredData::DictionarySP + ExtractValueFromPythonObject( + python::PythonObject &p, Status &error) { + python::PythonDictionary result_dict(python::PyRefType::Borrowed, p.get()); + return result_dict.CreateStructuredDictionary(); + } + template <> Status ExtractValueFromPythonObject(python::PythonObject &p, Status &error) { @@ -53,7 +61,8 @@ LLDBSWIGPython_CastPyObjectToSBData(p.get())); if (!sb_data) { - error.SetErrorString("Couldn't cast lldb::SBError to lldb::Status."); + error.SetErrorString( + "Couldn't cast lldb::SBData to lldb::DataExtractorSP."); return nullptr; } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.h @@ -0,0 +1,49 @@ +//===-- ScriptedThreadPythonInterface.h ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDTHREADPYTHONINTERFACE_H +#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDTHREADPYTHONINTERFACE_H + +#include "lldb/Host/Config.h" + +#if LLDB_ENABLE_PYTHON + +#include "ScriptedPythonInterface.h" +#include "lldb/Interpreter/ScriptedProcessInterface.h" + +namespace lldb_private { +class ScriptedThreadPythonInterface : public ScriptedThreadInterface, + public ScriptedPythonInterface { +public: + ScriptedThreadPythonInterface(ScriptInterpreterPythonImpl &interpreter); + + StructuredData::GenericSP + CreatePluginObject(const llvm::StringRef class_name, + ExecutionContext &exe_ctx, + StructuredData::DictionarySP args_sp) override; + + lldb::tid_t GetThreadID() override; + + llvm::Optional GetName() override; + + lldb::StateType GetState() override; + + llvm::Optional GetQueue() override; + + StructuredData::DictionarySP GetStopReason() override; + + StructuredData::ArraySP GetStackFrames() override; + + StructuredData::DictionarySP GetRegisterInfo() override; + + llvm::Optional GetRegisterContext() override; +}; +} // namespace lldb_private + +#endif // LLDB_ENABLE_PYTHON +#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPROCESSTHREADINTERFACE_H diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.cpp @@ -0,0 +1,205 @@ +//===-- ScriptedThreadPythonInterface.cpp ---------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Config.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Logging.h" +#include "lldb/lldb-enumerations.h" + +#if LLDB_ENABLE_PYTHON + +// LLDB Python header must be included first +#include "lldb-python.h" + +#include "SWIGPythonBridge.h" +#include "ScriptInterpreterPythonImpl.h" +#include "ScriptedThreadPythonInterface.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::python; +using Locker = ScriptInterpreterPythonImpl::Locker; + +ScriptedThreadPythonInterface::ScriptedThreadPythonInterface( + ScriptInterpreterPythonImpl &interpreter) + : ScriptedThreadInterface(), ScriptedPythonInterface(interpreter) {} + +StructuredData::GenericSP ScriptedThreadPythonInterface::CreatePluginObject( + const llvm::StringRef class_name, ExecutionContext &exe_ctx, + StructuredData::DictionarySP args_sp) { + + if (class_name.empty()) + return {}; + + std::string error_string; + TargetSP target_sp = exe_ctx.GetTargetSP(); + + Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, + Locker::FreeLock); + + void *ret_val = LLDBSwigPythonCreateScriptedThread( + class_name.str().c_str(), m_interpreter.GetDictionaryName(), target_sp, + error_string); + + if (!ret_val) + return {}; + + m_object_instance_sp = + StructuredData::GenericSP(new StructuredPythonObject(ret_val)); + + return m_object_instance_sp; +} + +lldb::tid_t ScriptedThreadPythonInterface::GetThreadID() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_thread_id", error); + + auto error_with_message = [](llvm::StringRef message) { + LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS), + "ScriptedProcess::%s ERROR = %s", __FUNCTION__, message.data()); + return LLDB_INVALID_THREAD_ID; + }; + + if (!obj || !obj->IsValid() || error.Fail()) { + return error_with_message(llvm::Twine("Null or invalid object (" + + llvm::Twine(error.AsCString()) + + llvm::Twine(").")) + .str()); + } + + return obj->GetIntegerValue(LLDB_INVALID_THREAD_ID); +} + +llvm::Optional ScriptedThreadPythonInterface::GetName() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_name", error); + + auto error_with_message = [](llvm::StringRef message) { + LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS), + "ScriptedProcess::%s ERROR = %s", __FUNCTION__, message.data()); + return llvm::None; + }; + + if (!obj || !obj->IsValid() || error.Fail()) { + return error_with_message(llvm::Twine("Null or invalid object (" + + llvm::Twine(error.AsCString()) + + llvm::Twine(").")) + .str()); + } + + return obj->GetStringValue().str(); +} + +lldb::StateType ScriptedThreadPythonInterface::GetState() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_state", error); + + auto error_with_message = [](llvm::StringRef message) { + LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS), + "ScriptedProcess::%s ERROR = %s", __FUNCTION__, message.data()); + return eStateInvalid; + }; + + if (!obj || !obj->IsValid() || error.Fail()) { + return error_with_message(llvm::Twine("Null or invalid object (" + + llvm::Twine(error.AsCString()) + + llvm::Twine(").")) + .str()); + } + + return static_cast(obj->GetIntegerValue(eStateInvalid)); +} + +llvm::Optional ScriptedThreadPythonInterface::GetQueue() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_queue", error); + + auto error_with_message = [](llvm::StringRef message) { + LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS), + "ScriptedProcess::%s ERROR = %s", __FUNCTION__, message.data()); + return llvm::None; + }; + + if (!obj || !obj->IsValid() || error.Fail()) { + return error_with_message(llvm::Twine("Null or invalid object (" + + llvm::Twine(error.AsCString()) + + llvm::Twine(").")) + .str()); + } + + return obj->GetStringValue().str(); +} + +StructuredData::DictionarySP ScriptedThreadPythonInterface::GetStopReason() { + Status error; + StructuredData::DictionarySP dict = + Dispatch("get_stop_reason", error); + + auto error_with_message = [](llvm::StringRef message) { + LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS), + "ScriptedProcess::%s ERROR = %s", __FUNCTION__, message.data()); + return StructuredData::DictionarySP(); + }; + + if (!dict || !dict->IsValid() || error.Fail()) { + return error_with_message(llvm::Twine("Null or invalid object (" + + llvm::Twine(error.AsCString()) + + llvm::Twine(").")) + .str()); + } + + return dict; +} + +StructuredData::ArraySP ScriptedThreadPythonInterface::GetStackFrames() { + return nullptr; +} + +StructuredData::DictionarySP ScriptedThreadPythonInterface::GetRegisterInfo() { + Status error; + StructuredData::DictionarySP dict = + Dispatch("get_register_info", error); + + auto error_with_message = [](llvm::StringRef message) { + LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS), + "ScriptedProcess::%s ERROR = %s", __FUNCTION__, message.data()); + return StructuredData::DictionarySP(); + }; + + if (!dict || !dict->IsValid() || error.Fail()) { + return error_with_message(llvm::Twine("Null or invalid object (" + + llvm::Twine(error.AsCString()) + + llvm::Twine(").")) + .str()); + } + + return dict; +} + +llvm::Optional +ScriptedThreadPythonInterface::GetRegisterContext() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_register_context", error); + + auto error_with_message = [](llvm::StringRef message) { + LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS), + "ScriptedProcess::%s ERROR = %s", __FUNCTION__, message.data()); + return llvm::None; + }; + + if (!obj || !obj->IsValid() || error.Fail()) { + return error_with_message(llvm::Twine("Null or invalid object (" + + llvm::Twine(error.AsCString()) + + llvm::Twine(").")) + .str()); + } + + return obj->GetAsString()->GetValue().str(); +} + +#endif 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 @@ -72,6 +72,28 @@ self.assertTrue(error.Success(), "Failed to read memory from scripted process.") self.assertEqual(hello_world, memory_read) + self.assertEqual(process.GetNumThreads(), 1) + + thread = process.GetSelectedThread() + self.assertTrue(thread, "Invalid thread.") + self.assertEqual(thread.GetThreadID(), 0x19) + self.assertEqual(thread.GetName(), "MyScriptedThread.thread-1") + self.assertEqual(thread.GetStopReason(), lldb.eStopReasonSignal) + + self.assertGreater(thread.GetNumFrames(), 0) + + frame = thread.GetFrameAtIndex(0) + register_set = frame.registers # Returns an SBValueList. + for regs in register_set: + if 'GPR' in regs.name: + registers = regs + break + + self.assertTrue(registers, "Invalid General Purpose Registers Set") + self.assertEqual(registers.GetNumChildren(), 21) + for idx, reg in enumerate(registers, start=1): + self.assertEqual(idx, int(reg.value, 16)) + def test_launch_scripted_process_cli(self): """Test that we can launch an lldb scripted process from the command line, check its process ID and read string from memory.""" diff --git a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py @@ -0,0 +1,90 @@ +import os,struct, signal + +from typing import Any, Dict + +import lldb +from lldb.plugins.scripted_process import ScriptedProcess +from lldb.plugins.scripted_process import ScriptedThread + +class DummyScriptedProcess(ScriptedProcess): + def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData): + super().__init__(target, args) + + def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo: + return self.memory_regions[0] + + def get_thread_with_id(self, tid: int): + return {} + + def get_registers_for_thread(self, tid: int): + return {} + + def read_memory_at_address(self, addr: int, size: int) -> lldb.SBData: + data = lldb.SBData().CreateDataFromCString( + self.target.GetByteOrder(), + self.target.GetCodeByteSize(), + "Hello, world!") + return data + + def get_loaded_images(self): + return self.loaded_images + + def get_process_id(self) -> int: + return 42 + + def should_stop(self) -> bool: + return True + + def is_alive(self) -> bool: + return True + + def get_scripted_thread_plugin(self): + return DummyScriptedThread.__module__ + "." + DummyScriptedThread.__name__ + + +class DummyScriptedThread(ScriptedThread): + def __init__(self, target): + super().__init__(target) + + def get_thread_id(self) -> int: + return 0x19 + + def get_name(self) -> str: + return DummyScriptedThread.__name__ + ".thread-1" + + def get_state(self) -> int: + return lldb.eStateStopped + + def get_stop_reason(self) -> Dict[str, Any]: + return { "type": lldb.eStopReasonSignal, "data": { + "signal": signal.SIGINT + } } + + def get_stackframes(self): + class ScriptedStackFrame: + def __init__(idx, cfa, pc, symbol_ctx): + self.idx = idx + self.cfa = cfa + self.pc = pc + self.symbol_ctx = symbol_ctx + + + symbol_ctx = lldb.SBSymbolContext() + frame_zero = ScriptedStackFrame(0, 0x42424242, 0x5000000, symbol_ctx) + self.frames.append(frame_zero) + + return self.frame_zero[0:0] + + def get_register_context(self) -> str: + return struct.pack( + '21Q', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21) + + +def __lldb_init_module(debugger, dict): + if not 'SKIP_SCRIPTED_PROCESS_LAUNCH' in os.environ: + debugger.HandleCommand( + "process launch -C %s.%s" % (__name__, + DummyScriptedProcess.__name__)) + else: + print("Name of the class that will manage the scripted process: '%s.%s'" + % (__name__, DummyScriptedProcess.__name__)) \ No newline at end of file diff --git a/lldb/test/API/functionalities/scripted_process/main.c b/lldb/test/API/functionalities/scripted_process/main.c --- a/lldb/test/API/functionalities/scripted_process/main.c +++ b/lldb/test/API/functionalities/scripted_process/main.c @@ -1,5 +1,8 @@ -#include - -int main() { - return 0; // break here +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/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp --- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -223,6 +223,12 @@ return nullptr; } +extern "C" void *LLDBSwigPythonCreateScriptedThread( + const char *python_class_name, const char *session_dictionary_name, + const lldb::TargetSP &target_sp, std::string &error_string) { + return nullptr; +} + extern "C" void * LLDBSWIGPython_CreateFrameRecognizer(const char *python_class_name, const char *session_dictionary_name) {