574 lines · cpp
1//===-- ScriptedProcess.cpp -----------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "ScriptedProcess.h"10 11#include "lldb/Core/Debugger.h"12#include "lldb/Core/Module.h"13#include "lldb/Core/PluginManager.h"14 15#include "lldb/Host/OptionParser.h"16#include "lldb/Host/ThreadLauncher.h"17#include "lldb/Interpreter/CommandInterpreter.h"18#include "lldb/Interpreter/OptionArgParser.h"19#include "lldb/Interpreter/OptionGroupBoolean.h"20#include "lldb/Interpreter/ScriptInterpreter.h"21#include "lldb/Target/MemoryRegionInfo.h"22#include "lldb/Target/Queue.h"23#include "lldb/Target/RegisterContext.h"24#include "lldb/Utility/LLDBLog.h"25#include "lldb/Utility/ScriptedMetadata.h"26#include "lldb/Utility/State.h"27 28#include "Plugins/ObjectFile/Placeholder/ObjectFilePlaceholder.h"29 30#include <mutex>31 32LLDB_PLUGIN_DEFINE(ScriptedProcess)33 34using namespace lldb;35using namespace lldb_private;36 37llvm::StringRef ScriptedProcess::GetPluginDescriptionStatic() {38 return "Scripted Process plug-in.";39}40 41static constexpr lldb::ScriptLanguage g_supported_script_languages[] = {42 ScriptLanguage::eScriptLanguagePython,43};44 45bool ScriptedProcess::IsScriptLanguageSupported(lldb::ScriptLanguage language) {46 llvm::ArrayRef<lldb::ScriptLanguage> supported_languages =47 llvm::ArrayRef(g_supported_script_languages);48 49 return llvm::is_contained(supported_languages, language);50}51 52lldb::ProcessSP ScriptedProcess::CreateInstance(lldb::TargetSP target_sp,53 lldb::ListenerSP listener_sp,54 const FileSpec *file,55 bool can_connect) {56 if (!target_sp ||57 !IsScriptLanguageSupported(target_sp->GetDebugger().GetScriptLanguage()))58 return nullptr;59 60 ScriptedMetadata scripted_metadata(target_sp->GetProcessLaunchInfo());61 62 Status error;63 auto process_sp = std::shared_ptr<ScriptedProcess>(64 new ScriptedProcess(target_sp, listener_sp, scripted_metadata, error));65 66 if (error.Fail() || !process_sp || !process_sp->m_interface_up) {67 LLDB_LOGF(GetLog(LLDBLog::Process), "%s", error.AsCString());68 return nullptr;69 }70 71 return process_sp;72}73 74bool ScriptedProcess::CanDebug(lldb::TargetSP target_sp,75 bool plugin_specified_by_name) {76 return true;77}78 79ScriptedProcess::ScriptedProcess(lldb::TargetSP target_sp,80 lldb::ListenerSP listener_sp,81 const ScriptedMetadata &scripted_metadata,82 Status &error)83 : Process(target_sp, listener_sp), m_scripted_metadata(scripted_metadata) {84 85 if (!target_sp) {86 error = Status::FromErrorStringWithFormat(87 "ScriptedProcess::%s () - ERROR: %s", __FUNCTION__, "Invalid target");88 return;89 }90 91 ScriptInterpreter *interpreter =92 target_sp->GetDebugger().GetScriptInterpreter();93 94 if (!interpreter) {95 error = Status::FromErrorStringWithFormat(96 "ScriptedProcess::%s () - ERROR: %s", __FUNCTION__,97 "Debugger has no Script Interpreter");98 return;99 }100 101 // Create process instance interface102 m_interface_up = interpreter->CreateScriptedProcessInterface();103 if (!m_interface_up) {104 error = Status::FromErrorStringWithFormat(105 "ScriptedProcess::%s () - ERROR: %s", __FUNCTION__,106 "Script interpreter couldn't create Scripted Process Interface");107 return;108 }109 110 ExecutionContext exe_ctx(target_sp, /*get_process=*/false);111 112 // Create process script object113 auto obj_or_err = GetInterface().CreatePluginObject(114 m_scripted_metadata.GetClassName(), exe_ctx,115 m_scripted_metadata.GetArgsSP());116 117 if (!obj_or_err) {118 llvm::consumeError(obj_or_err.takeError());119 error = Status::FromErrorString("Failed to create script object.");120 return;121 }122 123 StructuredData::GenericSP object_sp = *obj_or_err;124 125 if (!object_sp || !object_sp->IsValid()) {126 error = Status::FromErrorStringWithFormat(127 "ScriptedProcess::%s () - ERROR: %s", __FUNCTION__,128 "Failed to create valid script object");129 return;130 }131}132 133ScriptedProcess::~ScriptedProcess() {134 Clear();135 // If the interface is not valid, we can't call Finalize(). When that happens136 // it means that the Scripted Process instanciation failed and the137 // CreateProcess function returns a nullptr, so no one besides this class138 // should have access to that bogus process object.139 if (!m_interface_up)140 return;141 // We need to call finalize on the process before destroying ourselves to142 // make sure all of the broadcaster cleanup goes as planned. If we destruct143 // this class, then Process::~Process() might have problems trying to fully144 // destroy the broadcaster.145 Finalize(true /* destructing */);146}147 148void ScriptedProcess::Initialize() {149 static llvm::once_flag g_once_flag;150 151 llvm::call_once(g_once_flag, []() {152 PluginManager::RegisterPlugin(GetPluginNameStatic(),153 GetPluginDescriptionStatic(), CreateInstance);154 });155}156 157void ScriptedProcess::Terminate() {158 PluginManager::UnregisterPlugin(ScriptedProcess::CreateInstance);159}160 161Status ScriptedProcess::DoLoadCore() {162 ProcessLaunchInfo launch_info = GetTarget().GetProcessLaunchInfo();163 164 return DoLaunch(nullptr, launch_info);165}166 167Status ScriptedProcess::DoLaunch(Module *exe_module,168 ProcessLaunchInfo &launch_info) {169 LLDB_LOGF(GetLog(LLDBLog::Process), "ScriptedProcess::%s launching process", __FUNCTION__);170 171 /* MARK: This doesn't reflect how lldb actually launches a process.172 In reality, it attaches to debugserver, then resume the process.173 That's not true in all cases. If debugserver is remote, lldb174 asks debugserver to launch the process for it. */175 Status error = GetInterface().Launch();176 SetPrivateState(eStateStopped);177 return error;178}179 180void ScriptedProcess::DidLaunch() { m_pid = GetInterface().GetProcessID(); }181 182void ScriptedProcess::DidResume() {183 // Update the PID again, in case the user provided a placeholder pid at launch184 m_pid = GetInterface().GetProcessID();185}186 187Status ScriptedProcess::DoResume(RunDirection direction) {188 LLDB_LOGF(GetLog(LLDBLog::Process), "ScriptedProcess::%s resuming process", __FUNCTION__);189 190 if (direction == RunDirection::eRunForward)191 return GetInterface().Resume();192 // FIXME: Pipe reverse continue through Scripted Processes193 return Status::FromErrorStringWithFormatv(194 "{0} does not support reverse execution of processes", GetPluginName());195}196 197Status ScriptedProcess::DoAttach(const ProcessAttachInfo &attach_info) {198 Status error = GetInterface().Attach(attach_info);199 SetPrivateState(eStateRunning);200 SetPrivateState(eStateStopped);201 if (error.Fail())202 return error;203 // NOTE: We need to set the PID before finishing to attach otherwise we will204 // hit an assert when calling the attach completion handler.205 DidLaunch();206 207 return {};208}209 210Status211ScriptedProcess::DoAttachToProcessWithID(lldb::pid_t pid,212 const ProcessAttachInfo &attach_info) {213 return DoAttach(attach_info);214}215 216Status ScriptedProcess::DoAttachToProcessWithName(217 const char *process_name, const ProcessAttachInfo &attach_info) {218 return DoAttach(attach_info);219}220 221void ScriptedProcess::DidAttach(ArchSpec &process_arch) {222 process_arch = GetArchitecture();223}224 225Status ScriptedProcess::DoDestroy() { return Status(); }226 227bool ScriptedProcess::IsAlive() { return GetInterface().IsAlive(); }228 229size_t ScriptedProcess::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,230 Status &error) {231 lldb::DataExtractorSP data_extractor_sp =232 GetInterface().ReadMemoryAtAddress(addr, size, error);233 234 if (!data_extractor_sp || !data_extractor_sp->GetByteSize() || error.Fail())235 return 0;236 237 offset_t bytes_copied = data_extractor_sp->CopyByteOrderedData(238 0, data_extractor_sp->GetByteSize(), buf, size, GetByteOrder());239 240 if (!bytes_copied || bytes_copied == LLDB_INVALID_OFFSET)241 return ScriptedInterface::ErrorWithMessage<size_t>(242 LLVM_PRETTY_FUNCTION, "Failed to copy read memory to buffer.", error);243 244 // FIXME: We should use the diagnostic system to report a warning if the245 // `bytes_copied` is different from `size`.246 247 return bytes_copied;248}249 250size_t ScriptedProcess::DoWriteMemory(lldb::addr_t vm_addr, const void *buf,251 size_t size, Status &error) {252 lldb::DataExtractorSP data_extractor_sp = std::make_shared<DataExtractor>(253 buf, size, GetByteOrder(), GetAddressByteSize());254 255 if (!data_extractor_sp || !data_extractor_sp->GetByteSize())256 return 0;257 258 lldb::offset_t bytes_written =259 GetInterface().WriteMemoryAtAddress(vm_addr, data_extractor_sp, error);260 261 if (!bytes_written || bytes_written == LLDB_INVALID_OFFSET)262 return ScriptedInterface::ErrorWithMessage<size_t>(263 LLVM_PRETTY_FUNCTION, "Failed to copy write buffer to memory.", error);264 265 // FIXME: We should use the diagnostic system to report a warning if the266 // `bytes_written` is different from `size`.267 268 return bytes_written;269}270 271Status ScriptedProcess::EnableBreakpointSite(BreakpointSite *bp_site) {272 assert(bp_site != nullptr);273 274 if (bp_site->IsEnabled()) {275 return {};276 }277 278 if (bp_site->HardwareRequired()) {279 return Status::FromErrorString(280 "Scripted Processes don't support hardware breakpoints");281 }282 283 Status error;284 GetInterface().CreateBreakpoint(bp_site->GetLoadAddress(), error);285 286 return error;287}288 289ArchSpec ScriptedProcess::GetArchitecture() {290 return GetTarget().GetArchitecture();291}292 293Status ScriptedProcess::DoGetMemoryRegionInfo(lldb::addr_t load_addr,294 MemoryRegionInfo ®ion) {295 Status error;296 if (auto region_or_err =297 GetInterface().GetMemoryRegionContainingAddress(load_addr, error))298 region = *region_or_err;299 300 return error;301}302 303Status ScriptedProcess::GetMemoryRegions(MemoryRegionInfos ®ion_list) {304 Status error;305 lldb::addr_t address = 0;306 307 while (auto region_or_err =308 GetInterface().GetMemoryRegionContainingAddress(address, error)) {309 if (error.Fail())310 break;311 312 MemoryRegionInfo &mem_region = *region_or_err;313 auto range = mem_region.GetRange();314 address += range.GetRangeBase() + range.GetByteSize();315 region_list.push_back(mem_region);316 }317 318 return error;319}320 321void ScriptedProcess::Clear() { Process::m_thread_list.Clear(); }322 323bool ScriptedProcess::DoUpdateThreadList(ThreadList &old_thread_list,324 ThreadList &new_thread_list) {325 // TODO: Implement326 // This is supposed to get the current set of threads, if any of them are in327 // old_thread_list then they get copied to new_thread_list, and then any328 // actually new threads will get added to new_thread_list.329 m_thread_plans.ClearThreadCache();330 331 Status error;332 StructuredData::DictionarySP thread_info_sp = GetInterface().GetThreadsInfo();333 334 if (!thread_info_sp)335 return ScriptedInterface::ErrorWithMessage<bool>(336 LLVM_PRETTY_FUNCTION,337 "Couldn't fetch thread list from Scripted Process.", error);338 339 // Because `StructuredData::Dictionary` uses a `std::map<ConstString,340 // ObjectSP>` for storage, each item is sorted based on the key alphabetical341 // order. Since `GetThreadsInfo` provides thread indices as the key element,342 // thread info comes ordered alphabetically, instead of numerically, so we343 // need to sort the thread indices before creating thread.344 345 StructuredData::ArraySP keys = thread_info_sp->GetKeys();346 347 std::map<size_t, StructuredData::ObjectSP> sorted_threads;348 auto sort_keys = [&sorted_threads,349 &thread_info_sp](StructuredData::Object *item) -> bool {350 if (!item)351 return false;352 353 llvm::StringRef key = item->GetStringValue();354 size_t idx = 0;355 356 // Make sure the provided index is actually an integer357 if (!llvm::to_integer(key, idx))358 return false;359 360 sorted_threads[idx] = thread_info_sp->GetValueForKey(key);361 return true;362 };363 364 size_t thread_count = thread_info_sp->GetSize();365 366 if (!keys->ForEach(sort_keys) || sorted_threads.size() != thread_count)367 // Might be worth showing the unsorted thread list instead of return early.368 return ScriptedInterface::ErrorWithMessage<bool>(369 LLVM_PRETTY_FUNCTION, "Couldn't sort thread list.", error);370 371 auto create_scripted_thread =372 [this, &error, &new_thread_list](373 const std::pair<size_t, StructuredData::ObjectSP> pair) -> bool {374 size_t idx = pair.first;375 StructuredData::ObjectSP object_sp = pair.second;376 377 if (!object_sp)378 return ScriptedInterface::ErrorWithMessage<bool>(379 LLVM_PRETTY_FUNCTION, "Invalid thread info object", error);380 381 auto thread_or_error =382 ScriptedThread::Create(*this, object_sp->GetAsGeneric());383 384 if (!thread_or_error)385 return ScriptedInterface::ErrorWithMessage<bool>(386 LLVM_PRETTY_FUNCTION, toString(thread_or_error.takeError()), error);387 388 ThreadSP thread_sp = thread_or_error.get();389 lldbassert(thread_sp && "Couldn't initialize scripted thread.");390 391 RegisterContextSP reg_ctx_sp = thread_sp->GetRegisterContext();392 if (!reg_ctx_sp)393 return ScriptedInterface::ErrorWithMessage<bool>(394 LLVM_PRETTY_FUNCTION,395 llvm::Twine("Invalid Register Context for thread " + llvm::Twine(idx))396 .str(),397 error);398 399 new_thread_list.AddThread(thread_sp);400 401 return true;402 };403 404 llvm::for_each(sorted_threads, create_scripted_thread);405 406 return new_thread_list.GetSize(false) > 0;407}408 409void ScriptedProcess::RefreshStateAfterStop() {410 // Let all threads recover from stopping and do any clean up based on the411 // previous thread state (if any).412 m_thread_list.RefreshStateAfterStop();413}414 415bool ScriptedProcess::GetProcessInfo(ProcessInstanceInfo &info) {416 info.Clear();417 info.SetProcessID(GetID());418 info.SetArchitecture(GetArchitecture());419 lldb::ModuleSP module_sp = GetTarget().GetExecutableModule();420 if (module_sp) {421 const bool add_exe_file_as_first_arg = false;422 info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(),423 add_exe_file_as_first_arg);424 }425 return true;426}427 428lldb_private::StructuredData::ObjectSP429ScriptedProcess::GetLoadedDynamicLibrariesInfos() {430 Status error;431 auto error_with_message = [&error](llvm::StringRef message) {432 return ScriptedInterface::ErrorWithMessage<bool>(LLVM_PRETTY_FUNCTION,433 message.data(), error);434 };435 436 StructuredData::ArraySP loaded_images_sp = GetInterface().GetLoadedImages();437 438 if (!loaded_images_sp || !loaded_images_sp->GetSize())439 return ScriptedInterface::ErrorWithMessage<StructuredData::ObjectSP>(440 LLVM_PRETTY_FUNCTION, "No loaded images.", error);441 442 ModuleList module_list;443 Target &target = GetTarget();444 445 auto reload_image = [&target, &module_list, &error_with_message](446 StructuredData::Object *obj) -> bool {447 StructuredData::Dictionary *dict = obj->GetAsDictionary();448 449 if (!dict)450 return error_with_message("Couldn't cast image object into dictionary.");451 452 ModuleSpec module_spec;453 454 bool has_path = dict->HasKey("path");455 bool has_uuid = dict->HasKey("uuid");456 if (!has_path && !has_uuid)457 return error_with_message("Dictionary should have key 'path' or 'uuid'");458 if (!dict->HasKey("load_addr"))459 return error_with_message("Dictionary is missing key 'load_addr'");460 461 llvm::StringRef path = "";462 if (has_path) {463 dict->GetValueForKeyAsString("path", path);464 module_spec.GetFileSpec().SetPath(path);465 }466 467 llvm::StringRef uuid = "";468 if (has_uuid) {469 dict->GetValueForKeyAsString("uuid", uuid);470 module_spec.GetUUID().SetFromStringRef(uuid);471 }472 473 lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;474 lldb::offset_t slide = LLDB_INVALID_OFFSET;475 dict->GetValueForKeyAsInteger("load_addr", load_addr);476 dict->GetValueForKeyAsInteger("slide", slide);477 if (load_addr == LLDB_INVALID_ADDRESS)478 return error_with_message(479 "Couldn't get valid load address or slide offset.");480 481 if (slide != LLDB_INVALID_OFFSET)482 load_addr += slide;483 484 module_spec.GetArchitecture() = target.GetArchitecture();485 486 ModuleSP module_sp =487 target.GetOrCreateModule(module_spec, true /* notify */);488 489 bool is_placeholder_module = false;490 491 if (!module_sp) {492 // Create a placeholder module493 LLDB_LOGF(494 GetLog(LLDBLog::Process),495 "ScriptedProcess::%s unable to locate the matching "496 "object file path %s, creating a placeholder module at 0x%" PRIx64,497 __FUNCTION__, path.str().c_str(), load_addr);498 499 module_sp = Module::CreateModuleFromObjectFile<ObjectFilePlaceholder>(500 module_spec, load_addr, module_spec.GetFileSpec().MemorySize());501 502 is_placeholder_module = true;503 }504 505 bool changed = false;506 module_sp->SetLoadAddress(target, load_addr, false /*=value_is_offset*/,507 changed);508 509 if (!changed && !module_sp->GetObjectFile())510 return error_with_message("Couldn't set the load address for module.");511 512 FileSpec objfile(path);513 module_sp->SetFileSpecAndObjectName(objfile, objfile.GetFilename());514 515 if (is_placeholder_module) {516 target.GetImages().AppendIfNeeded(module_sp, true /*notify=*/);517 return true;518 }519 520 return module_list.AppendIfNeeded(module_sp);521 };522 523 size_t loaded_images_size = loaded_images_sp->GetSize();524 bool print_error = true;525 for (size_t idx = 0; idx < loaded_images_size; idx++) {526 const auto &loaded_image = loaded_images_sp->GetItemAtIndex(idx);527 if (!reload_image(loaded_image.get()) && print_error) {528 print_error = false;529 ScriptedInterface::ErrorWithMessage<StructuredData::ObjectSP>(530 LLVM_PRETTY_FUNCTION, "Couldn't reload all images.", error);531 }532 }533 534 target.ModulesDidLoad(module_list);535 536 return loaded_images_sp;537}538 539lldb_private::StructuredData::DictionarySP ScriptedProcess::GetMetadata() {540 StructuredData::DictionarySP metadata_sp = GetInterface().GetMetadata();541 542 Status error;543 if (!metadata_sp || !metadata_sp->GetSize())544 return ScriptedInterface::ErrorWithMessage<StructuredData::DictionarySP>(545 LLVM_PRETTY_FUNCTION, "No metadata.", error);546 547 return metadata_sp;548}549 550void ScriptedProcess::UpdateQueueListIfNeeded() {551 CheckScriptedInterface();552 for (ThreadSP thread_sp : Threads()) {553 if (const char *queue_name = thread_sp->GetQueueName()) {554 QueueSP queue_sp = std::make_shared<Queue>(555 m_process->shared_from_this(), thread_sp->GetQueueID(), queue_name);556 m_queue_list.AddQueue(queue_sp);557 }558 }559}560 561ScriptedProcessInterface &ScriptedProcess::GetInterface() const {562 CheckScriptedInterface();563 return *m_interface_up;564}565 566void *ScriptedProcess::GetImplementation() {567 StructuredData::GenericSP object_instance_sp =568 GetInterface().GetScriptObjectInstance();569 if (object_instance_sp &&570 object_instance_sp->GetType() == eStructuredDataTypeGeneric)571 return object_instance_sp->GetAsGeneric()->GetValue();572 return nullptr;573}574