Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
39642 views
//===-- ScriptedProcess.cpp -----------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "ScriptedProcess.h"910#include "lldb/Core/Debugger.h"11#include "lldb/Core/Module.h"12#include "lldb/Core/PluginManager.h"1314#include "lldb/Host/OptionParser.h"15#include "lldb/Host/ThreadLauncher.h"16#include "lldb/Interpreter/CommandInterpreter.h"17#include "lldb/Interpreter/OptionArgParser.h"18#include "lldb/Interpreter/OptionGroupBoolean.h"19#include "lldb/Interpreter/ScriptInterpreter.h"20#include "lldb/Target/MemoryRegionInfo.h"21#include "lldb/Target/Queue.h"22#include "lldb/Target/RegisterContext.h"23#include "lldb/Utility/LLDBLog.h"24#include "lldb/Utility/ScriptedMetadata.h"25#include "lldb/Utility/State.h"2627#include <mutex>2829LLDB_PLUGIN_DEFINE(ScriptedProcess)3031using namespace lldb;32using namespace lldb_private;3334llvm::StringRef ScriptedProcess::GetPluginDescriptionStatic() {35return "Scripted Process plug-in.";36}3738static constexpr lldb::ScriptLanguage g_supported_script_languages[] = {39ScriptLanguage::eScriptLanguagePython,40};4142bool ScriptedProcess::IsScriptLanguageSupported(lldb::ScriptLanguage language) {43llvm::ArrayRef<lldb::ScriptLanguage> supported_languages =44llvm::ArrayRef(g_supported_script_languages);4546return llvm::is_contained(supported_languages, language);47}4849lldb::ProcessSP ScriptedProcess::CreateInstance(lldb::TargetSP target_sp,50lldb::ListenerSP listener_sp,51const FileSpec *file,52bool can_connect) {53if (!target_sp ||54!IsScriptLanguageSupported(target_sp->GetDebugger().GetScriptLanguage()))55return nullptr;5657ScriptedMetadata scripted_metadata(target_sp->GetProcessLaunchInfo());5859Status error;60auto process_sp = std::shared_ptr<ScriptedProcess>(61new ScriptedProcess(target_sp, listener_sp, scripted_metadata, error));6263if (error.Fail() || !process_sp || !process_sp->m_interface_up) {64LLDB_LOGF(GetLog(LLDBLog::Process), "%s", error.AsCString());65return nullptr;66}6768return process_sp;69}7071bool ScriptedProcess::CanDebug(lldb::TargetSP target_sp,72bool plugin_specified_by_name) {73return true;74}7576ScriptedProcess::ScriptedProcess(lldb::TargetSP target_sp,77lldb::ListenerSP listener_sp,78const ScriptedMetadata &scripted_metadata,79Status &error)80: Process(target_sp, listener_sp), m_scripted_metadata(scripted_metadata) {8182if (!target_sp) {83error.SetErrorStringWithFormat("ScriptedProcess::%s () - ERROR: %s",84__FUNCTION__, "Invalid target");85return;86}8788ScriptInterpreter *interpreter =89target_sp->GetDebugger().GetScriptInterpreter();9091if (!interpreter) {92error.SetErrorStringWithFormat("ScriptedProcess::%s () - ERROR: %s",93__FUNCTION__,94"Debugger has no Script Interpreter");95return;96}9798// Create process instance interface99m_interface_up = interpreter->CreateScriptedProcessInterface();100if (!m_interface_up) {101error.SetErrorStringWithFormat(102"ScriptedProcess::%s () - ERROR: %s", __FUNCTION__,103"Script interpreter couldn't create Scripted Process Interface");104return;105}106107ExecutionContext exe_ctx(target_sp, /*get_process=*/false);108109// Create process script object110auto obj_or_err = GetInterface().CreatePluginObject(111m_scripted_metadata.GetClassName(), exe_ctx,112m_scripted_metadata.GetArgsSP());113114if (!obj_or_err) {115llvm::consumeError(obj_or_err.takeError());116error.SetErrorString("Failed to create script object.");117return;118}119120StructuredData::GenericSP object_sp = *obj_or_err;121122if (!object_sp || !object_sp->IsValid()) {123error.SetErrorStringWithFormat("ScriptedProcess::%s () - ERROR: %s",124__FUNCTION__,125"Failed to create valid script object");126return;127}128}129130ScriptedProcess::~ScriptedProcess() {131Clear();132// If the interface is not valid, we can't call Finalize(). When that happens133// it means that the Scripted Process instanciation failed and the134// CreateProcess function returns a nullptr, so no one besides this class135// should have access to that bogus process object.136if (!m_interface_up)137return;138// We need to call finalize on the process before destroying ourselves to139// make sure all of the broadcaster cleanup goes as planned. If we destruct140// this class, then Process::~Process() might have problems trying to fully141// destroy the broadcaster.142Finalize(true /* destructing */);143}144145void ScriptedProcess::Initialize() {146static llvm::once_flag g_once_flag;147148llvm::call_once(g_once_flag, []() {149PluginManager::RegisterPlugin(GetPluginNameStatic(),150GetPluginDescriptionStatic(), CreateInstance);151});152}153154void ScriptedProcess::Terminate() {155PluginManager::UnregisterPlugin(ScriptedProcess::CreateInstance);156}157158Status ScriptedProcess::DoLoadCore() {159ProcessLaunchInfo launch_info = GetTarget().GetProcessLaunchInfo();160161return DoLaunch(nullptr, launch_info);162}163164Status ScriptedProcess::DoLaunch(Module *exe_module,165ProcessLaunchInfo &launch_info) {166LLDB_LOGF(GetLog(LLDBLog::Process), "ScriptedProcess::%s launching process", __FUNCTION__);167168/* MARK: This doesn't reflect how lldb actually launches a process.169In reality, it attaches to debugserver, then resume the process.170That's not true in all cases. If debugserver is remote, lldb171asks debugserver to launch the process for it. */172Status error = GetInterface().Launch();173SetPrivateState(eStateStopped);174return error;175}176177void ScriptedProcess::DidLaunch() { m_pid = GetInterface().GetProcessID(); }178179void ScriptedProcess::DidResume() {180// Update the PID again, in case the user provided a placeholder pid at launch181m_pid = GetInterface().GetProcessID();182}183184Status ScriptedProcess::DoResume() {185LLDB_LOGF(GetLog(LLDBLog::Process), "ScriptedProcess::%s resuming process", __FUNCTION__);186187return GetInterface().Resume();188}189190Status ScriptedProcess::DoAttach(const ProcessAttachInfo &attach_info) {191Status error = GetInterface().Attach(attach_info);192SetPrivateState(eStateRunning);193SetPrivateState(eStateStopped);194if (error.Fail())195return error;196// NOTE: We need to set the PID before finishing to attach otherwise we will197// hit an assert when calling the attach completion handler.198DidLaunch();199200return {};201}202203Status204ScriptedProcess::DoAttachToProcessWithID(lldb::pid_t pid,205const ProcessAttachInfo &attach_info) {206return DoAttach(attach_info);207}208209Status ScriptedProcess::DoAttachToProcessWithName(210const char *process_name, const ProcessAttachInfo &attach_info) {211return DoAttach(attach_info);212}213214void ScriptedProcess::DidAttach(ArchSpec &process_arch) {215process_arch = GetArchitecture();216}217218Status ScriptedProcess::DoDestroy() { return Status(); }219220bool ScriptedProcess::IsAlive() { return GetInterface().IsAlive(); }221222size_t ScriptedProcess::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,223Status &error) {224lldb::DataExtractorSP data_extractor_sp =225GetInterface().ReadMemoryAtAddress(addr, size, error);226227if (!data_extractor_sp || !data_extractor_sp->GetByteSize() || error.Fail())228return 0;229230offset_t bytes_copied = data_extractor_sp->CopyByteOrderedData(2310, data_extractor_sp->GetByteSize(), buf, size, GetByteOrder());232233if (!bytes_copied || bytes_copied == LLDB_INVALID_OFFSET)234return ScriptedInterface::ErrorWithMessage<size_t>(235LLVM_PRETTY_FUNCTION, "Failed to copy read memory to buffer.", error);236237// FIXME: We should use the diagnostic system to report a warning if the238// `bytes_copied` is different from `size`.239240return bytes_copied;241}242243size_t ScriptedProcess::DoWriteMemory(lldb::addr_t vm_addr, const void *buf,244size_t size, Status &error) {245lldb::DataExtractorSP data_extractor_sp = std::make_shared<DataExtractor>(246buf, size, GetByteOrder(), GetAddressByteSize());247248if (!data_extractor_sp || !data_extractor_sp->GetByteSize())249return 0;250251lldb::offset_t bytes_written =252GetInterface().WriteMemoryAtAddress(vm_addr, data_extractor_sp, error);253254if (!bytes_written || bytes_written == LLDB_INVALID_OFFSET)255return ScriptedInterface::ErrorWithMessage<size_t>(256LLVM_PRETTY_FUNCTION, "Failed to copy write buffer to memory.", error);257258// FIXME: We should use the diagnostic system to report a warning if the259// `bytes_written` is different from `size`.260261return bytes_written;262}263264Status ScriptedProcess::EnableBreakpointSite(BreakpointSite *bp_site) {265assert(bp_site != nullptr);266267if (bp_site->IsEnabled()) {268return {};269}270271if (bp_site->HardwareRequired()) {272return Status("Scripted Processes don't support hardware breakpoints");273}274275Status error;276GetInterface().CreateBreakpoint(bp_site->GetLoadAddress(), error);277278return error;279}280281ArchSpec ScriptedProcess::GetArchitecture() {282return GetTarget().GetArchitecture();283}284285Status ScriptedProcess::DoGetMemoryRegionInfo(lldb::addr_t load_addr,286MemoryRegionInfo ®ion) {287Status error;288if (auto region_or_err =289GetInterface().GetMemoryRegionContainingAddress(load_addr, error))290region = *region_or_err;291292return error;293}294295Status ScriptedProcess::GetMemoryRegions(MemoryRegionInfos ®ion_list) {296Status error;297lldb::addr_t address = 0;298299while (auto region_or_err =300GetInterface().GetMemoryRegionContainingAddress(address, error)) {301if (error.Fail())302break;303304MemoryRegionInfo &mem_region = *region_or_err;305auto range = mem_region.GetRange();306address += range.GetRangeBase() + range.GetByteSize();307region_list.push_back(mem_region);308}309310return error;311}312313void ScriptedProcess::Clear() { Process::m_thread_list.Clear(); }314315bool ScriptedProcess::DoUpdateThreadList(ThreadList &old_thread_list,316ThreadList &new_thread_list) {317// TODO: Implement318// This is supposed to get the current set of threads, if any of them are in319// old_thread_list then they get copied to new_thread_list, and then any320// actually new threads will get added to new_thread_list.321m_thread_plans.ClearThreadCache();322323Status error;324StructuredData::DictionarySP thread_info_sp = GetInterface().GetThreadsInfo();325326if (!thread_info_sp)327return ScriptedInterface::ErrorWithMessage<bool>(328LLVM_PRETTY_FUNCTION,329"Couldn't fetch thread list from Scripted Process.", error);330331// Because `StructuredData::Dictionary` uses a `std::map<ConstString,332// ObjectSP>` for storage, each item is sorted based on the key alphabetical333// order. Since `GetThreadsInfo` provides thread indices as the key element,334// thread info comes ordered alphabetically, instead of numerically, so we335// need to sort the thread indices before creating thread.336337StructuredData::ArraySP keys = thread_info_sp->GetKeys();338339std::map<size_t, StructuredData::ObjectSP> sorted_threads;340auto sort_keys = [&sorted_threads,341&thread_info_sp](StructuredData::Object *item) -> bool {342if (!item)343return false;344345llvm::StringRef key = item->GetStringValue();346size_t idx = 0;347348// Make sure the provided index is actually an integer349if (!llvm::to_integer(key, idx))350return false;351352sorted_threads[idx] = thread_info_sp->GetValueForKey(key);353return true;354};355356size_t thread_count = thread_info_sp->GetSize();357358if (!keys->ForEach(sort_keys) || sorted_threads.size() != thread_count)359// Might be worth showing the unsorted thread list instead of return early.360return ScriptedInterface::ErrorWithMessage<bool>(361LLVM_PRETTY_FUNCTION, "Couldn't sort thread list.", error);362363auto create_scripted_thread =364[this, &error, &new_thread_list](365const std::pair<size_t, StructuredData::ObjectSP> pair) -> bool {366size_t idx = pair.first;367StructuredData::ObjectSP object_sp = pair.second;368369if (!object_sp)370return ScriptedInterface::ErrorWithMessage<bool>(371LLVM_PRETTY_FUNCTION, "Invalid thread info object", error);372373auto thread_or_error =374ScriptedThread::Create(*this, object_sp->GetAsGeneric());375376if (!thread_or_error)377return ScriptedInterface::ErrorWithMessage<bool>(378LLVM_PRETTY_FUNCTION, toString(thread_or_error.takeError()), error);379380ThreadSP thread_sp = thread_or_error.get();381lldbassert(thread_sp && "Couldn't initialize scripted thread.");382383RegisterContextSP reg_ctx_sp = thread_sp->GetRegisterContext();384if (!reg_ctx_sp)385return ScriptedInterface::ErrorWithMessage<bool>(386LLVM_PRETTY_FUNCTION,387llvm::Twine("Invalid Register Context for thread " + llvm::Twine(idx))388.str(),389error);390391new_thread_list.AddThread(thread_sp);392393return true;394};395396llvm::for_each(sorted_threads, create_scripted_thread);397398return new_thread_list.GetSize(false) > 0;399}400401void ScriptedProcess::RefreshStateAfterStop() {402// Let all threads recover from stopping and do any clean up based on the403// previous thread state (if any).404m_thread_list.RefreshStateAfterStop();405}406407bool ScriptedProcess::GetProcessInfo(ProcessInstanceInfo &info) {408info.Clear();409info.SetProcessID(GetID());410info.SetArchitecture(GetArchitecture());411lldb::ModuleSP module_sp = GetTarget().GetExecutableModule();412if (module_sp) {413const bool add_exe_file_as_first_arg = false;414info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(),415add_exe_file_as_first_arg);416}417return true;418}419420lldb_private::StructuredData::ObjectSP421ScriptedProcess::GetLoadedDynamicLibrariesInfos() {422Status error;423auto error_with_message = [&error](llvm::StringRef message) {424return ScriptedInterface::ErrorWithMessage<bool>(LLVM_PRETTY_FUNCTION,425message.data(), error);426};427428StructuredData::ArraySP loaded_images_sp = GetInterface().GetLoadedImages();429430if (!loaded_images_sp || !loaded_images_sp->GetSize())431return ScriptedInterface::ErrorWithMessage<StructuredData::ObjectSP>(432LLVM_PRETTY_FUNCTION, "No loaded images.", error);433434ModuleList module_list;435Target &target = GetTarget();436437auto reload_image = [&target, &module_list, &error_with_message](438StructuredData::Object *obj) -> bool {439StructuredData::Dictionary *dict = obj->GetAsDictionary();440441if (!dict)442return error_with_message("Couldn't cast image object into dictionary.");443444ModuleSpec module_spec;445llvm::StringRef value;446447bool has_path = dict->HasKey("path");448bool has_uuid = dict->HasKey("uuid");449if (!has_path && !has_uuid)450return error_with_message("Dictionary should have key 'path' or 'uuid'");451if (!dict->HasKey("load_addr"))452return error_with_message("Dictionary is missing key 'load_addr'");453454if (has_path) {455dict->GetValueForKeyAsString("path", value);456module_spec.GetFileSpec().SetPath(value);457}458459if (has_uuid) {460dict->GetValueForKeyAsString("uuid", value);461module_spec.GetUUID().SetFromStringRef(value);462}463module_spec.GetArchitecture() = target.GetArchitecture();464465ModuleSP module_sp =466target.GetOrCreateModule(module_spec, true /* notify */);467468if (!module_sp)469return error_with_message("Couldn't create or get module.");470471lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;472lldb::offset_t slide = LLDB_INVALID_OFFSET;473dict->GetValueForKeyAsInteger("load_addr", load_addr);474dict->GetValueForKeyAsInteger("slide", slide);475if (load_addr == LLDB_INVALID_ADDRESS)476return error_with_message(477"Couldn't get valid load address or slide offset.");478479if (slide != LLDB_INVALID_OFFSET)480load_addr += slide;481482bool changed = false;483module_sp->SetLoadAddress(target, load_addr, false /*=value_is_offset*/,484changed);485486if (!changed && !module_sp->GetObjectFile())487return error_with_message("Couldn't set the load address for module.");488489dict->GetValueForKeyAsString("path", value);490FileSpec objfile(value);491module_sp->SetFileSpecAndObjectName(objfile, objfile.GetFilename());492493return module_list.AppendIfNeeded(module_sp);494};495496if (!loaded_images_sp->ForEach(reload_image))497return ScriptedInterface::ErrorWithMessage<StructuredData::ObjectSP>(498LLVM_PRETTY_FUNCTION, "Couldn't reload all images.", error);499500target.ModulesDidLoad(module_list);501502return loaded_images_sp;503}504505lldb_private::StructuredData::DictionarySP ScriptedProcess::GetMetadata() {506StructuredData::DictionarySP metadata_sp = GetInterface().GetMetadata();507508Status error;509if (!metadata_sp || !metadata_sp->GetSize())510return ScriptedInterface::ErrorWithMessage<StructuredData::DictionarySP>(511LLVM_PRETTY_FUNCTION, "No metadata.", error);512513return metadata_sp;514}515516void ScriptedProcess::UpdateQueueListIfNeeded() {517CheckScriptedInterface();518for (ThreadSP thread_sp : Threads()) {519if (const char *queue_name = thread_sp->GetQueueName()) {520QueueSP queue_sp = std::make_shared<Queue>(521m_process->shared_from_this(), thread_sp->GetQueueID(), queue_name);522m_queue_list.AddQueue(queue_sp);523}524}525}526527ScriptedProcessInterface &ScriptedProcess::GetInterface() const {528CheckScriptedInterface();529return *m_interface_up;530}531532void *ScriptedProcess::GetImplementation() {533StructuredData::GenericSP object_instance_sp =534GetInterface().GetScriptObjectInstance();535if (object_instance_sp &&536object_instance_sp->GetType() == eStructuredDataTypeGeneric)537return object_instance_sp->GetAsGeneric()->GetValue();538return nullptr;539}540541542