mib created this revision. mib added reviewers: jasonmolenda, jingham, JDevlieghere, labath, LLDB. mib added a project: LLDB. Herald added a subscriber: mgorny. mib requested review of this revision. Herald added a subscriber: lldb-commits.
This patch introduces Scripted Processes to lldb. The goal, here, is to be able to attach to fake processes in the debugger that are backed my scripts (Python, Lua, Swift, etc ...) and inspect them statically. Scripted Processes can be used in cooperative multithreading environments like the XNU Kernel or other real-time operating systems, but it can also help us improve the debugger testing infrastructure by writting synthetic tests that simulates hard-to-reproduce process/thread states. Although ScriptedProcess is not feature-complete at the moment, it has basic execution capabilities and will improve in the following patches. rdar://65508855 Signed-off-by: Med Ismail Bennani <medismail.benn...@gmail.com> Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D95713 Files: lldb/source/Plugins/Process/CMakeLists.txt lldb/source/Plugins/Process/Scripted/CMakeLists.txt lldb/source/Plugins/Process/Scripted/ScriptedProcess.cpp lldb/source/Plugins/Process/Scripted/ScriptedProcess.h lldb/source/Target/Target.cpp
Index: lldb/source/Target/Target.cpp =================================================================== --- lldb/source/Target/Target.cpp +++ lldb/source/Target/Target.cpp @@ -2950,7 +2950,7 @@ // If we're not already connected to the process, and if we have a platform // that can launch a process for debugging, go ahead and do that here. if (state != eStateConnected && platform_sp && - platform_sp->CanDebugProcess()) { + platform_sp->CanDebugProcess() && !launch_info.IsScriptedProcess()) { LLDB_LOGF(log, "Target::%s asking the platform to debug the process", __FUNCTION__); Index: lldb/source/Plugins/Process/Scripted/ScriptedProcess.h =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Scripted/ScriptedProcess.h @@ -0,0 +1,107 @@ +//===-- ScriptedProcess.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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SCRIPTED_PROCESS_H +#define LLDB_SOURCE_PLUGINS_SCRIPTED_PROCESS_H + +#include "lldb/Target/Process.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Status.h" + +namespace lldb_private { + +class ScriptedProcess : public Process { +protected: + class LaunchInfo { + public: + LaunchInfo(const ProcessLaunchInfo &launch_info) { + m_class_name = launch_info.GetScriptedProcessClassName(); + m_dictionary_sp = launch_info.GetScriptedProcessDictionarySP(); + } + + llvm::StringRef GetClassName() const { return m_class_name; } + StructuredData::DictionarySP GetDictionarySP() const { + return m_dictionary_sp; + } + + private: + llvm::StringRef m_class_name; + StructuredData::DictionarySP m_dictionary_sp; + }; + +public: + static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file_path, + bool can_connect); + + static void Initialize(); + + static void Terminate(); + + static ConstString GetPluginNameStatic(); + + static const char *GetPluginDescriptionStatic(); + + ScriptedProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, + const ScriptedProcess::LaunchInfo &launch_info); + + ~ScriptedProcess() override; + + bool CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + + DynamicLoader *GetDynamicLoader() override { return nullptr; } + + ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override; + + SystemRuntime *GetSystemRuntime() override { return nullptr; } + + Status DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) override; + + Status DoResume() override; + + Status DoDestroy() override; + + void RefreshStateAfterStop() override {}; + + bool IsAlive() override; + + size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) override; + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) override; + + ArchSpec GetArchitecture(); + + Status GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) override; + + Status + GetMemoryRegions(lldb_private::MemoryRegionInfos ®ion_list) override; + + bool GetProcessInfo(ProcessInstanceInfo &info) override; + +protected: + void Clear(); + + bool DoUpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) override; + +private: + const LaunchInfo &m_launch_info; + lldb_private::ScriptInterpreter *m_interpreter; + lldb_private::StructuredData::ObjectSP m_python_object_sp; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SCRIPTED_PROCESS_H Index: lldb/source/Plugins/Process/Scripted/ScriptedProcess.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Scripted/ScriptedProcess.cpp @@ -0,0 +1,195 @@ +//===-- ScriptedProcess.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 "ScriptedProcess.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" + +#include "lldb/Host/OptionParser.h" + +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionGroupBoolean.h" +#include "lldb/Interpreter/ScriptInterpreter.h" + +#include "lldb/Target/MemoryRegionInfo.h" + +LLDB_PLUGIN_DEFINE(ScriptedProcess) + +using namespace lldb; +using namespace lldb_private; + +ConstString ScriptedProcess::GetPluginNameStatic() { + static ConstString g_name("ScriptedProcess"); + return g_name; +} + +const char *ScriptedProcess::GetPluginDescriptionStatic() { + return "Scripted Process plug-in."; +} + +lldb::ProcessSP ScriptedProcess::CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *file, + bool can_connect) { + LaunchInfo launch_info(target_sp->GetProcessLaunchInfo()); + + return std::make_shared<ScriptedProcess>(target_sp, listener_sp, launch_info); +} + +bool ScriptedProcess::CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) { + return true; +} + +ScriptedProcess::ScriptedProcess(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const ScriptedProcess::LaunchInfo &launch_info) + : Process(target_sp, listener_sp), m_launch_info(launch_info), + m_interpreter(nullptr), m_python_object_sp(nullptr) { + if (!target_sp) + return; + + m_interpreter = target_sp->GetDebugger().GetScriptInterpreter(); + + if (!m_interpreter) + return; + + StructuredData::ObjectSP object_sp = + m_interpreter->ScriptedProcess_CreatePluginObject( + m_launch_info.GetClassName().str().c_str(), target_sp, + m_launch_info.GetDictionarySP()); + + if (object_sp && object_sp->IsValid()) + m_python_object_sp = object_sp; +} + +ScriptedProcess::~ScriptedProcess() { + Clear(); + // We need to call finalize on the process before destroying ourselves to + // make sure all of the broadcaster cleanup goes as planned. If we destruct + // this class, then Process::~Process() might have problems trying to fully + // destroy the broadcaster. + Finalize(); +} + +void ScriptedProcess::Initialize() { + static llvm::once_flag g_once_flag; + + llvm::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); + }); +} + +void ScriptedProcess::Terminate() { + PluginManager::UnregisterPlugin(ScriptedProcess::CreateInstance); +} + +ConstString ScriptedProcess::GetPluginName() { return GetPluginNameStatic(); } + +uint32_t ScriptedProcess::GetPluginVersion() { return 1; } + +Status ScriptedProcess::DoLaunch(Module *exe_module, + ProcessLaunchInfo &launch_info) { + if (!m_interpreter) + return Status("No interpreter."); + + if (!m_python_object_sp) + return Status("No python object."); + + Status status = m_interpreter->ScriptedProcess_Launch(m_python_object_sp); + + if (status.Success()) + this->SetPrivateState(eStateStopped); + + return status; +}; + +Status ScriptedProcess::DoResume() { return Status(); } + +Status ScriptedProcess::DoDestroy() { return Status(); } + +bool ScriptedProcess::IsAlive() { return true; } + +size_t ScriptedProcess::ReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) { + // Don't allow the caching that lldb_private::Process::ReadMemory does since + // we have it all cached in our dump file anyway. + + return DoReadMemory(addr, buf, size, error); +} + +size_t ScriptedProcess::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) { + + m_interpreter->ScriptedProcess_ReadMemoryAtAddress(m_python_object_sp, addr, + size); + return size; +} + +ArchSpec ScriptedProcess::GetArchitecture() { + return GetTarget().GetArchitecture(); +} + +Status ScriptedProcess::GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo ®ion) { + // BuildMemoryRegions(); + // region = MinidumpParser::GetMemoryRegionInfo(*m_memory_regions, + // load_addr); + return Status(); +} + +Status ScriptedProcess::GetMemoryRegions(MemoryRegionInfos ®ion_list) { + Status error; + auto size = + m_interpreter->ScriptedProcess_GetNumMemoryRegions(m_python_object_sp); + + if (size == LLDB_INVALID_ADDRESS) { + error.SetErrorString("ScriptedProcess: Invalid number of memory region!"); + return error; + } + + for (uint64_t i = 0; i < size; i++) { + // FIXME: Update interface method to handle extra arg (index) + MemoryRegionInfoSP mem_region_sp = + m_interpreter->ScriptedProcess_GetMemoryRegionAtIndex( + m_python_object_sp, i); + + if (!mem_region_sp) { + // FIXME: Interpolate index in error string + error.SetErrorString( + "ScriptedProcess: Couldn't fetch memory region at index BLA"); + return error; + } + region_list.push_back(*mem_region_sp.get()); + } + + return error; +} + +void ScriptedProcess::Clear() { Process::m_thread_list.Clear(); } + +bool ScriptedProcess::DoUpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) { + return new_thread_list.GetSize(false) > 0; +} + +bool ScriptedProcess::GetProcessInfo(ProcessInstanceInfo &info) { + info.Clear(); + info.SetProcessID(GetID()); + info.SetArchitecture(GetArchitecture()); + lldb::ModuleSP module_sp = GetTarget().GetExecutableModule(); + if (module_sp) { + const bool add_exe_file_as_first_arg = false; + info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(), + add_exe_file_as_first_arg); + } + return true; +} Index: lldb/source/Plugins/Process/Scripted/CMakeLists.txt =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Scripted/CMakeLists.txt @@ -0,0 +1,13 @@ +add_lldb_library(lldbPluginScriptedProcess PLUGIN + ScriptedProcess.cpp + + LINK_LIBS + lldbCore + lldbTarget + lldbUtility + lldbPluginProcessUtility + LINK_COMPONENTS + BinaryFormat + Object + Support + ) \ No newline at end of file Index: lldb/source/Plugins/Process/CMakeLists.txt =================================================================== --- lldb/source/Plugins/Process/CMakeLists.txt +++ lldb/source/Plugins/Process/CMakeLists.txt @@ -13,6 +13,7 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") add_subdirectory(MacOSX-Kernel) endif() +add_subdirectory(Scripted) add_subdirectory(gdb-remote) add_subdirectory(Utility) add_subdirectory(elf-core)
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits