| //===-- Target.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/Target/Target.h" | |
| #include "lldb/Breakpoint/Breakpoint.h" | |
| #include "lldb/Breakpoint/BreakpointIDList.h" | |
| #include "lldb/Breakpoint/BreakpointPrecondition.h" | |
| #include "lldb/Breakpoint/BreakpointResolver.h" | |
| #include "lldb/Breakpoint/BreakpointResolverAddress.h" | |
| #include "lldb/Breakpoint/BreakpointResolverFileLine.h" | |
| #include "lldb/Breakpoint/BreakpointResolverFileRegex.h" | |
| #include "lldb/Breakpoint/BreakpointResolverName.h" | |
| #include "lldb/Breakpoint/BreakpointResolverScripted.h" | |
| #include "lldb/Breakpoint/ScriptedBreakpointOverrideResolver.h" | |
| #include "lldb/Breakpoint/Watchpoint.h" | |
| #include "lldb/Core/Debugger.h" | |
| #include "lldb/Core/Module.h" | |
| #include "lldb/Core/ModuleSpec.h" | |
| #include "lldb/Core/PluginManager.h" | |
| #include "lldb/Core/SearchFilter.h" | |
| #include "lldb/Core/Section.h" | |
| #include "lldb/Core/SourceManager.h" | |
| #include "lldb/Core/StructuredDataImpl.h" | |
| #include "lldb/Core/Telemetry.h" | |
| #include "lldb/DataFormatters/FormatterSection.h" | |
| #include "lldb/Expression/DiagnosticManager.h" | |
| #include "lldb/Expression/ExpressionVariable.h" | |
| #include "lldb/Expression/REPL.h" | |
| #include "lldb/Expression/UserExpression.h" | |
| #include "lldb/Expression/UtilityFunction.h" | |
| #include "lldb/Host/Host.h" | |
| #include "lldb/Host/PosixApi.h" | |
| #include "lldb/Host/StreamFile.h" | |
| #include "lldb/Interpreter/CommandInterpreter.h" | |
| #include "lldb/Interpreter/CommandOptionArgumentTable.h" | |
| #include "lldb/Interpreter/CommandReturnObject.h" | |
| #include "lldb/Interpreter/Interfaces/ScriptedBreakpointInterface.h" | |
| #include "lldb/Interpreter/Interfaces/ScriptedHookInterface.h" | |
| #include "lldb/Interpreter/OptionGroupWatchpoint.h" | |
| #include "lldb/Interpreter/OptionValueEnumeration.h" | |
| #include "lldb/Interpreter/OptionValues.h" | |
| #include "lldb/Interpreter/Property.h" | |
| #include "lldb/Symbol/Function.h" | |
| #include "lldb/Symbol/ObjectFile.h" | |
| #include "lldb/Symbol/Symbol.h" | |
| #include "lldb/Target/ABI.h" | |
| #include "lldb/Target/ExecutionContext.h" | |
| #include "lldb/Target/Language.h" | |
| #include "lldb/Target/LanguageRuntime.h" | |
| #include "lldb/Target/Process.h" | |
| #include "lldb/Target/RegisterTypeBuilder.h" | |
| #include "lldb/Target/SectionLoadList.h" | |
| #include "lldb/Target/StackFrame.h" | |
| #include "lldb/Target/StackFrameRecognizer.h" | |
| #include "lldb/Target/SystemRuntime.h" | |
| #include "lldb/Target/Thread.h" | |
| #include "lldb/Target/ThreadSpec.h" | |
| #include "lldb/Target/UnixSignals.h" | |
| #include "lldb/Utility/AnsiTerminal.h" | |
| #include "lldb/Utility/Event.h" | |
| #include "lldb/Utility/FileSpec.h" | |
| #include "lldb/Utility/LLDBAssert.h" | |
| #include "lldb/Utility/LLDBLog.h" | |
| #include "lldb/Utility/Log.h" | |
| #include "lldb/Utility/Policy.h" | |
| #include "lldb/Utility/RealpathPrefixes.h" | |
| #include "lldb/Utility/State.h" | |
| #include "lldb/Utility/StreamString.h" | |
| #include "lldb/Utility/Timer.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/ADT/ScopeExit.h" | |
| #include "llvm/ADT/SetVector.h" | |
| #include "llvm/Support/ErrorExtras.h" | |
| #include "llvm/Support/ThreadPool.h" | |
| #include <memory> | |
| #include <mutex> | |
| #include <optional> | |
| #include <set> | |
| #include <sstream> | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| namespace { | |
| struct ExecutableInstaller { | |
| ExecutableInstaller(PlatformSP platform, ModuleSP module) | |
| : m_platform{platform}, m_module{module}, | |
| m_local_file{m_module->GetFileSpec()}, | |
| m_remote_file{m_module->GetRemoteInstallFileSpec()} {} | |
| void setupRemoteFile() const { m_module->SetPlatformFileSpec(m_remote_file); } | |
| PlatformSP m_platform; | |
| ModuleSP m_module; | |
| const FileSpec m_local_file; | |
| const FileSpec m_remote_file; | |
| }; | |
| struct MainExecutableInstaller { | |
| MainExecutableInstaller(PlatformSP platform, ModuleSP module, TargetSP target, | |
| ProcessLaunchInfo &launch_info) | |
| : m_platform{platform}, m_module{module}, | |
| m_local_file{m_module->GetFileSpec()}, | |
| m_remote_file{ | |
| getRemoteFileSpec(m_platform, target, m_module, m_local_file)}, | |
| m_launch_info{launch_info} {} | |
| void setupRemoteFile() const { | |
| m_module->SetPlatformFileSpec(m_remote_file); | |
| m_launch_info.SetExecutableFile(m_remote_file, | |
| /*add_exe_file_as_first_arg=*/false); | |
| m_platform->SetFilePermissions(m_remote_file, 0700 /*-rwx------*/); | |
| } | |
| PlatformSP m_platform; | |
| ModuleSP m_module; | |
| const FileSpec m_local_file; | |
| const FileSpec m_remote_file; | |
| private: | |
| static FileSpec getRemoteFileSpec(PlatformSP platform, TargetSP target, | |
| ModuleSP module, | |
| const FileSpec &local_file) { | |
| FileSpec remote_file = module->GetRemoteInstallFileSpec(); | |
| if (remote_file || !target->GetAutoInstallMainExecutable()) | |
| return remote_file; | |
| if (!local_file) | |
| return {}; | |
| remote_file = platform->GetRemoteWorkingDirectory(); | |
| remote_file.AppendPathComponent(local_file.GetFilename()); | |
| return remote_file; | |
| } | |
| ProcessLaunchInfo &m_launch_info; | |
| }; | |
| } // namespace | |
| static std::atomic<lldb::user_id_t> g_target_unique_id{1}; | |
| template <typename Installer> | |
| static Status installExecutable(const Installer &installer) { | |
| if (!installer.m_local_file || !installer.m_remote_file) | |
| return Status(); | |
| Status error = installer.m_platform->Install(installer.m_local_file, | |
| installer.m_remote_file); | |
| if (error.Fail()) | |
| return error; | |
| installer.setupRemoteFile(); | |
| return Status(); | |
| } | |
| Target::Arch::Arch(const ArchSpec &spec) | |
| : m_spec(spec), | |
| m_plugin_up(PluginManager::CreateArchitectureInstance(spec)) {} | |
| const Target::Arch &Target::Arch::operator=(const ArchSpec &spec) { | |
| m_spec = spec; | |
| m_plugin_up = PluginManager::CreateArchitectureInstance(spec); | |
| return *this; | |
| } | |
| llvm::StringRef Target::GetStaticBroadcasterClass() { | |
| static constexpr llvm::StringLiteral class_name("lldb.target"); | |
| return class_name; | |
| } | |
| Target::Target(Debugger &debugger, const ArchSpec &target_arch, | |
| const lldb::PlatformSP &platform_sp, bool is_dummy_target) | |
| : TargetProperties(this), | |
| Broadcaster(debugger.GetBroadcasterManager(), | |
| Target::GetStaticBroadcasterClass().str()), | |
| ExecutionContextScope(), m_debugger(debugger), m_platform_sp(platform_sp), | |
| m_mutex(), m_arch(target_arch), m_images(this), m_section_load_history(), | |
| m_breakpoint_list(false), m_internal_breakpoint_list(true), | |
| m_watchpoint_list(), m_process_sp(), m_search_filter_sp(), | |
| m_image_search_paths(ImageSearchPathsChanged, this), | |
| m_source_manager_up(), m_stop_hooks(), m_stop_hook_next_id(0), | |
| m_internal_stop_hooks(), m_latest_stop_hook_id(0), m_valid(true), | |
| m_suppress_stop_hooks(false), m_is_dummy_target(is_dummy_target), | |
| m_target_unique_id(g_target_unique_id++), | |
| m_target_session_name( | |
| llvm::formatv("Session {0}", m_target_unique_id).str()), | |
| m_frame_recognizer_manager_up( | |
| std::make_unique<StackFrameRecognizerManager>()) { | |
| SetEventName(eBroadcastBitBreakpointChanged, "breakpoint-changed"); | |
| SetEventName(eBroadcastBitModulesLoaded, "modules-loaded"); | |
| SetEventName(eBroadcastBitModulesUnloaded, "modules-unloaded"); | |
| SetEventName(eBroadcastBitWatchpointChanged, "watchpoint-changed"); | |
| SetEventName(eBroadcastBitSymbolsLoaded, "symbols-loaded"); | |
| SetEventName(eBroadcastBitNewTargetCreated, "new-target-created"); | |
| CheckInWithManager(); | |
| LLDB_LOG(GetLog(LLDBLog::Object), "{0} Target::Target()", | |
| static_cast<void *>(this)); | |
| if (target_arch.IsValid()) { | |
| LLDB_LOG(GetLog(LLDBLog::Target), | |
| "Target::Target created with architecture {0} ({1})", | |
| target_arch.GetArchitectureName(), | |
| target_arch.GetTriple().getTriple().c_str()); | |
| } | |
| UpdateLaunchInfoFromProperties(); | |
| } | |
| Target::~Target() { | |
| Log *log = GetLog(LLDBLog::Object); | |
| LLDB_LOG(log, "{0} Target::~Target()", static_cast<void *>(this)); | |
| DeleteCurrentProcess(); | |
| } | |
| void Target::PrimeFromDummyTarget(Target &target) { | |
| m_stop_hooks = target.m_stop_hooks; | |
| m_stop_hook_next_id = target.m_stop_hook_next_id; | |
| m_internal_stop_hooks = target.m_internal_stop_hooks; | |
| m_hooks = target.m_hooks; | |
| m_hook_next_id = target.m_hook_next_id; | |
| for (const auto &breakpoint_sp : target.m_breakpoint_list.Breakpoints()) { | |
| if (breakpoint_sp->IsInternal()) | |
| continue; | |
| BreakpointSP new_bp( | |
| Breakpoint::CopyFromBreakpoint(shared_from_this(), *breakpoint_sp)); | |
| AddBreakpoint(std::move(new_bp), false); | |
| } | |
| for (const auto &bp_name_entry : target.m_breakpoint_names) { | |
| AddBreakpointName(std::make_unique<BreakpointName>(*bp_name_entry.second)); | |
| } | |
| for (auto const &elem : target.m_breakpoint_overrides) { | |
| BreakpointResolverOverrideUP new_override_up = | |
| elem.second->CopyIntoNewTarget(*this); | |
| if (new_override_up->Validate()) | |
| AddBreakpointResolverOverride(std::move(new_override_up)); | |
| } | |
| m_frame_recognizer_manager_up = std::make_unique<StackFrameRecognizerManager>( | |
| *target.m_frame_recognizer_manager_up); | |
| m_dummy_signals = target.m_dummy_signals; | |
| } | |
| void Target::Dump(Stream *s, lldb::DescriptionLevel description_level) { | |
| // s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); | |
| if (description_level != lldb::eDescriptionLevelBrief) { | |
| s->Indent(); | |
| s->PutCString("Target\n"); | |
| s->IndentMore(); | |
| m_images.Dump(s); | |
| m_breakpoint_list.Dump(s); | |
| m_internal_breakpoint_list.Dump(s); | |
| s->IndentLess(); | |
| } else { | |
| Module *exe_module = GetExecutableModulePointer(); | |
| if (exe_module) | |
| s->PutCString(exe_module->GetFileSpec().GetFilename()); | |
| else | |
| s->PutCString("No executable module."); | |
| } | |
| } | |
| void Target::CleanupProcess() { | |
| // Do any cleanup of the target we need to do between process instances. | |
| // NB It is better to do this before destroying the process in case the | |
| // clean up needs some help from the process. | |
| m_breakpoint_list.ClearAllBreakpointSites(); | |
| m_internal_breakpoint_list.ClearAllBreakpointSites(); | |
| ResetBreakpointHitCounts(); | |
| llvm::consumeError(m_process_sp->FlushDelayedBreakpoints()); | |
| // Disable watchpoints just on the debugger side. | |
| std::unique_lock<std::recursive_mutex> lock; | |
| this->GetWatchpointList().GetListMutex(lock); | |
| DisableAllWatchpoints(false); | |
| ClearAllWatchpointHitCounts(); | |
| ClearAllWatchpointHistoricValues(); | |
| m_latest_stop_hook_id = 0; | |
| } | |
| void Target::DeleteCurrentProcess() { | |
| if (m_process_sp) { | |
| // We dispose any active tracing sessions on the current process | |
| m_trace_sp.reset(); | |
| if (m_process_sp->IsAlive()) | |
| m_process_sp->Destroy(false); | |
| m_process_sp->Finalize(false /* not destructing */); | |
| // Let the process finalize itself first, then clear the section load | |
| // history. Some objects owned by the process might end up calling | |
| // SectionLoadHistory::SetSectionUnloaded() which can create entries in | |
| // the section load history that can mess up subsequent processes. | |
| m_section_load_history.Clear(); | |
| CleanupProcess(); | |
| m_process_sp.reset(); | |
| } | |
| } | |
| const lldb::ProcessSP &Target::CreateProcess(ListenerSP listener_sp, | |
| llvm::StringRef plugin_name, | |
| const FileSpec *crash_file, | |
| bool can_connect) { | |
| if (!listener_sp) | |
| listener_sp = GetDebugger().GetListener(); | |
| DeleteCurrentProcess(); | |
| m_process_sp = Process::FindPlugin(shared_from_this(), plugin_name, | |
| listener_sp, crash_file, can_connect); | |
| return m_process_sp; | |
| } | |
| const lldb::ProcessSP &Target::GetProcessSP() const { return m_process_sp; } | |
| lldb::REPLSP Target::GetREPL(Status &err, lldb::LanguageType language, | |
| const char *repl_options, bool can_create) { | |
| if (language == eLanguageTypeUnknown) | |
| language = m_debugger.GetREPLLanguage(); | |
| if (language == eLanguageTypeUnknown) { | |
| LanguageSet repl_languages = Language::GetLanguagesSupportingREPLs(); | |
| if (auto single_lang = repl_languages.GetSingularLanguage()) { | |
| language = *single_lang; | |
| } else if (repl_languages.Empty()) { | |
| err = Status::FromErrorString( | |
| "LLDB isn't configured with REPL support for any languages."); | |
| return REPLSP(); | |
| } else { | |
| err = Status::FromErrorString( | |
| "Multiple possible REPL languages. Please specify a language."); | |
| return REPLSP(); | |
| } | |
| } | |
| REPLMap::iterator pos = m_repl_map.find(language); | |
| if (pos != m_repl_map.end()) { | |
| return pos->second; | |
| } | |
| if (!can_create) { | |
| err = Status::FromErrorStringWithFormat( | |
| "Couldn't find an existing REPL for %s, and can't create a new one", | |
| Language::GetNameForLanguageType(language)); | |
| return lldb::REPLSP(); | |
| } | |
| Debugger *const debugger = nullptr; | |
| lldb::REPLSP ret = REPL::Create(err, language, debugger, this, repl_options); | |
| if (ret) { | |
| m_repl_map[language] = ret; | |
| return m_repl_map[language]; | |
| } | |
| if (err.Success()) { | |
| err = Status::FromErrorStringWithFormat( | |
| "Couldn't create a REPL for %s", | |
| Language::GetNameForLanguageType(language)); | |
| } | |
| return lldb::REPLSP(); | |
| } | |
| void Target::SetREPL(lldb::LanguageType language, lldb::REPLSP repl_sp) { | |
| lldbassert(!m_repl_map.count(language)); | |
| m_repl_map[language] = repl_sp; | |
| } | |
| void Target::Destroy() { | |
| std::lock_guard<std::recursive_mutex> guard(m_mutex); | |
| m_valid = false; | |
| DeleteCurrentProcess(); | |
| m_platform_sp.reset(); | |
| m_arch = ArchSpec(); | |
| ClearModules(true); | |
| m_section_load_history.Clear(); | |
| const bool notify = false; | |
| m_breakpoint_list.RemoveAll(notify); | |
| m_internal_breakpoint_list.RemoveAll(notify); | |
| m_last_created_breakpoint.reset(); | |
| m_watchpoint_list.RemoveAll(notify); | |
| m_last_created_watchpoint.reset(); | |
| m_search_filter_sp.reset(); | |
| m_image_search_paths.Clear(notify); | |
| m_stop_hooks.clear(); | |
| m_stop_hook_next_id = 0; | |
| m_internal_stop_hooks.clear(); | |
| m_suppress_stop_hooks = false; | |
| m_repl_map.clear(); | |
| Args signal_args; | |
| ClearDummySignals(signal_args); | |
| } | |
| llvm::StringRef Target::GetABIName() const { | |
| lldb::ABISP abi_sp; | |
| if (m_process_sp) | |
| abi_sp = m_process_sp->GetABI(); | |
| if (!abi_sp) | |
| abi_sp = ABI::FindPlugin(ProcessSP(), GetArchitecture()); | |
| if (abi_sp) | |
| return abi_sp->GetPluginName(); | |
| return {}; | |
| } | |
| BreakpointList &Target::GetBreakpointList(bool internal) { | |
| if (internal) | |
| return m_internal_breakpoint_list; | |
| else | |
| return m_breakpoint_list; | |
| } | |
| const BreakpointList &Target::GetBreakpointList(bool internal) const { | |
| if (internal) | |
| return m_internal_breakpoint_list; | |
| else | |
| return m_breakpoint_list; | |
| } | |
| BreakpointSP Target::GetBreakpointByID(break_id_t break_id) { | |
| BreakpointSP bp_sp; | |
| if (LLDB_BREAK_ID_IS_INTERNAL(break_id)) | |
| bp_sp = m_internal_breakpoint_list.FindBreakpointByID(break_id); | |
| else | |
| bp_sp = m_breakpoint_list.FindBreakpointByID(break_id); | |
| return bp_sp; | |
| } | |
| lldb::BreakpointSP | |
| lldb_private::Target::CreateBreakpointAtUserEntry(Status &error) { | |
| ModuleSP main_module_sp = GetExecutableModule(); | |
| if (!main_module_sp) { | |
| error = Status::FromErrorString("target has no executable\n"); | |
| return lldb::BreakpointSP(); | |
| } | |
| FileSpecList shared_lib_filter; | |
| shared_lib_filter.Append(main_module_sp->GetFileSpec()); | |
| llvm::SetVector<std::string, std::vector<std::string>, | |
| std::unordered_set<std::string>> | |
| entryPointNamesSet; | |
| for (LanguageType lang_type : Language::GetSupportedLanguages()) { | |
| Language *lang = Language::FindPlugin(lang_type); | |
| if (!lang) { | |
| error = Status::FromErrorString("Language not found\n"); | |
| return lldb::BreakpointSP(); | |
| } | |
| std::string entryPointName = lang->GetUserEntryPointName().str(); | |
| if (!entryPointName.empty()) | |
| entryPointNamesSet.insert(entryPointName); | |
| } | |
| if (entryPointNamesSet.empty()) { | |
| error = Status::FromErrorString("No entry point name found\n"); | |
| return lldb::BreakpointSP(); | |
| } | |
| BreakpointSP bp_sp = CreateBreakpoint( | |
| &shared_lib_filter, | |
| /*containingSourceFiles=*/nullptr, entryPointNamesSet.takeVector(), | |
| /*func_name_type_mask=*/eFunctionNameTypeFull, | |
| /*language=*/eLanguageTypeUnknown, | |
| /*offset=*/0, | |
| /*skip_prologue=*/eLazyBoolNo, | |
| /*internal=*/false, | |
| /*hardware=*/false); | |
| if (!bp_sp) { | |
| error = Status::FromErrorString("Breakpoint creation failed.\n"); | |
| return lldb::BreakpointSP(); | |
| } | |
| bp_sp->SetOneShot(true); | |
| return bp_sp; | |
| } | |
| BreakpointSP Target::CreateSourceRegexBreakpoint( | |
| const FileSpecList *containingModules, | |
| const FileSpecList *source_file_spec_list, | |
| const std::unordered_set<std::string> &function_names, | |
| RegularExpression source_regex, bool internal, bool hardware, | |
| LazyBool move_to_nearest_code) { | |
| SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( | |
| containingModules, source_file_spec_list)); | |
| if (move_to_nearest_code == eLazyBoolCalculate) | |
| move_to_nearest_code = GetMoveToNearestCode() ? eLazyBoolYes : eLazyBoolNo; | |
| BreakpointResolverSP resolver_sp(new BreakpointResolverFileRegex( | |
| nullptr, std::move(source_regex), function_names, | |
| !static_cast<bool>(move_to_nearest_code))); | |
| return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); | |
| } | |
| BreakpointSP Target::CreateBreakpoint(const FileSpecList *containingModules, | |
| const FileSpec &file, uint32_t line_no, | |
| uint32_t column, lldb::addr_t offset, | |
| LazyBool check_inlines, | |
| LazyBool skip_prologue, bool internal, | |
| bool hardware, | |
| LazyBool move_to_nearest_code) { | |
| FileSpec remapped_file; | |
| std::optional<llvm::StringRef> removed_prefix_opt = | |
| GetSourcePathMap().ReverseRemapPath(file, remapped_file); | |
| if (!removed_prefix_opt) | |
| remapped_file = file; | |
| if (check_inlines == eLazyBoolCalculate) { | |
| const InlineStrategy inline_strategy = GetInlineStrategy(); | |
| switch (inline_strategy) { | |
| case eInlineBreakpointsNever: | |
| check_inlines = eLazyBoolNo; | |
| break; | |
| case eInlineBreakpointsHeaders: | |
| if (remapped_file.IsSourceImplementationFile()) | |
| check_inlines = eLazyBoolNo; | |
| else | |
| check_inlines = eLazyBoolYes; | |
| break; | |
| case eInlineBreakpointsAlways: | |
| check_inlines = eLazyBoolYes; | |
| break; | |
| } | |
| } | |
| SearchFilterSP filter_sp; | |
| if (check_inlines == eLazyBoolNo) { | |
| // Not checking for inlines, we are looking only for matching compile units | |
| FileSpecList compile_unit_list; | |
| compile_unit_list.Append(remapped_file); | |
| filter_sp = GetSearchFilterForModuleAndCUList(containingModules, | |
| &compile_unit_list); | |
| } else { | |
| filter_sp = GetSearchFilterForModuleList(containingModules); | |
| } | |
| if (skip_prologue == eLazyBoolCalculate) | |
| skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo; | |
| if (move_to_nearest_code == eLazyBoolCalculate) | |
| move_to_nearest_code = GetMoveToNearestCode() ? eLazyBoolYes : eLazyBoolNo; | |
| SourceLocationSpec location_spec(remapped_file, line_no, column, | |
| check_inlines, | |
| !static_cast<bool>(move_to_nearest_code)); | |
| if (!location_spec) | |
| return nullptr; | |
| BreakpointResolverSP resolver_sp(new BreakpointResolverFileLine( | |
| nullptr, offset, skip_prologue, location_spec, removed_prefix_opt)); | |
| return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); | |
| } | |
| BreakpointSP Target::CreateBreakpoint(lldb::addr_t addr, bool internal, | |
| bool hardware) { | |
| Address so_addr; | |
| // Check for any reason we want to move this breakpoint to other address. | |
| addr = GetBreakableLoadAddress(addr); | |
| // Attempt to resolve our load address if possible, though it is ok if it | |
| // doesn't resolve to section/offset. | |
| // Try and resolve as a load address if possible | |
| GetSectionLoadList().ResolveLoadAddress(addr, so_addr); | |
| if (!so_addr.IsValid()) { | |
| // The address didn't resolve, so just set this as an absolute address | |
| so_addr.SetOffset(addr); | |
| } | |
| BreakpointSP bp_sp(CreateBreakpoint(so_addr, internal, hardware)); | |
| return bp_sp; | |
| } | |
| BreakpointSP Target::CreateBreakpoint(const Address &addr, bool internal, | |
| bool hardware) { | |
| SearchFilterSP filter_sp = | |
| std::make_shared<SearchFilterForUnconstrainedSearches>( | |
| shared_from_this()); | |
| BreakpointResolverSP resolver_sp = | |
| std::make_shared<BreakpointResolverAddress>(nullptr, addr); | |
| return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, false); | |
| } | |
| lldb::BreakpointSP | |
| Target::CreateAddressInModuleBreakpoint(lldb::addr_t file_addr, bool internal, | |
| const FileSpec &file_spec, | |
| bool request_hardware) { | |
| SearchFilterSP filter_sp = | |
| std::make_shared<SearchFilterForUnconstrainedSearches>( | |
| shared_from_this()); | |
| BreakpointResolverSP resolver_sp = | |
| std::make_shared<BreakpointResolverAddress>(nullptr, Address(file_addr), | |
| file_spec); | |
| return CreateBreakpoint(filter_sp, resolver_sp, internal, request_hardware, | |
| false); | |
| } | |
| BreakpointSP Target::CreateBreakpoint( | |
| const FileSpecList *containingModules, | |
| const FileSpecList *containingSourceFiles, const char *func_name, | |
| FunctionNameType func_name_type_mask, LanguageType language, | |
| lldb::addr_t offset, bool offset_is_insn_count, LazyBool skip_prologue, | |
| bool internal, bool hardware) { | |
| BreakpointSP bp_sp; | |
| if (func_name) { | |
| SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( | |
| containingModules, containingSourceFiles)); | |
| if (skip_prologue == eLazyBoolCalculate) | |
| skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo; | |
| if (language == lldb::eLanguageTypeUnknown) | |
| language = GetLanguage().AsLanguageType(); | |
| BreakpointResolverSP resolver_sp(new BreakpointResolverName( | |
| nullptr, func_name, func_name_type_mask, language, Breakpoint::Exact, | |
| offset, offset_is_insn_count, skip_prologue)); | |
| bp_sp = CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); | |
| } | |
| return bp_sp; | |
| } | |
| lldb::BreakpointSP | |
| Target::CreateBreakpoint(const FileSpecList *containingModules, | |
| const FileSpecList *containingSourceFiles, | |
| const std::vector<std::string> &func_names, | |
| FunctionNameType func_name_type_mask, | |
| LanguageType language, lldb::addr_t offset, | |
| LazyBool skip_prologue, bool internal, bool hardware) { | |
| BreakpointSP bp_sp; | |
| size_t num_names = func_names.size(); | |
| if (num_names > 0) { | |
| SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( | |
| containingModules, containingSourceFiles)); | |
| if (skip_prologue == eLazyBoolCalculate) | |
| skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo; | |
| if (language == lldb::eLanguageTypeUnknown) | |
| language = GetLanguage().AsLanguageType(); | |
| BreakpointResolverSP resolver_sp( | |
| new BreakpointResolverName(nullptr, func_names, func_name_type_mask, | |
| language, offset, skip_prologue)); | |
| bp_sp = CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); | |
| } | |
| return bp_sp; | |
| } | |
| BreakpointSP | |
| Target::CreateBreakpoint(const FileSpecList *containingModules, | |
| const FileSpecList *containingSourceFiles, | |
| const char *func_names[], size_t num_names, | |
| FunctionNameType func_name_type_mask, | |
| LanguageType language, lldb::addr_t offset, | |
| LazyBool skip_prologue, bool internal, bool hardware) { | |
| BreakpointSP bp_sp; | |
| if (num_names > 0) { | |
| SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( | |
| containingModules, containingSourceFiles)); | |
| if (skip_prologue == eLazyBoolCalculate) { | |
| if (offset == 0) | |
| skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo; | |
| else | |
| skip_prologue = eLazyBoolNo; | |
| } | |
| if (language == lldb::eLanguageTypeUnknown) | |
| language = GetLanguage().AsLanguageType(); | |
| BreakpointResolverSP resolver_sp(new BreakpointResolverName( | |
| nullptr, func_names, num_names, func_name_type_mask, language, offset, | |
| skip_prologue)); | |
| resolver_sp->SetOffset(offset); | |
| bp_sp = CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); | |
| } | |
| return bp_sp; | |
| } | |
| SearchFilterSP | |
| Target::GetSearchFilterForModule(const FileSpec *containingModule) { | |
| SearchFilterSP filter_sp; | |
| if (containingModule != nullptr) { | |
| // TODO: We should look into sharing module based search filters | |
| // across many breakpoints like we do for the simple target based one | |
| filter_sp = std::make_shared<SearchFilterByModule>(shared_from_this(), | |
| *containingModule); | |
| } else { | |
| if (!m_search_filter_sp) | |
| m_search_filter_sp = | |
| std::make_shared<SearchFilterForUnconstrainedSearches>( | |
| shared_from_this()); | |
| filter_sp = m_search_filter_sp; | |
| } | |
| return filter_sp; | |
| } | |
| SearchFilterSP | |
| Target::GetSearchFilterForModuleList(const FileSpecList *containingModules) { | |
| SearchFilterSP filter_sp; | |
| if (containingModules && containingModules->GetSize() != 0) { | |
| // TODO: We should look into sharing module based search filters | |
| // across many breakpoints like we do for the simple target based one | |
| filter_sp = std::make_shared<SearchFilterByModuleList>(shared_from_this(), | |
| *containingModules); | |
| } else { | |
| if (!m_search_filter_sp) | |
| m_search_filter_sp = | |
| std::make_shared<SearchFilterForUnconstrainedSearches>( | |
| shared_from_this()); | |
| filter_sp = m_search_filter_sp; | |
| } | |
| return filter_sp; | |
| } | |
| SearchFilterSP Target::GetSearchFilterForModuleAndCUList( | |
| const FileSpecList *containingModules, | |
| const FileSpecList *containingSourceFiles) { | |
| if (containingSourceFiles == nullptr || containingSourceFiles->GetSize() == 0) | |
| return GetSearchFilterForModuleList(containingModules); | |
| SearchFilterSP filter_sp; | |
| if (containingModules == nullptr) { | |
| // We could make a special "CU List only SearchFilter". Better yet was if | |
| // these could be composable, but that will take a little reworking. | |
| filter_sp = std::make_shared<SearchFilterByModuleListAndCU>( | |
| shared_from_this(), FileSpecList(), *containingSourceFiles); | |
| } else { | |
| filter_sp = std::make_shared<SearchFilterByModuleListAndCU>( | |
| shared_from_this(), *containingModules, *containingSourceFiles); | |
| } | |
| return filter_sp; | |
| } | |
| BreakpointSP Target::CreateFuncRegexBreakpoint( | |
| const FileSpecList *containingModules, | |
| const FileSpecList *containingSourceFiles, RegularExpression func_regex, | |
| lldb::LanguageType requested_language, LazyBool skip_prologue, | |
| bool internal, bool hardware) { | |
| SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( | |
| containingModules, containingSourceFiles)); | |
| bool skip = (skip_prologue == eLazyBoolCalculate) | |
| ? GetSkipPrologue() | |
| : static_cast<bool>(skip_prologue); | |
| BreakpointResolverSP resolver_sp(new BreakpointResolverName( | |
| nullptr, std::move(func_regex), requested_language, 0, skip)); | |
| return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); | |
| } | |
| lldb::BreakpointSP | |
| Target::CreateExceptionBreakpoint(enum lldb::LanguageType language, | |
| bool catch_bp, bool throw_bp, bool internal, | |
| Args *additional_args, Status *error) { | |
| BreakpointSP exc_bkpt_sp = LanguageRuntime::CreateExceptionBreakpoint( | |
| *this, language, catch_bp, throw_bp, internal); | |
| if (exc_bkpt_sp && additional_args) { | |
| BreakpointPreconditionSP precondition_sp = exc_bkpt_sp->GetPrecondition(); | |
| if (precondition_sp && additional_args) { | |
| if (error) | |
| *error = precondition_sp->ConfigurePrecondition(*additional_args); | |
| else | |
| precondition_sp->ConfigurePrecondition(*additional_args); | |
| } | |
| } | |
| return exc_bkpt_sp; | |
| } | |
| lldb::BreakpointSP Target::CreateScriptedBreakpoint( | |
| const llvm::StringRef class_name, const FileSpecList *containingModules, | |
| const FileSpecList *containingSourceFiles, bool internal, | |
| bool request_hardware, StructuredData::ObjectSP extra_args_sp, | |
| Status *creation_error) { | |
| SearchFilterSP filter_sp; | |
| lldb::SearchDepth depth = lldb::eSearchDepthTarget; | |
| bool has_files = | |
| containingSourceFiles && containingSourceFiles->GetSize() > 0; | |
| bool has_modules = containingModules && containingModules->GetSize() > 0; | |
| if (has_files && has_modules) { | |
| filter_sp = GetSearchFilterForModuleAndCUList(containingModules, | |
| containingSourceFiles); | |
| } else if (has_files) { | |
| filter_sp = | |
| GetSearchFilterForModuleAndCUList(nullptr, containingSourceFiles); | |
| } else if (has_modules) { | |
| filter_sp = GetSearchFilterForModuleList(containingModules); | |
| } else { | |
| filter_sp = std::make_shared<SearchFilterForUnconstrainedSearches>( | |
| shared_from_this()); | |
| } | |
| BreakpointResolverSP resolver_sp(new BreakpointResolverScripted( | |
| nullptr, class_name, depth, StructuredDataImpl(extra_args_sp))); | |
| return CreateBreakpoint(filter_sp, resolver_sp, internal, false, true); | |
| } | |
| BreakpointSP Target::CreateBreakpoint(SearchFilterSP &filter_sp, | |
| BreakpointResolverSP &resolver_sp, | |
| bool internal, bool request_hardware, | |
| bool resolve_indirect_symbols) { | |
| BreakpointSP bp_sp; | |
| if (filter_sp && resolver_sp) { | |
| // Now check whether there are any "Breakpoint Overrides" registered, and | |
| // if there are see if one of them want to handle this request instead. | |
| // But we don't allow overrides for internal breakpoints: | |
| if (!internal) { | |
| BreakpointResolverSP overridden_sp = | |
| CheckBreakpointOverrides(resolver_sp); | |
| if (overridden_sp) | |
| resolver_sp = overridden_sp; | |
| } | |
| const bool hardware = request_hardware || GetRequireHardwareBreakpoints(); | |
| bp_sp.reset(new Breakpoint(*this, filter_sp, resolver_sp, hardware, | |
| resolve_indirect_symbols)); | |
| resolver_sp->SetBreakpoint(bp_sp); | |
| AddBreakpoint(bp_sp, internal); | |
| } | |
| return bp_sp; | |
| } | |
| void Target::AddBreakpoint(lldb::BreakpointSP bp_sp, bool internal) { | |
| if (!bp_sp) | |
| return; | |
| if (internal) | |
| m_internal_breakpoint_list.Add(bp_sp, false); | |
| else | |
| m_breakpoint_list.Add(bp_sp, true); | |
| Log *log = GetLog(LLDBLog::Breakpoints); | |
| if (log) { | |
| StreamString s; | |
| bp_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); | |
| LLDB_LOGF(log, "Target::%s (internal = %s) => break_id = %s\n", | |
| __FUNCTION__, bp_sp->IsInternal() ? "yes" : "no", s.GetData()); | |
| } | |
| bp_sp->ResolveBreakpoint(); | |
| if (!internal) { | |
| m_last_created_breakpoint = bp_sp; | |
| } | |
| } | |
| void Target::AddNameToBreakpoint(BreakpointID &id, llvm::StringRef name, | |
| Status &error) { | |
| BreakpointSP bp_sp = | |
| m_breakpoint_list.FindBreakpointByID(id.GetBreakpointID()); | |
| if (!bp_sp) { | |
| StreamString s; | |
| id.GetDescription(&s, eDescriptionLevelBrief); | |
| error = Status::FromErrorStringWithFormat("Could not find breakpoint %s", | |
| s.GetData()); | |
| return; | |
| } | |
| AddNameToBreakpoint(bp_sp, name, error); | |
| } | |
| void Target::AddNameToBreakpoint(BreakpointSP &bp_sp, llvm::StringRef name, | |
| Status &error) { | |
| if (!bp_sp) | |
| return; | |
| BreakpointName *bp_name = FindBreakpointName(name, true, error); | |
| if (!bp_name) | |
| return; | |
| bp_name->ConfigureBreakpoint(bp_sp); | |
| bp_sp->AddName(name); | |
| } | |
| void Target::AddBreakpointName(std::unique_ptr<BreakpointName> bp_name) { | |
| m_breakpoint_names.insert( | |
| std::make_pair(bp_name->GetName(), std::move(bp_name))); | |
| } | |
| BreakpointName *Target::FindBreakpointName(llvm::StringRef name, | |
| bool can_create, Status &error) { | |
| BreakpointID::StringIsBreakpointName(name, error); | |
| if (!error.Success()) | |
| return nullptr; | |
| BreakpointNameMap::iterator iter = m_breakpoint_names.find(name); | |
| if (iter != m_breakpoint_names.end()) { | |
| return iter->second.get(); | |
| } | |
| if (!can_create) { | |
| error = Status::FromErrorStringWithFormatv( | |
| "Breakpoint name \"{0}\" doesn't exist and can_create is false.", name); | |
| return nullptr; | |
| } | |
| return m_breakpoint_names | |
| .insert( | |
| std::make_pair(name, std::make_unique<BreakpointName>(name.str()))) | |
| .first->second.get(); | |
| } | |
| void Target::DeleteBreakpointName(llvm::StringRef name) { | |
| BreakpointNameMap::iterator iter = m_breakpoint_names.find(name); | |
| if (iter != m_breakpoint_names.end()) { | |
| m_breakpoint_names.erase(iter); | |
| for (auto bp_sp : m_breakpoint_list.Breakpoints()) | |
| bp_sp->RemoveName(name); | |
| } | |
| } | |
| void Target::RemoveNameFromBreakpoint(lldb::BreakpointSP &bp_sp, | |
| llvm::StringRef name) { | |
| bp_sp->RemoveName(name); | |
| } | |
| void Target::ConfigureBreakpointName( | |
| BreakpointName &bp_name, const BreakpointOptions &new_options, | |
| const BreakpointName::Permissions &new_permissions) { | |
| bp_name.GetOptions().CopyOverSetOptions(new_options); | |
| bp_name.GetPermissions().MergeInto(new_permissions); | |
| ApplyNameToBreakpoints(bp_name); | |
| } | |
| void Target::ApplyNameToBreakpoints(BreakpointName &bp_name) { | |
| llvm::Expected<std::vector<BreakpointSP>> expected_vector = | |
| m_breakpoint_list.FindBreakpointsByName(bp_name.GetName()); | |
| if (!expected_vector) { | |
| LLDB_LOG(GetLog(LLDBLog::Breakpoints), "invalid breakpoint name: {}", | |
| llvm::toString(expected_vector.takeError())); | |
| return; | |
| } | |
| for (auto bp_sp : *expected_vector) | |
| bp_name.ConfigureBreakpoint(bp_sp); | |
| } | |
| void Target::GetBreakpointNames(std::vector<std::string> &names) { | |
| names.clear(); | |
| for (const auto &bp_name_entry : m_breakpoint_names) { | |
| names.push_back(bp_name_entry.first().str()); | |
| } | |
| llvm::sort(names); | |
| } | |
| llvm::Expected<lldb::user_id_t> Target::AddBreakpointResolverOverride( | |
| llvm::StringRef class_name, uint64_t type_mask, | |
| StructuredData::DictionarySP args_data_sp, llvm::StringRef description) { | |
| if (class_name.empty()) | |
| return llvm::createStringError(llvm::inconvertibleErrorCode(), | |
| "empty class name"); | |
| if (!BreakpointResolver::TypeMaskIsValid(type_mask)) | |
| return llvm::createStringErrorV( | |
| llvm::inconvertibleErrorCode(), | |
| "invalid breakpoint type mask: {0}, should be composed of the " | |
| "elements of the BreakpointResolverType enum.", | |
| type_mask); | |
| StructuredDataImpl impl; | |
| impl.SetObjectSP(args_data_sp); | |
| BreakpointResolverOverrideUP new_override_up( | |
| new ScriptedBreakpointResolverOverride(*this, std::string(description), | |
| type_mask, std::string(class_name), | |
| impl)); | |
| llvm::Error error = new_override_up->Validate(); | |
| if (error) | |
| return error; | |
| return AddBreakpointResolverOverride(std::move(new_override_up)); | |
| } | |
| std::string Target::BreakpointResolverOverride::DescribeTypeMask() { | |
| return BreakpointResolver::DescribeMask(m_type_mask); | |
| } | |
| void Target::DescribeBreakpointOverrides(Stream &stream, | |
| std::vector<lldb::user_id_t> &idxs, | |
| uint32_t output_width, | |
| bool use_color) { | |
| if (m_breakpoint_overrides.size() == 0) { | |
| stream << "No overrides.\n"; | |
| return; | |
| } | |
| bool empty = idxs.empty(); | |
| bool print_first = true; | |
| for (auto const &elem : m_breakpoint_overrides) { | |
| auto idx_pos = llvm::find(idxs, elem.first); | |
| if (empty || idx_pos != idxs.end()) { | |
| if (print_first) { | |
| ansi::OutputWordWrappedLines(stream, "ID Mask Description\n", | |
| output_width, use_color); | |
| ansi::OutputWordWrappedLines(stream, "---- ------ -----------\n", | |
| output_width, use_color); | |
| print_first = false; | |
| } | |
| auto content = llvm::formatv("{0,4} {1,6} {2}\n", elem.first, | |
| elem.second->DescribeTypeMask(), | |
| elem.second->GetDescription()) | |
| .str(); | |
| ansi::OutputWordWrappedLines(stream, content, output_width, use_color); | |
| if (!empty) | |
| idxs.erase(idx_pos); | |
| } | |
| } | |
| } | |
| bool Target::ProcessIsValid() { | |
| return (m_process_sp && m_process_sp->IsAlive()); | |
| } | |
| lldb::BreakpointResolverSP | |
| Target::CheckBreakpointOverrides(lldb::BreakpointResolverSP original_sp) { | |
| for (auto const &elem : m_breakpoint_overrides) { | |
| if (!original_sp->ResolverTyInMask(elem.second->GetTypeMask())) | |
| continue; | |
| if (lldb::BreakpointResolverSP overriden_sp = | |
| elem.second->CheckForOverride(*this, original_sp)) | |
| return overriden_sp; | |
| } | |
| return {}; | |
| } | |
| static bool CheckIfWatchpointsSupported(Target *target, Status &error) { | |
| std::optional<uint32_t> num_supported_hardware_watchpoints = | |
| target->GetProcessSP()->GetWatchpointSlotCount(); | |
| // If unable to determine the # of watchpoints available, | |
| // assume they are supported. | |
| if (!num_supported_hardware_watchpoints) | |
| return true; | |
| if (*num_supported_hardware_watchpoints == 0) { | |
| error = Status::FromErrorStringWithFormat( | |
| "Target supports (%u) hardware watchpoint slots.\n", | |
| *num_supported_hardware_watchpoints); | |
| return false; | |
| } | |
| return true; | |
| } | |
| // See also Watchpoint::SetWatchpointType(uint32_t type) and the | |
| // OptionGroupWatchpoint::WatchType enum type. | |
| WatchpointSP Target::CreateWatchpoint(lldb::addr_t addr, size_t size, | |
| const CompilerType *type, uint32_t kind, | |
| Status &error) { | |
| Log *log = GetLog(LLDBLog::Watchpoints); | |
| LLDB_LOGF(log, | |
| "Target::%s (addr = 0x%8.8" PRIx64 " size = %" PRIu64 | |
| " type = %u)\n", | |
| __FUNCTION__, addr, (uint64_t)size, kind); | |
| WatchpointSP wp_sp; | |
| if (!ProcessIsValid()) { | |
| error = Status::FromErrorString("process is not alive"); | |
| return wp_sp; | |
| } | |
| if (addr == LLDB_INVALID_ADDRESS || size == 0) { | |
| if (size == 0) | |
| error = Status::FromErrorString( | |
| "cannot set a watchpoint with watch_size of 0"); | |
| else | |
| error = Status::FromErrorStringWithFormat( | |
| "invalid watch address: %" PRIu64, addr); | |
| return wp_sp; | |
| } | |
| if (!LLDB_WATCH_TYPE_IS_VALID(kind)) { | |
| error = | |
| Status::FromErrorStringWithFormat("invalid watchpoint type: %d", kind); | |
| } | |
| if (!CheckIfWatchpointsSupported(this, error)) | |
| return wp_sp; | |
| // Currently we only support one watchpoint per address, with total number of | |
| // watchpoints limited by the hardware which the inferior is running on. | |
| // Grab the list mutex while doing operations. | |
| const bool notify = false; // Don't notify about all the state changes we do | |
| // on creating the watchpoint. | |
| // Mask off ignored bits from watchpoint address. | |
| if (ABISP abi = m_process_sp->GetABI()) | |
| addr = abi->FixDataAddress(addr); | |
| // LWP_TODO this sequence is looking for an existing watchpoint | |
| // at the exact same user-specified address, disables the new one | |
| // if addr/size/type match. If type/size differ, disable old one. | |
| // This isn't correct, we need both watchpoints to use a shared | |
| // WatchpointResource in the target, and expand the WatchpointResource | |
| // to handle the needs of both Watchpoints. | |
| // Also, even if the addresses don't match, they may need to be | |
| // supported by the same WatchpointResource, e.g. a watchpoint | |
| // watching 1 byte at 0x102 and a watchpoint watching 1 byte at 0x103. | |
| // They're in the same word and must be watched by a single hardware | |
| // watchpoint register. | |
| std::unique_lock<std::recursive_mutex> lock; | |
| this->GetWatchpointList().GetListMutex(lock); | |
| WatchpointSP matched_sp = m_watchpoint_list.FindByAddress(addr); | |
| if (matched_sp) { | |
| size_t old_size = matched_sp->GetByteSize(); | |
| uint32_t old_type = | |
| (matched_sp->WatchpointRead() ? LLDB_WATCH_TYPE_READ : 0) | | |
| (matched_sp->WatchpointWrite() ? LLDB_WATCH_TYPE_WRITE : 0) | | |
| (matched_sp->WatchpointModify() ? LLDB_WATCH_TYPE_MODIFY : 0); | |
| // Return the existing watchpoint if both size and type match. | |
| if (size == old_size && kind == old_type) { | |
| wp_sp = matched_sp; | |
| wp_sp->SetEnabled(false, notify); | |
| } else { | |
| // Nil the matched watchpoint; we will be creating a new one. | |
| m_process_sp->DisableWatchpoint(matched_sp, notify); | |
| m_watchpoint_list.Remove(matched_sp->GetID(), true); | |
| } | |
| } | |
| if (!wp_sp) { | |
| wp_sp = std::make_shared<Watchpoint>(*this, addr, size, type); | |
| wp_sp->SetWatchpointType(kind, notify); | |
| m_watchpoint_list.Add(wp_sp, true); | |
| } | |
| error = m_process_sp->EnableWatchpoint(wp_sp, notify); | |
| LLDB_LOGF(log, "Target::%s (creation of watchpoint %s with id = %u)\n", | |
| __FUNCTION__, error.Success() ? "succeeded" : "failed", | |
| wp_sp->GetID()); | |
| if (error.Fail()) { | |
| // Enabling the watchpoint on the device side failed. Remove the said | |
| // watchpoint from the list maintained by the target instance. | |
| m_watchpoint_list.Remove(wp_sp->GetID(), true); | |
| wp_sp.reset(); | |
| } else | |
| m_last_created_watchpoint = wp_sp; | |
| return wp_sp; | |
| } | |
| void Target::RemoveAllowedBreakpoints() { | |
| Log *log = GetLog(LLDBLog::Breakpoints); | |
| LLDB_LOGF(log, "Target::%s \n", __FUNCTION__); | |
| m_breakpoint_list.RemoveAllowed(true); | |
| m_last_created_breakpoint.reset(); | |
| } | |
| void Target::RemoveAllBreakpoints(bool internal_also) { | |
| Log *log = GetLog(LLDBLog::Breakpoints); | |
| LLDB_LOGF(log, "Target::%s (internal_also = %s)\n", __FUNCTION__, | |
| internal_also ? "yes" : "no"); | |
| m_breakpoint_list.RemoveAll(true); | |
| if (internal_also) | |
| m_internal_breakpoint_list.RemoveAll(false); | |
| m_last_created_breakpoint.reset(); | |
| } | |
| void Target::DisableAllBreakpoints(bool internal_also) { | |
| Log *log = GetLog(LLDBLog::Breakpoints); | |
| LLDB_LOGF(log, "Target::%s (internal_also = %s)\n", __FUNCTION__, | |
| internal_also ? "yes" : "no"); | |
| m_breakpoint_list.SetEnabledAll(false); | |
| if (internal_also) | |
| m_internal_breakpoint_list.SetEnabledAll(false); | |
| } | |
| void Target::DisableAllowedBreakpoints() { | |
| Log *log = GetLog(LLDBLog::Breakpoints); | |
| LLDB_LOGF(log, "Target::%s", __FUNCTION__); | |
| m_breakpoint_list.SetEnabledAllowed(false); | |
| } | |
| void Target::EnableAllBreakpoints(bool internal_also) { | |
| Log *log = GetLog(LLDBLog::Breakpoints); | |
| LLDB_LOGF(log, "Target::%s (internal_also = %s)\n", __FUNCTION__, | |
| internal_also ? "yes" : "no"); | |
| m_breakpoint_list.SetEnabledAll(true); | |
| if (internal_also) | |
| m_internal_breakpoint_list.SetEnabledAll(true); | |
| } | |
| void Target::EnableAllowedBreakpoints() { | |
| Log *log = GetLog(LLDBLog::Breakpoints); | |
| LLDB_LOGF(log, "Target::%s", __FUNCTION__); | |
| m_breakpoint_list.SetEnabledAllowed(true); | |
| } | |
| bool Target::RemoveBreakpointByID(break_id_t break_id) { | |
| Log *log = GetLog(LLDBLog::Breakpoints); | |
| LLDB_LOGF(log, "Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, | |
| break_id, LLDB_BREAK_ID_IS_INTERNAL(break_id) ? "yes" : "no"); | |
| if (DisableBreakpointByID(break_id)) { | |
| if (LLDB_BREAK_ID_IS_INTERNAL(break_id)) | |
| m_internal_breakpoint_list.Remove(break_id, false); | |
| else { | |
| if (m_last_created_breakpoint) { | |
| if (m_last_created_breakpoint->GetID() == break_id) | |
| m_last_created_breakpoint.reset(); | |
| } | |
| m_breakpoint_list.Remove(break_id, true); | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool Target::DisableBreakpointByID(break_id_t break_id) { | |
| Log *log = GetLog(LLDBLog::Breakpoints); | |
| LLDB_LOGF(log, "Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, | |
| break_id, LLDB_BREAK_ID_IS_INTERNAL(break_id) ? "yes" : "no"); | |
| BreakpointSP bp_sp; | |
| if (LLDB_BREAK_ID_IS_INTERNAL(break_id)) | |
| bp_sp = m_internal_breakpoint_list.FindBreakpointByID(break_id); | |
| else | |
| bp_sp = m_breakpoint_list.FindBreakpointByID(break_id); | |
| if (bp_sp) { | |
| bp_sp->SetEnabled(false); | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool Target::EnableBreakpointByID(break_id_t break_id) { | |
| Log *log = GetLog(LLDBLog::Breakpoints); | |
| LLDB_LOGF(log, "Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, | |
| break_id, LLDB_BREAK_ID_IS_INTERNAL(break_id) ? "yes" : "no"); | |
| BreakpointSP bp_sp; | |
| if (LLDB_BREAK_ID_IS_INTERNAL(break_id)) | |
| bp_sp = m_internal_breakpoint_list.FindBreakpointByID(break_id); | |
| else | |
| bp_sp = m_breakpoint_list.FindBreakpointByID(break_id); | |
| if (bp_sp) { | |
| bp_sp->SetEnabled(true); | |
| return true; | |
| } | |
| return false; | |
| } | |
| void Target::ResetBreakpointHitCounts() { | |
| GetBreakpointList().ResetHitCounts(); | |
| } | |
| Status Target::SerializeBreakpointsToFile(const FileSpec &file, | |
| const BreakpointIDList &bp_ids, | |
| bool append) { | |
| Status error; | |
| if (!file) { | |
| error = Status::FromErrorString("Invalid FileSpec."); | |
| return error; | |
| } | |
| std::string path(file.GetPath()); | |
| StructuredData::ObjectSP input_data_sp; | |
| StructuredData::ArraySP break_store_sp; | |
| StructuredData::Array *break_store_ptr = nullptr; | |
| if (append) { | |
| input_data_sp = StructuredData::ParseJSONFromFile(file, error); | |
| if (error.Success()) { | |
| break_store_ptr = input_data_sp->GetAsArray(); | |
| if (!break_store_ptr) { | |
| error = Status::FromErrorStringWithFormat( | |
| "Tried to append to invalid input file %s", path.c_str()); | |
| return error; | |
| } | |
| } | |
| } | |
| if (!break_store_ptr) { | |
| break_store_sp = std::make_shared<StructuredData::Array>(); | |
| break_store_ptr = break_store_sp.get(); | |
| } | |
| StreamFile out_file(path.c_str(), | |
| File::eOpenOptionTruncate | File::eOpenOptionWriteOnly | | |
| File::eOpenOptionCanCreate | | |
| File::eOpenOptionCloseOnExec, | |
| lldb::eFilePermissionsFileDefault); | |
| if (!out_file.GetFile().IsValid()) { | |
| error = Status::FromErrorStringWithFormat("Unable to open output file: %s.", | |
| path.c_str()); | |
| return error; | |
| } | |
| std::unique_lock<std::recursive_mutex> lock; | |
| GetBreakpointList().GetListMutex(lock); | |
| if (bp_ids.GetSize() == 0) { | |
| const BreakpointList &breakpoints = GetBreakpointList(); | |
| size_t num_breakpoints = breakpoints.GetSize(); | |
| for (size_t i = 0; i < num_breakpoints; i++) { | |
| Breakpoint *bp = breakpoints.GetBreakpointAtIndex(i).get(); | |
| StructuredData::ObjectSP bkpt_save_sp = bp->SerializeToStructuredData(); | |
| // If a breakpoint can't serialize it, just ignore it for now: | |
| if (bkpt_save_sp) | |
| break_store_ptr->AddItem(bkpt_save_sp); | |
| } | |
| } else { | |
| std::unordered_set<lldb::break_id_t> processed_bkpts; | |
| const size_t count = bp_ids.GetSize(); | |
| for (size_t i = 0; i < count; ++i) { | |
| BreakpointID cur_bp_id = bp_ids.GetBreakpointIDAtIndex(i); | |
| lldb::break_id_t bp_id = cur_bp_id.GetBreakpointID(); | |
| if (bp_id != LLDB_INVALID_BREAK_ID) { | |
| // Only do each breakpoint once: | |
| std::pair<std::unordered_set<lldb::break_id_t>::iterator, bool> | |
| insert_result = processed_bkpts.insert(bp_id); | |
| if (!insert_result.second) | |
| continue; | |
| Breakpoint *bp = GetBreakpointByID(bp_id).get(); | |
| StructuredData::ObjectSP bkpt_save_sp = bp->SerializeToStructuredData(); | |
| // If the user explicitly asked to serialize a breakpoint, and we | |
| // can't, then raise an error: | |
| if (!bkpt_save_sp) { | |
| error = Status::FromErrorStringWithFormat( | |
| "Unable to serialize breakpoint %d", bp_id); | |
| return error; | |
| } | |
| break_store_ptr->AddItem(bkpt_save_sp); | |
| } | |
| } | |
| } | |
| break_store_ptr->Dump(out_file, false); | |
| out_file.PutChar('\n'); | |
| return error; | |
| } | |
| Status Target::CreateBreakpointsFromFile(const FileSpec &file, | |
| BreakpointIDList &new_bps) { | |
| std::vector<std::string> no_names; | |
| return CreateBreakpointsFromFile(file, no_names, new_bps); | |
| } | |
| Status Target::CreateBreakpointsFromFile(const FileSpec &file, | |
| std::vector<std::string> &names, | |
| BreakpointIDList &new_bps) { | |
| std::unique_lock<std::recursive_mutex> lock; | |
| GetBreakpointList().GetListMutex(lock); | |
| Status error; | |
| StructuredData::ObjectSP input_data_sp = | |
| StructuredData::ParseJSONFromFile(file, error); | |
| if (!error.Success()) { | |
| return error; | |
| } else if (!input_data_sp || !input_data_sp->IsValid()) { | |
| error = Status::FromErrorStringWithFormat( | |
| "Invalid JSON from input file: %s.", file.GetPath().c_str()); | |
| return error; | |
| } | |
| StructuredData::Array *bkpt_array = input_data_sp->GetAsArray(); | |
| if (!bkpt_array) { | |
| error = Status::FromErrorStringWithFormat( | |
| "Invalid breakpoint data from input file: %s.", file.GetPath().c_str()); | |
| return error; | |
| } | |
| size_t num_bkpts = bkpt_array->GetSize(); | |
| size_t num_names = names.size(); | |
| for (size_t i = 0; i < num_bkpts; i++) { | |
| StructuredData::ObjectSP bkpt_object_sp = bkpt_array->GetItemAtIndex(i); | |
| // Peel off the breakpoint key, and feed the rest to the Breakpoint: | |
| StructuredData::Dictionary *bkpt_dict = bkpt_object_sp->GetAsDictionary(); | |
| if (!bkpt_dict) { | |
| error = Status::FromErrorStringWithFormat( | |
| "Invalid breakpoint data for element %zu from input file: %s.", i, | |
| file.GetPath().c_str()); | |
| return error; | |
| } | |
| StructuredData::ObjectSP bkpt_data_sp = | |
| bkpt_dict->GetValueForKey(Breakpoint::GetSerializationKey()); | |
| if (num_names && | |
| !Breakpoint::SerializedBreakpointMatchesNames(bkpt_data_sp, names)) | |
| continue; | |
| BreakpointSP bkpt_sp = Breakpoint::CreateFromStructuredData( | |
| shared_from_this(), bkpt_data_sp, error); | |
| if (!error.Success()) { | |
| error = Status::FromErrorStringWithFormat( | |
| "Error restoring breakpoint %zu from %s: %s.", i, | |
| file.GetPath().c_str(), error.AsCString()); | |
| return error; | |
| } | |
| new_bps.AddBreakpointID(BreakpointID(bkpt_sp->GetID())); | |
| } | |
| return error; | |
| } | |
| // The flag 'end_to_end', default to true, signifies that the operation is | |
| // performed end to end, for both the debugger and the debuggee. | |
| // Assumption: Caller holds the list mutex lock for m_watchpoint_list for end | |
| // to end operations. | |
| bool Target::RemoveAllWatchpoints(bool end_to_end) { | |
| Log *log = GetLog(LLDBLog::Watchpoints); | |
| LLDB_LOGF(log, "Target::%s\n", __FUNCTION__); | |
| if (!end_to_end) { | |
| m_watchpoint_list.RemoveAll(true); | |
| return true; | |
| } | |
| // Otherwise, it's an end to end operation. | |
| if (!ProcessIsValid()) | |
| return false; | |
| for (WatchpointSP wp_sp : m_watchpoint_list.Watchpoints()) { | |
| if (!wp_sp) | |
| return false; | |
| Status rc = m_process_sp->DisableWatchpoint(wp_sp); | |
| if (rc.Fail()) | |
| return false; | |
| } | |
| m_watchpoint_list.RemoveAll(true); | |
| m_last_created_watchpoint.reset(); | |
| return true; // Success! | |
| } | |
| // Assumption: Caller holds the list mutex lock for m_watchpoint_list for end | |
| // to end operations. | |
| bool Target::DisableAllWatchpoints(bool end_to_end) { | |
| Log *log = GetLog(LLDBLog::Watchpoints); | |
| LLDB_LOGF(log, "Target::%s\n", __FUNCTION__); | |
| if (!end_to_end) { | |
| m_watchpoint_list.SetEnabledAll(false); | |
| return true; | |
| } | |
| // Otherwise, it's an end to end operation. | |
| if (!ProcessIsValid()) | |
| return false; | |
| for (WatchpointSP wp_sp : m_watchpoint_list.Watchpoints()) { | |
| if (!wp_sp) | |
| return false; | |
| Status rc = m_process_sp->DisableWatchpoint(wp_sp); | |
| if (rc.Fail()) | |
| return false; | |
| } | |
| return true; // Success! | |
| } | |
| // Assumption: Caller holds the list mutex lock for m_watchpoint_list for end | |
| // to end operations. | |
| bool Target::EnableAllWatchpoints(bool end_to_end) { | |
| Log *log = GetLog(LLDBLog::Watchpoints); | |
| LLDB_LOGF(log, "Target::%s\n", __FUNCTION__); | |
| if (!end_to_end) { | |
| m_watchpoint_list.SetEnabledAll(true); | |
| return true; | |
| } | |
| // Otherwise, it's an end to end operation. | |
| if (!ProcessIsValid()) | |
| return false; | |
| for (WatchpointSP wp_sp : m_watchpoint_list.Watchpoints()) { | |
| if (!wp_sp) | |
| return false; | |
| Status rc = m_process_sp->EnableWatchpoint(wp_sp); | |
| if (rc.Fail()) | |
| return false; | |
| } | |
| return true; // Success! | |
| } | |
| // Assumption: Caller holds the list mutex lock for m_watchpoint_list. | |
| bool Target::ClearAllWatchpointHitCounts() { | |
| Log *log = GetLog(LLDBLog::Watchpoints); | |
| LLDB_LOGF(log, "Target::%s\n", __FUNCTION__); | |
| for (WatchpointSP wp_sp : m_watchpoint_list.Watchpoints()) { | |
| if (!wp_sp) | |
| return false; | |
| wp_sp->ResetHitCount(); | |
| } | |
| return true; // Success! | |
| } | |
| // Assumption: Caller holds the list mutex lock for m_watchpoint_list. | |
| bool Target::ClearAllWatchpointHistoricValues() { | |
| Log *log = GetLog(LLDBLog::Watchpoints); | |
| LLDB_LOGF(log, "Target::%s\n", __FUNCTION__); | |
| for (WatchpointSP wp_sp : m_watchpoint_list.Watchpoints()) { | |
| if (!wp_sp) | |
| return false; | |
| wp_sp->ResetHistoricValues(); | |
| } | |
| return true; // Success! | |
| } | |
| // Assumption: Caller holds the list mutex lock for m_watchpoint_list during | |
| // these operations. | |
| bool Target::IgnoreAllWatchpoints(uint32_t ignore_count) { | |
| Log *log = GetLog(LLDBLog::Watchpoints); | |
| LLDB_LOGF(log, "Target::%s\n", __FUNCTION__); | |
| if (!ProcessIsValid()) | |
| return false; | |
| for (WatchpointSP wp_sp : m_watchpoint_list.Watchpoints()) { | |
| if (!wp_sp) | |
| return false; | |
| wp_sp->SetIgnoreCount(ignore_count); | |
| } | |
| return true; // Success! | |
| } | |
| // Assumption: Caller holds the list mutex lock for m_watchpoint_list. | |
| bool Target::DisableWatchpointByID(lldb::watch_id_t watch_id) { | |
| Log *log = GetLog(LLDBLog::Watchpoints); | |
| LLDB_LOGF(log, "Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id); | |
| if (!ProcessIsValid()) | |
| return false; | |
| WatchpointSP wp_sp = m_watchpoint_list.FindByID(watch_id); | |
| if (wp_sp) { | |
| Status rc = m_process_sp->DisableWatchpoint(wp_sp); | |
| if (rc.Success()) | |
| return true; | |
| // Else, fallthrough. | |
| } | |
| return false; | |
| } | |
| // Assumption: Caller holds the list mutex lock for m_watchpoint_list. | |
| bool Target::EnableWatchpointByID(lldb::watch_id_t watch_id) { | |
| Log *log = GetLog(LLDBLog::Watchpoints); | |
| LLDB_LOGF(log, "Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id); | |
| if (!ProcessIsValid()) | |
| return false; | |
| WatchpointSP wp_sp = m_watchpoint_list.FindByID(watch_id); | |
| if (wp_sp) { | |
| Status rc = m_process_sp->EnableWatchpoint(wp_sp); | |
| if (rc.Success()) | |
| return true; | |
| // Else, fallthrough. | |
| } | |
| return false; | |
| } | |
| // Assumption: Caller holds the list mutex lock for m_watchpoint_list. | |
| bool Target::RemoveWatchpointByID(lldb::watch_id_t watch_id) { | |
| Log *log = GetLog(LLDBLog::Watchpoints); | |
| LLDB_LOGF(log, "Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id); | |
| WatchpointSP watch_to_remove_sp = m_watchpoint_list.FindByID(watch_id); | |
| if (watch_to_remove_sp == m_last_created_watchpoint) | |
| m_last_created_watchpoint.reset(); | |
| if (DisableWatchpointByID(watch_id)) { | |
| m_watchpoint_list.Remove(watch_id, true); | |
| return true; | |
| } | |
| return false; | |
| } | |
| // Assumption: Caller holds the list mutex lock for m_watchpoint_list. | |
| bool Target::IgnoreWatchpointByID(lldb::watch_id_t watch_id, | |
| uint32_t ignore_count) { | |
| Log *log = GetLog(LLDBLog::Watchpoints); | |
| LLDB_LOGF(log, "Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id); | |
| if (!ProcessIsValid()) | |
| return false; | |
| WatchpointSP wp_sp = m_watchpoint_list.FindByID(watch_id); | |
| if (wp_sp) { | |
| wp_sp->SetIgnoreCount(ignore_count); | |
| return true; | |
| } | |
| return false; | |
| } | |
| ModuleSP Target::GetExecutableModule() { | |
| std::lock_guard<std::recursive_mutex> lock(m_images.GetMutex()); | |
| // Search for the first executable in the module list. | |
| for (ModuleSP module_sp : m_images.ModulesNoLocking()) { | |
| lldb_private::ObjectFile *obj = module_sp->GetObjectFile(); | |
| if (obj == nullptr) | |
| continue; | |
| if (obj->GetType() == ObjectFile::Type::eTypeExecutable) | |
| return module_sp; | |
| } | |
| // If there is none, fall back to the module marked as the executable. | |
| ModuleSP executable_sp = m_executable_module_wp.lock(); | |
| if (executable_sp && | |
| m_images.GetIndexForModule(executable_sp.get()) != LLDB_INVALID_INDEX32) | |
| return executable_sp; | |
| return nullptr; | |
| } | |
| Module *Target::GetExecutableModulePointer() { | |
| return GetExecutableModule().get(); | |
| } | |
| void Target::MarkExecutableModule(const ModuleSP &module_sp) { | |
| std::lock_guard<std::recursive_mutex> lock(m_images.GetMutex()); | |
| m_executable_module_wp = module_sp; | |
| } | |
| void Target::ClearModules(bool delete_locations) { | |
| ModulesDidUnload(m_images, delete_locations); | |
| m_section_load_history.Clear(); | |
| m_images.Clear(); | |
| MarkExecutableModule(nullptr); | |
| m_scratch_type_system_map.Clear(); | |
| } | |
| void Target::DidExec() { | |
| // When a process exec's we need to know about it so we can do some cleanup. | |
| m_breakpoint_list.RemoveInvalidLocations(m_arch.GetSpec()); | |
| m_internal_breakpoint_list.RemoveInvalidLocations(m_arch.GetSpec()); | |
| } | |
| void Target::RebuildModuleListWithExecutable( | |
| ModuleSP &executable_sp, LoadDependentFiles load_dependent_files) { | |
| telemetry::ScopedDispatcher<telemetry::ExecutableModuleInfo> helper( | |
| &m_debugger); | |
| Log *log = GetLog(LLDBLog::Target); | |
| ClearModules(false); | |
| if (executable_sp) { | |
| lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; | |
| if (ProcessSP proc = GetProcessSP()) | |
| pid = proc->GetID(); | |
| helper.DispatchNow([&](telemetry::ExecutableModuleInfo *info) { | |
| info->exec_mod = executable_sp; | |
| info->uuid = executable_sp->GetUUID(); | |
| info->pid = pid; | |
| info->triple = executable_sp->GetArchitecture().GetTriple().getTriple(); | |
| info->is_start_entry = true; | |
| }); | |
| helper.DispatchOnExit([&, pid](telemetry::ExecutableModuleInfo *info) { | |
| info->exec_mod = executable_sp; | |
| info->uuid = executable_sp->GetUUID(); | |
| info->pid = pid; | |
| }); | |
| ElapsedTime elapsed(m_stats.GetCreateTime()); | |
| LLDB_SCOPED_TIMERF( | |
| "Target::RebuildModuleListWithExecutable (executable = '%s')", | |
| executable_sp->GetFileSpec().GetPath().c_str()); | |
| const bool notify = true; | |
| // Mark it before appending, because the append notifies observers that | |
| // may ask for the executable. | |
| MarkExecutableModule(executable_sp); | |
| m_images.Append(executable_sp, | |
| notify); // The first image is our executable file | |
| // If we haven't set an architecture yet, reset our architecture based on | |
| // what we found in the executable module. | |
| if (!m_arch.GetSpec().IsValid()) { | |
| m_arch = executable_sp->GetArchitecture(); | |
| LLDB_LOG(log, | |
| "Target::RebuildModuleListWithExecutable setting architecture " | |
| "to {0} ({1}) " | |
| "based on executable file", | |
| m_arch.GetSpec().GetArchitectureName(), | |
| m_arch.GetSpec().GetTriple().getTriple()); | |
| } | |
| ObjectFile *executable_objfile = executable_sp->GetObjectFile(); | |
| bool load_dependents = true; | |
| switch (load_dependent_files) { | |
| case eLoadDependentsDefault: | |
| load_dependents = executable_sp->IsExecutable(); | |
| break; | |
| case eLoadDependentsYes: | |
| load_dependents = true; | |
| break; | |
| case eLoadDependentsNo: | |
| load_dependents = false; | |
| break; | |
| } | |
| if (executable_objfile && load_dependents) { | |
| // FileSpecList is not thread safe and needs to be synchronized. | |
| FileSpecList dependent_files; | |
| std::mutex dependent_files_mutex; | |
| // ModuleList is thread safe. | |
| ModuleList added_modules; | |
| auto GetDependentModules = [&](FileSpec dependent_file_spec) { | |
| FileSpec platform_dependent_file_spec; | |
| if (m_platform_sp) | |
| m_platform_sp->GetFileWithUUID(dependent_file_spec, nullptr, | |
| platform_dependent_file_spec); | |
| else | |
| platform_dependent_file_spec = dependent_file_spec; | |
| ModuleSpec module_spec(platform_dependent_file_spec, m_arch.GetSpec()); | |
| ModuleSP image_module_sp( | |
| GetOrCreateModule(module_spec, false /* notify */)); | |
| if (image_module_sp) { | |
| added_modules.AppendIfNeeded(image_module_sp, false); | |
| ObjectFile *objfile = image_module_sp->GetObjectFile(); | |
| if (objfile) { | |
| // Create a local copy of the dependent file list so we don't have | |
| // to lock for the whole duration of GetDependentModules. | |
| FileSpecList dependent_files_copy; | |
| { | |
| std::lock_guard<std::mutex> guard(dependent_files_mutex); | |
| dependent_files_copy = dependent_files; | |
| } | |
| // Remember the size of the local copy so we can append only the | |
| // modules that have been added by GetDependentModules. | |
| const size_t previous_dependent_files = | |
| dependent_files_copy.GetSize(); | |
| objfile->GetDependentModules(dependent_files_copy); | |
| { | |
| std::lock_guard<std::mutex> guard(dependent_files_mutex); | |
| for (size_t i = previous_dependent_files; | |
| i < dependent_files_copy.GetSize(); ++i) | |
| dependent_files.AppendIfUnique( | |
| dependent_files_copy.GetFileSpecAtIndex(i)); | |
| } | |
| } | |
| } | |
| }; | |
| executable_objfile->GetDependentModules(dependent_files); | |
| llvm::ThreadPoolTaskGroup task_group(Debugger::GetThreadPool()); | |
| for (uint32_t i = 0; i < dependent_files.GetSize(); i++) { | |
| // Process all currently known dependencies in parallel in the innermost | |
| // loop. This may create newly discovered dependencies to be appended to | |
| // dependent_files. We'll deal with these files during the next | |
| // iteration of the outermost loop. | |
| { | |
| std::lock_guard<std::mutex> guard(dependent_files_mutex); | |
| for (; i < dependent_files.GetSize(); i++) | |
| task_group.async(GetDependentModules, | |
| dependent_files.GetFileSpecAtIndex(i)); | |
| } | |
| task_group.wait(); | |
| } | |
| ModulesDidLoad(added_modules); | |
| } | |
| } | |
| } | |
| bool Target::SetArchitecture(const ArchSpec &arch_spec, bool set_platform, | |
| bool merge) { | |
| Log *log = GetLog(LLDBLog::Target); | |
| bool missing_local_arch = !m_arch.GetSpec().IsValid(); | |
| bool replace_local_arch = true; | |
| bool compatible_local_arch = false; | |
| ArchSpec other(arch_spec); | |
| // Changing the architecture might mean that the currently selected platform | |
| // isn't compatible. Set the platform correctly if we are asked to do so, | |
| // otherwise assume the user will set the platform manually. | |
| if (set_platform) { | |
| if (other.IsValid()) { | |
| auto platform_sp = GetPlatform(); | |
| if (!platform_sp || !platform_sp->IsCompatibleArchitecture( | |
| other, {}, ArchSpec::CompatibleMatch, nullptr)) { | |
| ArchSpec platform_arch; | |
| if (PlatformSP arch_platform_sp = | |
| GetDebugger().GetPlatformList().GetOrCreate(other, {}, | |
| &platform_arch)) { | |
| arch_platform_sp->SetLocateModuleCallback( | |
| platform_sp->GetLocateModuleCallback()); | |
| SetPlatform(arch_platform_sp); | |
| if (platform_arch.IsValid()) | |
| other = platform_arch; | |
| } | |
| } | |
| } | |
| } | |
| if (!missing_local_arch) { | |
| if (merge && m_arch.GetSpec().IsCompatibleMatch(arch_spec)) { | |
| other.MergeFrom(m_arch.GetSpec()); | |
| if (m_arch.GetSpec().IsCompatibleMatch(other)) { | |
| compatible_local_arch = true; | |
| if (m_arch.GetSpec().GetTriple() == other.GetTriple()) | |
| replace_local_arch = false; | |
| } | |
| } | |
| } | |
| if (compatible_local_arch || missing_local_arch) { | |
| // If we haven't got a valid arch spec, or the architectures are compatible | |
| // update the architecture, unless the one we already have is more | |
| // specified | |
| if (replace_local_arch) | |
| m_arch = other; | |
| LLDB_LOG(log, | |
| "Target::SetArchitecture merging compatible arch; arch " | |
| "is now {0} ({1})", | |
| m_arch.GetSpec().GetArchitectureName(), | |
| m_arch.GetSpec().GetTriple().getTriple()); | |
| return true; | |
| } | |
| // If we have an executable file, try to reset the executable to the desired | |
| // architecture | |
| LLDB_LOGF( | |
| log, | |
| "Target::SetArchitecture changing architecture to %s (%s) from %s (%s)", | |
| arch_spec.GetArchitectureName(), | |
| arch_spec.GetTriple().getTriple().c_str(), | |
| m_arch.GetSpec().GetArchitectureName(), | |
| m_arch.GetSpec().GetTriple().getTriple().c_str()); | |
| m_arch = other; | |
| ModuleSP executable_sp = GetExecutableModule(); | |
| ClearModules(true); | |
| // Need to do something about unsetting breakpoints. | |
| if (executable_sp) { | |
| LLDB_LOGF(log, | |
| "Target::SetArchitecture Trying to select executable file " | |
| "architecture %s (%s)", | |
| arch_spec.GetArchitectureName(), | |
| arch_spec.GetTriple().getTriple().c_str()); | |
| ModuleSpec module_spec(executable_sp->GetFileSpec(), other); | |
| module_spec.SetTarget(shared_from_this()); | |
| Status error = ModuleList::GetSharedModule(module_spec, executable_sp, | |
| nullptr, nullptr); | |
| if (!error.Fail() && executable_sp) { | |
| RebuildModuleListWithExecutable(executable_sp, eLoadDependentsYes); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool Target::MergeArchitecture(const ArchSpec &arch_spec) { | |
| Log *log = GetLog(LLDBLog::Target); | |
| if (arch_spec.IsValid()) { | |
| if (m_arch.GetSpec().IsCompatibleMatch(arch_spec)) { | |
| // The current target arch is compatible with "arch_spec", see if we can | |
| // improve our current architecture using bits from "arch_spec" | |
| LLDB_LOGF(log, | |
| "Target::MergeArchitecture target has arch %s, merging with " | |
| "arch %s", | |
| m_arch.GetSpec().GetTriple().getTriple().c_str(), | |
| arch_spec.GetTriple().getTriple().c_str()); | |
| // Merge bits from arch_spec into "merged_arch" and set our architecture | |
| ArchSpec merged_arch(m_arch.GetSpec()); | |
| merged_arch.MergeFrom(arch_spec); | |
| return SetArchitecture(merged_arch); | |
| } else { | |
| // The new architecture is different, we just need to replace it | |
| return SetArchitecture(arch_spec); | |
| } | |
| } | |
| return false; | |
| } | |
| void Target::NotifyWillClearList(const ModuleList &module_list) {} | |
| void Target::NotifyModuleAdded(const ModuleList &module_list, | |
| const ModuleSP &module_sp) { | |
| // A module is being added to this target for the first time | |
| if (m_valid) { | |
| ModuleList my_module_list; | |
| my_module_list.Append(module_sp); | |
| ModulesDidLoad(my_module_list); | |
| } | |
| } | |
| void Target::NotifyModuleRemoved(const ModuleList &module_list, | |
| const ModuleSP &module_sp) { | |
| // A module is being removed from this target. | |
| if (m_valid) { | |
| ModuleList my_module_list; | |
| my_module_list.Append(module_sp); | |
| ModulesDidUnload(my_module_list, false); | |
| } | |
| } | |
| void Target::NotifyModuleUpdated(const ModuleList &module_list, | |
| const ModuleSP &old_module_sp, | |
| const ModuleSP &new_module_sp) { | |
| // A module is replacing an already added module | |
| if (m_valid) { | |
| m_breakpoint_list.UpdateBreakpointsWhenModuleIsReplaced(old_module_sp, | |
| new_module_sp); | |
| m_internal_breakpoint_list.UpdateBreakpointsWhenModuleIsReplaced( | |
| old_module_sp, new_module_sp); | |
| } | |
| } | |
| void Target::NotifyModulesRemoved(lldb_private::ModuleList &module_list) { | |
| ModulesDidUnload(module_list, false); | |
| } | |
| void Target::ModulesDidLoad(ModuleList &module_list) { | |
| if (GetPreloadSymbols()) | |
| module_list.PreloadSymbols(GetParallelModuleLoad()); | |
| const size_t num_images = module_list.GetSize(); | |
| if (m_valid && num_images) { | |
| std::list<Status> errors; | |
| module_list.LoadScriptingResourcesInTarget(this, errors); | |
| for (const auto &err : errors) | |
| GetDebugger().GetAsyncErrorStream()->PutCString(err.AsCString()); | |
| for (size_t idx = 0; idx < num_images; ++idx) { | |
| ModuleSP module_sp(module_list.GetModuleAtIndex(idx)); | |
| LoadTypeSummariesForModule(module_sp); | |
| LoadFormattersForModule(module_sp); | |
| } | |
| m_breakpoint_list.UpdateBreakpoints(module_list, true, false); | |
| m_internal_breakpoint_list.UpdateBreakpoints(module_list, true, false); | |
| if (m_process_sp) { | |
| m_process_sp->ModulesDidLoad(module_list); | |
| } | |
| RunModuleHooks(/*is_load=*/true); | |
| auto data_sp = | |
| std::make_shared<TargetEventData>(shared_from_this(), module_list); | |
| BroadcastEvent(eBroadcastBitModulesLoaded, data_sp); | |
| } | |
| } | |
| void Target::SymbolsDidLoad(ModuleList &module_list) { | |
| if (m_valid && module_list.GetSize()) { | |
| if (m_process_sp) { | |
| for (LanguageRuntime *runtime : m_process_sp->GetLanguageRuntimes()) { | |
| runtime->SymbolsDidLoad(module_list); | |
| } | |
| } | |
| m_breakpoint_list.UpdateBreakpoints(module_list, true, false); | |
| m_internal_breakpoint_list.UpdateBreakpoints(module_list, true, false); | |
| auto data_sp = | |
| std::make_shared<TargetEventData>(shared_from_this(), module_list); | |
| BroadcastEvent(eBroadcastBitSymbolsLoaded, data_sp); | |
| } | |
| } | |
| void Target::ModulesDidUnload(ModuleList &module_list, bool delete_locations) { | |
| if (m_valid && module_list.GetSize()) { | |
| UnloadModuleSections(module_list); | |
| auto data_sp = | |
| std::make_shared<TargetEventData>(shared_from_this(), module_list); | |
| BroadcastEvent(eBroadcastBitModulesUnloaded, data_sp); | |
| m_breakpoint_list.UpdateBreakpoints(module_list, false, delete_locations); | |
| m_internal_breakpoint_list.UpdateBreakpoints(module_list, false, | |
| delete_locations); | |
| // If a module was torn down it will have torn down the 'TypeSystemClang's | |
| // that we used as source 'ASTContext's for the persistent variables in | |
| // the current target. Those would now be unsafe to access because the | |
| // 'DeclOrigin' are now possibly stale. Thus clear all persistent | |
| // variables. We only want to flush 'TypeSystem's if the module being | |
| // unloaded was capable of describing a source type. JITted module unloads | |
| // happen frequently for Objective-C utility functions or the REPL and rely | |
| // on the persistent variables to stick around. | |
| const bool should_flush_type_systems = | |
| module_list.AnyOf([](lldb_private::Module &module) { | |
| auto *object_file = module.GetObjectFile(); | |
| if (!object_file) | |
| return false; | |
| auto type = object_file->GetType(); | |
| // eTypeExecutable: when debugged binary was rebuilt | |
| // eTypeSharedLibrary: if dylib was re-loaded | |
| return module.FileHasChanged() && | |
| (type == ObjectFile::eTypeObjectFile || | |
| type == ObjectFile::eTypeExecutable || | |
| type == ObjectFile::eTypeSharedLibrary); | |
| }); | |
| if (should_flush_type_systems) | |
| m_scratch_type_system_map.Clear(); | |
| RunModuleHooks(/*is_load=*/false); | |
| } | |
| } | |
| bool Target::ModuleIsExcludedForUnconstrainedSearches( | |
| const FileSpec &module_file_spec) { | |
| if (GetBreakpointsConsultPlatformAvoidList()) { | |
| ModuleList matchingModules; | |
| ModuleSpec module_spec(module_file_spec); | |
| GetImages().FindModules(module_spec, matchingModules); | |
| size_t num_modules = matchingModules.GetSize(); | |
| // If there is more than one module for this file spec, only | |
| // return true if ALL the modules are on the black list. | |
| if (num_modules > 0) { | |
| for (size_t i = 0; i < num_modules; i++) { | |
| if (!ModuleIsExcludedForUnconstrainedSearches( | |
| matchingModules.GetModuleAtIndex(i))) | |
| return false; | |
| } | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool Target::ModuleIsExcludedForUnconstrainedSearches( | |
| const lldb::ModuleSP &module_sp) { | |
| if (GetBreakpointsConsultPlatformAvoidList()) { | |
| if (m_platform_sp) | |
| return m_platform_sp->ModuleIsExcludedForUnconstrainedSearches(*this, | |
| module_sp); | |
| } | |
| return false; | |
| } | |
| size_t Target::ReadMemoryFromFileCache(const Address &addr, void *dst, | |
| size_t dst_len, Status &error) { | |
| SectionSP section_sp(addr.GetSection()); | |
| if (section_sp) { | |
| // If the contents of this section are encrypted, the on-disk file is | |
| // unusable. Read only from live memory. | |
| if (section_sp->IsEncrypted()) { | |
| error = Status::FromErrorString("section is encrypted"); | |
| return 0; | |
| } | |
| ModuleSP module_sp(section_sp->GetModule()); | |
| if (module_sp) { | |
| ObjectFile *objfile = section_sp->GetModule()->GetObjectFile(); | |
| if (objfile) { | |
| size_t bytes_read = objfile->ReadSectionData( | |
| section_sp.get(), addr.GetOffset(), dst, dst_len); | |
| if (bytes_read > 0) | |
| return bytes_read; | |
| else | |
| error = Status::FromErrorStringWithFormatv( | |
| "error reading data from section {0}", section_sp->GetName()); | |
| } else | |
| error = Status::FromErrorString("address isn't from a object file"); | |
| } else | |
| error = Status::FromErrorString("address isn't in a module"); | |
| } else | |
| error = Status::FromErrorString( | |
| "address doesn't contain a section that points to a " | |
| "section in a object file"); | |
| return 0; | |
| } | |
| size_t Target::ReadMemory(const Address &addr, void *dst, size_t dst_len, | |
| Status &error, bool force_live_memory, | |
| lldb::addr_t *load_addr_ptr, | |
| bool *did_read_live_memory) { | |
| error.Clear(); | |
| if (did_read_live_memory) | |
| *did_read_live_memory = false; | |
| Address fixed_addr = addr; | |
| if (ProcessIsValid()) | |
| if (const ABISP &abi = m_process_sp->GetABI()) | |
| fixed_addr.SetLoadAddress(abi->FixAnyAddress(addr.GetLoadAddress(this)), | |
| this); | |
| // if we end up reading this from process memory, we will fill this with the | |
| // actual load address | |
| if (load_addr_ptr) | |
| *load_addr_ptr = LLDB_INVALID_ADDRESS; | |
| size_t bytes_read = 0; | |
| addr_t load_addr = LLDB_INVALID_ADDRESS; | |
| addr_t file_addr = LLDB_INVALID_ADDRESS; | |
| Address resolved_addr; | |
| if (!fixed_addr.IsSectionOffset()) { | |
| SectionLoadList §ion_load_list = GetSectionLoadList(); | |
| if (section_load_list.IsEmpty()) { | |
| // No sections are loaded, so we must assume we are not running yet and | |
| // anything we are given is a file address. | |
| file_addr = | |
| fixed_addr.GetOffset(); // "fixed_addr" doesn't have a section, so | |
| // its offset is the file address | |
| m_images.ResolveFileAddress(file_addr, resolved_addr); | |
| } else { | |
| // We have at least one section loaded. This can be because we have | |
| // manually loaded some sections with "target modules load ..." or | |
| // because we have a live process that has sections loaded through | |
| // the dynamic loader | |
| load_addr = | |
| fixed_addr.GetOffset(); // "fixed_addr" doesn't have a section, so | |
| // its offset is the load address | |
| section_load_list.ResolveLoadAddress(load_addr, resolved_addr); | |
| } | |
| } | |
| if (!resolved_addr.IsValid()) | |
| resolved_addr = fixed_addr; | |
| // If we read from the file cache but can't get as many bytes as requested, | |
| // we keep the result around in this buffer, in case this result is the | |
| // best we can do. | |
| std::unique_ptr<uint8_t[]> file_cache_read_buffer; | |
| size_t file_cache_bytes_read = 0; | |
| // Read from file cache if read-only section. | |
| if (!force_live_memory && resolved_addr.IsSectionOffset()) { | |
| SectionSP section_sp(resolved_addr.GetSection()); | |
| if (section_sp) { | |
| auto permissions = Flags(section_sp->GetPermissions()); | |
| bool is_readonly = !permissions.Test(ePermissionsWritable) && | |
| permissions.Test(ePermissionsReadable); | |
| if (is_readonly) { | |
| file_cache_bytes_read = | |
| ReadMemoryFromFileCache(resolved_addr, dst, dst_len, error); | |
| if (file_cache_bytes_read == dst_len) | |
| return file_cache_bytes_read; | |
| else if (file_cache_bytes_read > 0) { | |
| file_cache_read_buffer = | |
| std::make_unique<uint8_t[]>(file_cache_bytes_read); | |
| std::memcpy(file_cache_read_buffer.get(), dst, file_cache_bytes_read); | |
| } | |
| } | |
| } | |
| } | |
| if (ProcessIsValid()) { | |
| if (load_addr == LLDB_INVALID_ADDRESS) | |
| load_addr = resolved_addr.GetLoadAddress(this); | |
| if (load_addr == LLDB_INVALID_ADDRESS) { | |
| ModuleSP addr_module_sp(resolved_addr.GetModule()); | |
| if (addr_module_sp && addr_module_sp->GetFileSpec()) | |
| error = Status::FromErrorStringWithFormatv( | |
| "{0:F}[{1:x+}] can't be resolved, {0:F} is not currently loaded", | |
| addr_module_sp->GetFileSpec(), resolved_addr.GetFileAddress()); | |
| else | |
| error = Status::FromErrorStringWithFormat( | |
| "0x%" PRIx64 " can't be resolved", resolved_addr.GetFileAddress()); | |
| } else { | |
| bytes_read = m_process_sp->ReadMemory(load_addr, dst, dst_len, error); | |
| if (bytes_read != dst_len) { | |
| if (error.Success()) { | |
| if (bytes_read == 0) | |
| error = Status::FromErrorStringWithFormat( | |
| "read memory from 0x%" PRIx64 " failed", load_addr); | |
| else | |
| error = Status::FromErrorStringWithFormat( | |
| "only %" PRIu64 " of %" PRIu64 | |
| " bytes were read from memory at 0x%" PRIx64, | |
| (uint64_t)bytes_read, (uint64_t)dst_len, load_addr); | |
| } | |
| } | |
| if (bytes_read) { | |
| if (load_addr_ptr) | |
| *load_addr_ptr = load_addr; | |
| if (did_read_live_memory) | |
| *did_read_live_memory = true; | |
| return bytes_read; | |
| } | |
| } | |
| } | |
| if (file_cache_read_buffer && file_cache_bytes_read > 0) { | |
| // Reading from the process failed. If we've previously succeeded in reading | |
| // something from the file cache, then copy that over and return that. | |
| std::memcpy(dst, file_cache_read_buffer.get(), file_cache_bytes_read); | |
| return file_cache_bytes_read; | |
| } | |
| if (!file_cache_read_buffer && resolved_addr.IsSectionOffset()) { | |
| // If we didn't already try and read from the object file cache, then try | |
| // it after failing to read from the process. | |
| error.Clear(); | |
| bytes_read = ReadMemoryFromFileCache(resolved_addr, dst, dst_len, error); | |
| // A short read here is only a failure if a live read already failed too. | |
| // Reaching this point with a valid process means the process contributed | |
| // nothing. | |
| if (bytes_read > 0 && bytes_read != dst_len && error.Success() && | |
| ProcessIsValid()) | |
| error = Status::FromErrorStringWithFormatv( | |
| "only {0} of {1} bytes were read from the object file cache", | |
| bytes_read, dst_len); | |
| return bytes_read; | |
| } | |
| return 0; | |
| } | |
| size_t Target::ReadCStringFromMemory(const Address &addr, std::string &out_str, | |
| Status &error, bool force_live_memory) { | |
| char buf[256]; | |
| out_str.clear(); | |
| addr_t curr_addr = addr.GetLoadAddress(this); | |
| Address address(addr); | |
| while (true) { | |
| size_t length = ReadCStringFromMemory(address, buf, sizeof(buf), error, | |
| force_live_memory); | |
| if (length == 0) | |
| break; | |
| out_str.append(buf, length); | |
| // If we got "length - 1" bytes, we didn't get the whole C string, we need | |
| // to read some more characters | |
| if (length == sizeof(buf) - 1) | |
| curr_addr += length; | |
| else | |
| break; | |
| address = Address(curr_addr); | |
| } | |
| return out_str.size(); | |
| } | |
| size_t Target::ReadCStringFromMemory(const Address &addr, char *dst, | |
| size_t dst_max_len, Status &result_error, | |
| bool force_live_memory) { | |
| size_t total_cstr_len = 0; | |
| if (dst && dst_max_len) { | |
| result_error.Clear(); | |
| // NULL out everything just to be safe | |
| memset(dst, 0, dst_max_len); | |
| addr_t curr_addr = addr.GetLoadAddress(this); | |
| Address address(addr); | |
| // We could call m_process_sp->GetMemoryCacheLineSize() but I don't think | |
| // this really needs to be tied to the memory cache subsystem's cache line | |
| // size, so leave this as a fixed constant. | |
| const size_t cache_line_size = 512; | |
| size_t bytes_left = dst_max_len - 1; | |
| char *curr_dst = dst; | |
| while (bytes_left > 0) { | |
| addr_t cache_line_bytes_left = | |
| cache_line_size - (curr_addr % cache_line_size); | |
| addr_t bytes_to_read = | |
| std::min<addr_t>(bytes_left, cache_line_bytes_left); | |
| Status error; | |
| size_t bytes_read = ReadMemory(address, curr_dst, bytes_to_read, error, | |
| force_live_memory); | |
| if (bytes_read == 0) { | |
| result_error = std::move(error); | |
| dst[total_cstr_len] = '\0'; | |
| break; | |
| } | |
| const size_t len = strlen(curr_dst); | |
| total_cstr_len += len; | |
| if (len < bytes_to_read) | |
| break; | |
| curr_dst += bytes_read; | |
| curr_addr += bytes_read; | |
| bytes_left -= bytes_read; | |
| address = Address(curr_addr); | |
| } | |
| } else { | |
| if (dst == nullptr) | |
| result_error = Status::FromErrorString("invalid arguments"); | |
| else | |
| result_error.Clear(); | |
| } | |
| return total_cstr_len; | |
| } | |
| addr_t Target::GetReasonableReadSize(const Address &addr) { | |
| addr_t load_addr = addr.GetLoadAddress(this); | |
| if (load_addr != LLDB_INVALID_ADDRESS && m_process_sp) { | |
| // Avoid crossing cache line boundaries. | |
| addr_t cache_line_size = m_process_sp->GetMemoryCacheLineSize(); | |
| return cache_line_size - (load_addr % cache_line_size); | |
| } | |
| // The read is going to go to the file cache, so we can just pick a largish | |
| // value. | |
| return 0x1000; | |
| } | |
| size_t Target::ReadStringFromMemory(const Address &addr, char *dst, | |
| size_t max_bytes, Status &error, | |
| size_t type_width, bool force_live_memory) { | |
| if (!dst || !max_bytes || !type_width || max_bytes < type_width) | |
| return 0; | |
| size_t total_bytes_read = 0; | |
| // Ensure a null terminator independent of the number of bytes that is | |
| // read. | |
| memset(dst, 0, max_bytes); | |
| size_t bytes_left = max_bytes - type_width; | |
| const char terminator[4] = {'\0', '\0', '\0', '\0'}; | |
| assert(sizeof(terminator) >= type_width && "Attempting to validate a " | |
| "string with more than 4 bytes " | |
| "per character!"); | |
| Address address = addr; | |
| char *curr_dst = dst; | |
| error.Clear(); | |
| while (bytes_left > 0 && error.Success()) { | |
| addr_t bytes_to_read = | |
| std::min<addr_t>(bytes_left, GetReasonableReadSize(address)); | |
| size_t bytes_read = | |
| ReadMemory(address, curr_dst, bytes_to_read, error, force_live_memory); | |
| if (bytes_read == 0) | |
| break; | |
| // Search for a null terminator of correct size and alignment in | |
| // bytes_read | |
| size_t aligned_start = total_bytes_read - total_bytes_read % type_width; | |
| for (size_t i = aligned_start; | |
| i + type_width <= total_bytes_read + bytes_read; i += type_width) | |
| if (::memcmp(&dst[i], terminator, type_width) == 0) { | |
| error.Clear(); | |
| return i; | |
| } | |
| total_bytes_read += bytes_read; | |
| curr_dst += bytes_read; | |
| address.Slide(bytes_read); | |
| bytes_left -= bytes_read; | |
| } | |
| return total_bytes_read; | |
| } | |
| size_t Target::ReadScalarIntegerFromMemory(const Address &addr, uint32_t byte_size, | |
| bool is_signed, Scalar &scalar, | |
| Status &error, | |
| bool force_live_memory) { | |
| uint64_t uval; | |
| if (byte_size <= sizeof(uval)) { | |
| size_t bytes_read = | |
| ReadMemory(addr, &uval, byte_size, error, force_live_memory); | |
| if (bytes_read == byte_size) { | |
| DataExtractor data(&uval, sizeof(uval), m_arch.GetSpec().GetByteOrder(), | |
| m_arch.GetSpec().GetAddressByteSize()); | |
| lldb::offset_t offset = 0; | |
| if (byte_size <= 4) | |
| scalar = data.GetMaxU32(&offset, byte_size); | |
| else | |
| scalar = data.GetMaxU64(&offset, byte_size); | |
| if (is_signed) { | |
| scalar.MakeSigned(); | |
| scalar.SignExtend(byte_size * 8); | |
| } | |
| return bytes_read; | |
| } | |
| } else { | |
| error = Status::FromErrorStringWithFormat( | |
| "byte size of %u is too large for integer scalar type", byte_size); | |
| } | |
| return 0; | |
| } | |
| int64_t Target::ReadSignedIntegerFromMemory(const Address &addr, | |
| size_t integer_byte_size, | |
| int64_t fail_value, Status &error, | |
| bool force_live_memory) { | |
| Scalar scalar; | |
| if (ReadScalarIntegerFromMemory(addr, integer_byte_size, true, scalar, error, | |
| force_live_memory)) | |
| return scalar.SLongLong(fail_value); | |
| return fail_value; | |
| } | |
| uint64_t Target::ReadUnsignedIntegerFromMemory(const Address &addr, | |
| size_t integer_byte_size, | |
| uint64_t fail_value, Status &error, | |
| bool force_live_memory) { | |
| Scalar scalar; | |
| if (ReadScalarIntegerFromMemory(addr, integer_byte_size, false, scalar, error, | |
| force_live_memory)) | |
| return scalar.ULongLong(fail_value); | |
| return fail_value; | |
| } | |
| bool Target::ReadPointerFromMemory(const Address &addr, Status &error, | |
| Address &pointer_addr, | |
| bool force_live_memory) { | |
| Scalar scalar; | |
| if (ReadScalarIntegerFromMemory(addr, m_arch.GetSpec().GetAddressByteSize(), | |
| false, scalar, error, force_live_memory)) { | |
| addr_t pointer_vm_addr = scalar.ULongLong(LLDB_INVALID_ADDRESS); | |
| if (pointer_vm_addr != LLDB_INVALID_ADDRESS) { | |
| SectionLoadList §ion_load_list = GetSectionLoadList(); | |
| if (section_load_list.IsEmpty()) { | |
| // No sections are loaded, so we must assume we are not running yet and | |
| // anything we are given is a file address. | |
| m_images.ResolveFileAddress(pointer_vm_addr, pointer_addr); | |
| } else { | |
| // We have at least one section loaded. This can be because we have | |
| // manually loaded some sections with "target modules load ..." or | |
| // because we have a live process that has sections loaded through | |
| // the dynamic loader | |
| section_load_list.ResolveLoadAddress(pointer_vm_addr, pointer_addr); | |
| } | |
| // We weren't able to resolve the pointer value, so just return an | |
| // address with no section | |
| if (!pointer_addr.IsValid()) | |
| pointer_addr.SetOffset(pointer_vm_addr); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| ModuleSP Target::GetOrCreateModule(const ModuleSpec &orig_module_spec, | |
| bool notify, Status *error_ptr) { | |
| ModuleSP module_sp; | |
| Status error; | |
| // Apply any remappings specified in target.object-map: | |
| ModuleSpec module_spec(orig_module_spec); | |
| module_spec.SetTarget(shared_from_this()); | |
| PathMappingList &obj_mapping = GetObjectPathMap(); | |
| if (std::optional<FileSpec> remapped_obj_file = | |
| obj_mapping.RemapPath(orig_module_spec.GetFileSpec().GetPath(), | |
| true /* only_if_exists */)) { | |
| module_spec.GetFileSpec().SetPath(remapped_obj_file->GetPath()); | |
| } | |
| // First see if we already have this module in our module list. If we do, | |
| // then we're done, we don't need to consult the shared modules list. But | |
| // only do this if we are passed a UUID. | |
| if (module_spec.GetUUID().IsValid()) | |
| module_sp = m_images.FindFirstModule(module_spec); | |
| if (!module_sp) { | |
| llvm::SmallVector<ModuleSP, 1> | |
| old_modules; // This will get filled in if we have a new version | |
| // of the library | |
| bool did_create_module = false; | |
| FileSpecList search_paths = GetExecutableSearchPaths(); | |
| FileSpec symbol_file_spec; | |
| // Call locate module callback if set. This allows users to implement their | |
| // own module cache system. For example, to leverage build system artifacts, | |
| // to bypass pulling files from remote platform, or to search symbol files | |
| // from symbol servers. | |
| if (m_platform_sp) | |
| m_platform_sp->CallLocateModuleCallbackIfSet( | |
| module_spec, module_sp, symbol_file_spec, &did_create_module); | |
| // The result of this CallLocateModuleCallbackIfSet is one of the following. | |
| // 1. module_sp:loaded, symbol_file_spec:set | |
| // The callback found a module file and a symbol file for the | |
| // module_spec. We will call module_sp->SetSymbolFileFileSpec with | |
| // the symbol_file_spec later. | |
| // 2. module_sp:loaded, symbol_file_spec:empty | |
| // The callback only found a module file for the module_spec. | |
| // 3. module_sp:empty, symbol_file_spec:set | |
| // The callback only found a symbol file for the module. We continue | |
| // to find a module file for this module_spec and we will call | |
| // module_sp->SetSymbolFileFileSpec with the symbol_file_spec later. | |
| // 4. module_sp:empty, symbol_file_spec:empty | |
| // Platform does not exist, the callback is not set, the callback did | |
| // not find any module files nor any symbol files, the callback failed, | |
| // or something went wrong. We continue to find a module file for this | |
| // module_spec. | |
| if (!module_sp) { | |
| // If there are image search path entries, try to use them to acquire a | |
| // suitable image. | |
| if (m_image_search_paths.GetSize()) { | |
| ModuleSpec transformed_spec(module_spec); | |
| ConstString transformed_dir; | |
| if (m_image_search_paths.RemapPath( | |
| ConstString(module_spec.GetFileSpec().GetDirectory()), | |
| transformed_dir)) { | |
| transformed_spec.GetFileSpec().SetDirectory(transformed_dir); | |
| transformed_spec.GetFileSpec().SetFilename( | |
| module_spec.GetFileSpec().GetFilename()); | |
| transformed_spec.SetTarget(shared_from_this()); | |
| error = ModuleList::GetSharedModule(transformed_spec, module_sp, | |
| &old_modules, &did_create_module); | |
| } | |
| } | |
| } | |
| if (!module_sp) { | |
| // If we have a UUID, we can check our global shared module list in case | |
| // we already have it. If we don't have a valid UUID, then we can't since | |
| // the path in "module_spec" will be a platform path, and we will need to | |
| // let the platform find that file. For example, we could be asking for | |
| // "/usr/lib/dyld" and if we do not have a UUID, we don't want to pick | |
| // the local copy of "/usr/lib/dyld" since our platform could be a remote | |
| // platform that has its own "/usr/lib/dyld" in an SDK or in a local file | |
| // cache. | |
| if (module_spec.GetUUID().IsValid()) { | |
| // We have a UUID, it is OK to check the global module list... | |
| error = ModuleList::GetSharedModule(module_spec, module_sp, | |
| &old_modules, &did_create_module); | |
| } | |
| if (!module_sp) { | |
| // The platform is responsible for finding and caching an appropriate | |
| // module in the shared module cache. | |
| if (m_platform_sp) { | |
| error = m_platform_sp->GetSharedModule( | |
| module_spec, *this, module_sp, &old_modules, &did_create_module); | |
| } else { | |
| error = Status::FromErrorString("no platform is currently set"); | |
| } | |
| } | |
| } | |
| // We found a module that wasn't in our target list. Let's make sure that | |
| // there wasn't an equivalent module in the list already, and if there was, | |
| // let's remove it. | |
| if (module_sp) { | |
| ObjectFile *objfile = module_sp->GetObjectFile(); | |
| if (objfile) { | |
| switch (objfile->GetType()) { | |
| case ObjectFile::eTypeCoreFile: /// A core file that has a checkpoint of | |
| /// a program's execution state | |
| case ObjectFile::eTypeExecutable: /// A normal executable | |
| case ObjectFile::eTypeDynamicLinker: /// The platform's dynamic linker | |
| /// executable | |
| case ObjectFile::eTypeObjectFile: /// An intermediate object file | |
| case ObjectFile::eTypeSharedLibrary: /// A shared library that can be | |
| /// used during execution | |
| break; | |
| case ObjectFile::eTypeDebugInfo: /// An object file that contains only | |
| /// debug information | |
| if (error_ptr) | |
| *error_ptr = Status::FromErrorString( | |
| "debug info files aren't valid target " | |
| "modules, please specify an executable"); | |
| return ModuleSP(); | |
| case ObjectFile::eTypeStubLibrary: /// A library that can be linked | |
| /// against but not used for | |
| /// execution | |
| if (error_ptr) | |
| *error_ptr = Status::FromErrorString( | |
| "stub libraries aren't valid target " | |
| "modules, please specify an executable"); | |
| return ModuleSP(); | |
| default: | |
| if (error_ptr) | |
| *error_ptr = Status::FromErrorString( | |
| "unsupported file type, please specify an executable"); | |
| return ModuleSP(); | |
| } | |
| // GetSharedModule is not guaranteed to find the old shared module, for | |
| // instance in the common case where you pass in the UUID, it is only | |
| // going to find the one module matching the UUID. In fact, it has no | |
| // good way to know what the "old module" relevant to this target is, | |
| // since there might be many copies of a module with this file spec in | |
| // various running debug sessions, but only one of them will belong to | |
| // this target. So let's remove the UUID from the module list, and look | |
| // in the target's module list. Only do this if there is SOMETHING else | |
| // in the module spec... | |
| if (module_spec.GetUUID().IsValid() && | |
| !module_spec.GetFileSpec().GetFilename().empty() && | |
| !module_spec.GetFileSpec().GetDirectory().empty()) { | |
| ModuleSpec module_spec_copy(module_spec.GetFileSpec()); | |
| module_spec_copy.GetUUID().Clear(); | |
| ModuleList found_modules; | |
| m_images.FindModules(module_spec_copy, found_modules); | |
| found_modules.ForEach([&](const ModuleSP &found_module) { | |
| old_modules.push_back(found_module); | |
| return IterationAction::Continue; | |
| }); | |
| } | |
| // If the locate module callback had found a symbol file, set it to the | |
| // module_sp before preloading symbols. | |
| if (symbol_file_spec) | |
| module_sp->SetSymbolFileFileSpec(symbol_file_spec); | |
| llvm::SmallVector<ModuleSP, 1> replaced_modules; | |
| for (ModuleSP &old_module_sp : old_modules) { | |
| if (m_images.GetIndexForModule(old_module_sp.get()) != | |
| LLDB_INVALID_INDEX32) { | |
| if (replaced_modules.empty()) | |
| m_images.ReplaceModule(old_module_sp, module_sp); | |
| else | |
| m_images.Remove(old_module_sp); | |
| replaced_modules.push_back(std::move(old_module_sp)); | |
| } | |
| } | |
| if (replaced_modules.size() > 1) { | |
| // The same new module replaced multiple old modules | |
| // simultaneously. It's not clear this should ever | |
| // happen (if we always replace old modules as we add | |
| // new ones, presumably we should never have more than | |
| // one old one). If there are legitimate cases where | |
| // this happens, then the ModuleList::Notifier interface | |
| // may need to be adjusted to allow reporting this. | |
| // In the meantime, just log that this has happened; just | |
| // above we called ReplaceModule on the first one, and Remove | |
| // on the rest. | |
| if (Log *log = GetLog(LLDBLog::Target | LLDBLog::Modules)) { | |
| StreamString message; | |
| auto dump = [&message](Module &dump_module) -> void { | |
| UUID dump_uuid = dump_module.GetUUID(); | |
| message << '['; | |
| dump_module.GetDescription(message.AsRawOstream()); | |
| message << " (uuid "; | |
| if (dump_uuid.IsValid()) | |
| dump_uuid.Dump(message); | |
| else | |
| message << "not specified"; | |
| message << ")]"; | |
| }; | |
| message << "New module "; | |
| dump(*module_sp); | |
| message.AsRawOstream() | |
| << llvm::formatv(" simultaneously replaced {0} old modules: ", | |
| replaced_modules.size()); | |
| for (ModuleSP &replaced_module_sp : replaced_modules) | |
| dump(*replaced_module_sp); | |
| log->PutString(message.GetString()); | |
| } | |
| } | |
| if (replaced_modules.empty()) { | |
| if (!m_images.AppendIfNeeded(module_sp, notify) && notify) | |
| NotifyModuleAdded(m_images, module_sp); | |
| } | |
| for (ModuleSP &old_module_sp : replaced_modules) { | |
| auto old_module_wp = old_module_sp->weak_from_this(); | |
| old_module_sp.reset(); | |
| ModuleList::RemoveSharedModuleIfOrphaned(old_module_wp); | |
| } | |
| } else | |
| module_sp.reset(); | |
| } | |
| } | |
| if (error_ptr) | |
| *error_ptr = std::move(error); | |
| return module_sp; | |
| } | |
| TargetSP Target::CalculateTarget() { return shared_from_this(); } | |
| ProcessSP Target::CalculateProcess() { return m_process_sp; } | |
| ThreadSP Target::CalculateThread() { return ThreadSP(); } | |
| StackFrameSP Target::CalculateStackFrame() { return StackFrameSP(); } | |
| void Target::CalculateExecutionContext(ExecutionContext &exe_ctx) { | |
| exe_ctx.Clear(); | |
| exe_ctx.SetTargetPtr(this); | |
| } | |
| PathMappingList &Target::GetImageSearchPathList() { | |
| return m_image_search_paths; | |
| } | |
| void Target::ImageSearchPathsChanged(const PathMappingList &path_list, | |
| void *baton) { | |
| Target *target = (Target *)baton; | |
| ModuleSP exe_module_sp(target->GetExecutableModule()); | |
| if (exe_module_sp) | |
| target->RebuildModuleListWithExecutable(exe_module_sp, eLoadDependentsYes); | |
| } | |
| llvm::Expected<lldb::TypeSystemSP> | |
| Target::GetScratchTypeSystemForLanguage(lldb::LanguageType language, | |
| bool create_on_demand) { | |
| if (!m_valid) | |
| return llvm::createStringError("invalid target"); | |
| if (language == eLanguageTypeMipsAssembler // GNU AS and LLVM use it for all | |
| // assembly code | |
| || language == eLanguageTypeAssembly || | |
| language == eLanguageTypeUnknown) { | |
| LanguageSet languages_for_expressions = | |
| Language::GetLanguagesSupportingTypeSystemsForExpressions(); | |
| if (languages_for_expressions[eLanguageTypeC]) { | |
| language = eLanguageTypeC; // LLDB's default. Override by setting the | |
| // target language. | |
| } else { | |
| if (languages_for_expressions.Empty()) | |
| return llvm::createStringError( | |
| "No expression support for any languages"); | |
| language = (LanguageType)languages_for_expressions.bitvector.find_first(); | |
| } | |
| } | |
| return m_scratch_type_system_map.GetTypeSystemForLanguage(language, this, | |
| create_on_demand); | |
| } | |
| CompilerType Target::GetRegisterType(const RegisterInfo ®_info) { | |
| if (!m_register_type_builder_sp) | |
| m_register_type_builder_sp = PluginManager::GetRegisterTypeBuilder(*this); | |
| assert(m_register_type_builder_sp); | |
| return m_register_type_builder_sp->GetRegisterType(reg_info); | |
| } | |
| std::vector<lldb::TypeSystemSP> | |
| Target::GetScratchTypeSystems(bool create_on_demand) { | |
| if (!m_valid) | |
| return {}; | |
| // Some TypeSystem instances are associated with several LanguageTypes so | |
| // they will show up several times in the loop below. The SetVector filters | |
| // out all duplicates as they serve no use for the caller. | |
| // | |
| // The insertion order matters: callers such as SBTarget::GetBasicType query | |
| // the TypeSystems in order and use the first one that can answer, so the | |
| // result has to be a function of the languages and not of where the | |
| // instances happen to live in memory. | |
| llvm::SetVector<lldb::TypeSystemSP, std::vector<lldb::TypeSystemSP>, | |
| std::set<lldb::TypeSystemSP>> | |
| scratch_type_systems; | |
| LanguageSet languages_for_expressions = | |
| Language::GetLanguagesSupportingTypeSystemsForExpressions(); | |
| for (auto bit : languages_for_expressions.bitvector.set_bits()) { | |
| auto language = (LanguageType)bit; | |
| auto type_system_or_err = | |
| GetScratchTypeSystemForLanguage(language, create_on_demand); | |
| if (!type_system_or_err) | |
| LLDB_LOG_ERROR( | |
| GetLog(LLDBLog::Target), type_system_or_err.takeError(), | |
| "Language '{1}' has expression support but no scratch type " | |
| "system available: {0}", | |
| Language::GetNameForLanguageType(language)); | |
| else if (auto ts = *type_system_or_err) | |
| scratch_type_systems.insert(ts); | |
| } | |
| return scratch_type_systems.takeVector(); | |
| } | |
| PersistentExpressionState * | |
| Target::GetPersistentExpressionStateForLanguage(lldb::LanguageType language) { | |
| auto type_system_or_err = GetScratchTypeSystemForLanguage(language, true); | |
| if (auto err = type_system_or_err.takeError()) { | |
| LLDB_LOG_ERROR( | |
| GetLog(LLDBLog::Target), std::move(err), | |
| "Unable to get persistent expression state for language {1}: {0}", | |
| Language::GetNameForLanguageType(language)); | |
| return nullptr; | |
| } | |
| if (auto ts = *type_system_or_err) | |
| return ts->GetPersistentExpressionState(); | |
| LLDB_LOG(GetLog(LLDBLog::Target), | |
| "Unable to get persistent expression state for language {}:", | |
| Language::GetNameForLanguageType(language)); | |
| return nullptr; | |
| } | |
| UserExpression *Target::GetUserExpressionForLanguage( | |
| llvm::StringRef expr, llvm::StringRef prefix, SourceLanguage language, | |
| Expression::ResultType desired_type, | |
| const EvaluateExpressionOptions &options, ValueObject *ctx_obj, | |
| Status &error) { | |
| auto type_system_or_err = | |
| GetScratchTypeSystemForLanguage(language.AsLanguageType()); | |
| if (auto err = type_system_or_err.takeError()) { | |
| error = Status::FromErrorStringWithFormat( | |
| "Could not find type system for language %s: %s", | |
| Language::GetNameForLanguageType(language.AsLanguageType()), | |
| llvm::toString(std::move(err)).c_str()); | |
| return nullptr; | |
| } | |
| auto ts = *type_system_or_err; | |
| if (!ts) { | |
| error = Status::FromErrorStringWithFormat( | |
| "Type system for language %s is no longer live", | |
| language.GetDescription().data()); | |
| return nullptr; | |
| } | |
| auto *user_expr = ts->GetUserExpression(expr, prefix, language, desired_type, | |
| options, ctx_obj); | |
| if (!user_expr) | |
| error = Status::FromErrorStringWithFormat( | |
| "Could not create an expression for language %s", | |
| language.GetDescription().data()); | |
| return user_expr; | |
| } | |
| FunctionCaller *Target::GetFunctionCallerForLanguage( | |
| lldb::LanguageType language, const CompilerType &return_type, | |
| const Address &function_address, const ValueList &arg_value_list, | |
| const char *name, Status &error) { | |
| auto type_system_or_err = GetScratchTypeSystemForLanguage(language); | |
| if (auto err = type_system_or_err.takeError()) { | |
| error = Status::FromErrorStringWithFormat( | |
| "Could not find type system for language %s: %s", | |
| Language::GetNameForLanguageType(language), | |
| llvm::toString(std::move(err)).c_str()); | |
| return nullptr; | |
| } | |
| auto ts = *type_system_or_err; | |
| if (!ts) { | |
| error = Status::FromErrorStringWithFormat( | |
| "Type system for language %s is no longer live", | |
| Language::GetNameForLanguageType(language)); | |
| return nullptr; | |
| } | |
| auto *persistent_fn = ts->GetFunctionCaller(return_type, function_address, | |
| arg_value_list, name); | |
| if (!persistent_fn) | |
| error = Status::FromErrorStringWithFormat( | |
| "Could not create an expression for language %s", | |
| Language::GetNameForLanguageType(language)); | |
| return persistent_fn; | |
| } | |
| llvm::Expected<std::unique_ptr<UtilityFunction>> | |
| Target::CreateUtilityFunction(std::string expression, std::string name, | |
| lldb::LanguageType language, | |
| ExecutionContext &exe_ctx) { | |
| auto type_system_or_err = GetScratchTypeSystemForLanguage(language); | |
| if (!type_system_or_err) | |
| return type_system_or_err.takeError(); | |
| auto ts = *type_system_or_err; | |
| if (!ts) | |
| return llvm::createStringError( | |
| llvm::StringRef("Type system for language ") + | |
| Language::GetNameForLanguageType(language) + | |
| llvm::StringRef(" is no longer live")); | |
| std::unique_ptr<UtilityFunction> utility_fn = | |
| ts->CreateUtilityFunction(std::move(expression), std::move(name)); | |
| if (!utility_fn) | |
| return llvm::createStringError( | |
| llvm::StringRef("Could not create an expression for language") + | |
| Language::GetNameForLanguageType(language)); | |
| DiagnosticManager diagnostics; | |
| if (!utility_fn->Install(diagnostics, exe_ctx)) | |
| return diagnostics.GetAsError(lldb::eExpressionSetupError, | |
| "Could not install utility function:"); | |
| return std::move(utility_fn); | |
| } | |
| void Target::SettingsInitialize() { Process::SettingsInitialize(); } | |
| void Target::SettingsTerminate() { Process::SettingsTerminate(); } | |
| FileSpecList Target::GetDefaultExecutableSearchPaths() { | |
| return Target::GetGlobalProperties().GetExecutableSearchPaths(); | |
| } | |
| FileSpecList Target::GetDefaultDebugFileSearchPaths() { | |
| return Target::GetGlobalProperties().GetDebugFileSearchPaths(); | |
| } | |
| ArchSpec Target::GetDefaultArchitecture() { | |
| return Target::GetGlobalProperties().GetDefaultArchitecture(); | |
| } | |
| void Target::SetDefaultArchitecture(const ArchSpec &arch) { | |
| LLDB_LOG(GetLog(LLDBLog::Target), | |
| "setting target's default architecture to {0} ({1})", | |
| arch.GetArchitectureName(), arch.GetTriple().getTriple()); | |
| Target::GetGlobalProperties().SetDefaultArchitecture(arch); | |
| } | |
| llvm::Error Target::SetLabel(llvm::StringRef label) { | |
| size_t n = LLDB_INVALID_INDEX32; | |
| if (llvm::to_integer(label, n)) | |
| return llvm::createStringError("cannot use integer as target label"); | |
| TargetList &targets = GetDebugger().GetTargetList(); | |
| for (size_t i = 0; i < targets.GetNumTargets(); i++) { | |
| TargetSP target_sp = targets.GetTargetAtIndex(i); | |
| if (target_sp && target_sp->GetLabel() == label) { | |
| return llvm::createStringErrorV( | |
| "Cannot use label '{0}' since it's set in target #{1}.", label, i); | |
| } | |
| } | |
| m_label = label.str(); | |
| return llvm::Error::success(); | |
| } | |
| Target *Target::GetTargetFromContexts(const ExecutionContext *exe_ctx_ptr, | |
| const SymbolContext *sc_ptr) { | |
| // The target can either exist in the "process" of ExecutionContext, or in | |
| // the "target_sp" member of SymbolContext. This accessor helper function | |
| // will get the target from one of these locations. | |
| Target *target = nullptr; | |
| if (sc_ptr != nullptr) | |
| target = sc_ptr->target_sp.get(); | |
| if (target == nullptr && exe_ctx_ptr) | |
| target = exe_ctx_ptr->GetTargetPtr(); | |
| return target; | |
| } | |
| ExpressionResults Target::EvaluateExpression( | |
| llvm::StringRef expr, ExecutionContextScope *exe_scope, | |
| lldb::ValueObjectSP &result_valobj_sp, | |
| const EvaluateExpressionOptions &options, std::string *fixed_expression, | |
| ValueObject *ctx_obj) { | |
| result_valobj_sp.reset(); | |
| ExpressionResults execution_results = eExpressionSetupError; | |
| if (expr.empty()) { | |
| m_stats.GetExpressionStats().NotifyFailure(); | |
| return execution_results; | |
| } | |
| // We shouldn't run stop hooks in expressions. | |
| bool old_suppress_value = m_suppress_stop_hooks; | |
| m_suppress_stop_hooks = true; | |
| llvm::scope_exit on_exit([this, old_suppress_value]() { | |
| m_suppress_stop_hooks = old_suppress_value; | |
| }); | |
| ExecutionContext exe_ctx; | |
| if (exe_scope) { | |
| exe_scope->CalculateExecutionContext(exe_ctx); | |
| } else if (m_process_sp) { | |
| m_process_sp->CalculateExecutionContext(exe_ctx); | |
| } else { | |
| CalculateExecutionContext(exe_ctx); | |
| } | |
| // Make sure we aren't just trying to see the value of a persistent variable | |
| // (something like "$0") | |
| // Only check for persistent variables the expression starts with a '$' | |
| lldb::ExpressionVariableSP persistent_var_sp; | |
| if (expr[0] == '$') { | |
| auto type_system_or_err = | |
| GetScratchTypeSystemForLanguage(eLanguageTypeC); | |
| if (auto err = type_system_or_err.takeError()) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Target), std::move(err), | |
| "Unable to get scratch type system: {0}"); | |
| } else { | |
| auto ts = *type_system_or_err; | |
| if (!ts) | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Target), std::move(err), | |
| "Scratch type system is no longer live: {0}"); | |
| else | |
| persistent_var_sp = | |
| ts->GetPersistentExpressionState()->GetVariable(expr); | |
| } | |
| } | |
| if (persistent_var_sp) { | |
| result_valobj_sp = persistent_var_sp->GetValueObject(); | |
| execution_results = eExpressionCompleted; | |
| } else { | |
| llvm::StringRef prefix = GetExpressionPrefixContents(); | |
| execution_results = | |
| UserExpression::Evaluate(exe_ctx, options, expr, prefix, | |
| result_valobj_sp, fixed_expression, ctx_obj); | |
| } | |
| if (execution_results == eExpressionCompleted) | |
| m_stats.GetExpressionStats().NotifySuccess(); | |
| else | |
| m_stats.GetExpressionStats().NotifyFailure(); | |
| return execution_results; | |
| } | |
| lldb::ExpressionVariableSP Target::GetPersistentVariable(ConstString name) { | |
| lldb::ExpressionVariableSP variable_sp; | |
| m_scratch_type_system_map.ForEach( | |
| [name, &variable_sp](TypeSystemSP type_system) -> bool { | |
| auto ts = type_system.get(); | |
| if (!ts) | |
| return true; | |
| if (PersistentExpressionState *persistent_state = | |
| ts->GetPersistentExpressionState()) { | |
| variable_sp = persistent_state->GetVariable(name); | |
| if (variable_sp) | |
| return false; // Stop iterating the ForEach | |
| } | |
| return true; // Keep iterating the ForEach | |
| }); | |
| return variable_sp; | |
| } | |
| lldb::addr_t Target::GetPersistentSymbol(ConstString name) { | |
| lldb::addr_t address = LLDB_INVALID_ADDRESS; | |
| m_scratch_type_system_map.ForEach( | |
| [name, &address](lldb::TypeSystemSP type_system) -> bool { | |
| auto ts = type_system.get(); | |
| if (!ts) | |
| return true; | |
| if (PersistentExpressionState *persistent_state = | |
| ts->GetPersistentExpressionState()) { | |
| address = persistent_state->LookupSymbol(name); | |
| if (address != LLDB_INVALID_ADDRESS) | |
| return false; // Stop iterating the ForEach | |
| } | |
| return true; // Keep iterating the ForEach | |
| }); | |
| return address; | |
| } | |
| llvm::Expected<lldb_private::Address> Target::GetEntryPointAddress() { | |
| Module *exe_module = GetExecutableModulePointer(); | |
| // Try to find the entry point address in the primary executable. | |
| const bool has_primary_executable = exe_module && exe_module->GetObjectFile(); | |
| if (has_primary_executable) { | |
| Address entry_addr = exe_module->GetObjectFile()->GetEntryPointAddress(); | |
| if (entry_addr.IsValid()) | |
| return entry_addr; | |
| } | |
| const ModuleList &modules = GetImages(); | |
| const size_t num_images = modules.GetSize(); | |
| for (size_t idx = 0; idx < num_images; ++idx) { | |
| ModuleSP module_sp(modules.GetModuleAtIndex(idx)); | |
| if (!module_sp || !module_sp->GetObjectFile()) | |
| continue; | |
| Address entry_addr = module_sp->GetObjectFile()->GetEntryPointAddress(); | |
| if (entry_addr.IsValid()) | |
| return entry_addr; | |
| } | |
| // We haven't found the entry point address. Return an appropriate error. | |
| if (!has_primary_executable) | |
| return llvm::createStringError( | |
| "No primary executable found and could not find entry point address in " | |
| "any executable module"); | |
| return llvm::createStringError( | |
| "Could not find entry point address for primary executable module \"" + | |
| exe_module->GetFileSpec().GetFilename() + "\""); | |
| } | |
| lldb::addr_t Target::GetCallableLoadAddress(lldb::addr_t load_addr, | |
| AddressClass addr_class) const { | |
| auto arch_plugin = GetArchitecturePlugin(); | |
| return arch_plugin | |
| ? arch_plugin->GetCallableLoadAddress(load_addr, addr_class) | |
| : load_addr; | |
| } | |
| lldb::addr_t Target::GetOpcodeLoadAddress(lldb::addr_t load_addr, | |
| AddressClass addr_class) const { | |
| auto arch_plugin = GetArchitecturePlugin(); | |
| return arch_plugin ? arch_plugin->GetOpcodeLoadAddress(load_addr, addr_class) | |
| : load_addr; | |
| } | |
| lldb::addr_t Target::GetBreakableLoadAddress(lldb::addr_t addr) { | |
| auto arch_plugin = GetArchitecturePlugin(); | |
| return arch_plugin ? arch_plugin->GetBreakableLoadAddress(addr, *this) : addr; | |
| } | |
| llvm::Expected<lldb::DisassemblerSP> | |
| Target::ReadInstructions(const Address &start_addr, uint32_t count, | |
| const char *flavor_string) { | |
| DataBufferHeap data(GetArchitecture().GetMaximumOpcodeByteSize() * count, 0); | |
| bool force_live_memory = true; | |
| lldb_private::Status error; | |
| lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; | |
| const size_t bytes_read = | |
| ReadMemory(start_addr, data.GetBytes(), data.GetByteSize(), error, | |
| force_live_memory, &load_addr); | |
| if (error.Fail()) { | |
| return llvm::joinErrors( | |
| llvm::createStringErrorV( | |
| "Target::ReadInstructions failed to read memory at {:x}: ", | |
| start_addr.GetLoadAddress(this)), | |
| error.takeError()); | |
| } | |
| const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS; | |
| if (!flavor_string || flavor_string[0] == '\0') { | |
| // FIXME - we don't have the mechanism in place to do per-architecture | |
| // settings. But since we know that for now we only support flavors on | |
| // x86 & x86_64, | |
| const llvm::Triple::ArchType arch = GetArchitecture().GetTriple().getArch(); | |
| if (arch == llvm::Triple::x86 || arch == llvm::Triple::x86_64) | |
| flavor_string = GetDisassemblyFlavor(); | |
| } | |
| return Disassembler::DisassembleBytes( | |
| GetArchitecture(), nullptr, flavor_string, GetDisassemblyCPU(), | |
| GetDisassemblyFeatures(), start_addr, data.GetBytes(), bytes_read, count, | |
| data_from_file); | |
| } | |
| SourceManager &Target::GetSourceManager() { | |
| if (!m_source_manager_up) | |
| m_source_manager_up = std::make_unique<SourceManager>(shared_from_this()); | |
| return *m_source_manager_up; | |
| } | |
| Target::StopHookSP Target::CreateStopHook(StopHook::StopHookKind kind, | |
| bool internal) { | |
| user_id_t new_uid = (internal ? LLDB_INVALID_UID : ++m_stop_hook_next_id); | |
| Target::StopHookSP stop_hook_sp; | |
| switch (kind) { | |
| case StopHook::StopHookKind::CommandBased: | |
| stop_hook_sp.reset(new StopHookCommandLine(shared_from_this(), new_uid)); | |
| break; | |
| case StopHook::StopHookKind::ScriptBased: | |
| stop_hook_sp.reset(new StopHookScripted(shared_from_this(), new_uid)); | |
| break; | |
| case StopHook::StopHookKind::CodeBased: | |
| stop_hook_sp.reset(new StopHookCoded(shared_from_this(), new_uid)); | |
| break; | |
| } | |
| if (internal) | |
| m_internal_stop_hooks.push_back(stop_hook_sp); | |
| else | |
| m_stop_hooks[new_uid] = stop_hook_sp; | |
| return stop_hook_sp; | |
| } | |
| void Target::UndoCreateStopHook(lldb::user_id_t user_id) { | |
| if (!RemoveStopHookByID(user_id)) | |
| return; | |
| if (user_id == m_stop_hook_next_id) | |
| m_stop_hook_next_id--; | |
| } | |
| bool Target::RemoveStopHookByID(lldb::user_id_t user_id) { | |
| size_t num_removed = m_stop_hooks.erase(user_id); | |
| return (num_removed != 0); | |
| } | |
| void Target::RemoveAllStopHooks() { m_stop_hooks.clear(); } | |
| Target::StopHookSP Target::GetStopHookByID(lldb::user_id_t user_id) { | |
| StopHookSP found_hook; | |
| StopHookCollection::iterator specified_hook_iter; | |
| specified_hook_iter = m_stop_hooks.find(user_id); | |
| if (specified_hook_iter != m_stop_hooks.end()) | |
| found_hook = (*specified_hook_iter).second; | |
| return found_hook; | |
| } | |
| bool Target::SetStopHookActiveStateByID(lldb::user_id_t user_id, | |
| bool active_state) { | |
| StopHookCollection::iterator specified_hook_iter; | |
| specified_hook_iter = m_stop_hooks.find(user_id); | |
| if (specified_hook_iter == m_stop_hooks.end()) | |
| return false; | |
| (*specified_hook_iter).second->SetIsActive(active_state); | |
| return true; | |
| } | |
| void Target::SetAllStopHooksActiveState(bool active_state) { | |
| StopHookCollection::iterator pos, end = m_stop_hooks.end(); | |
| for (pos = m_stop_hooks.begin(); pos != end; pos++) { | |
| (*pos).second->SetIsActive(active_state); | |
| } | |
| } | |
| // FIXME: Ideally we would like to return a `const &` (const reference) instead | |
| // of creating copy here, but that is not possible due to different container | |
| // types. In C++20, we should be able to use `std::ranges::views::values` to | |
| // adapt the key-pair entries in the `std::map` (behind `StopHookCollection`) | |
| // to avoid creating the copy. | |
| const std::vector<Target::StopHookSP> | |
| Target::GetStopHooks(bool internal) const { | |
| if (internal) | |
| return m_internal_stop_hooks; | |
| std::vector<StopHookSP> stop_hooks; | |
| for (auto &[_, hook] : m_stop_hooks) | |
| stop_hooks.push_back(hook); | |
| return stop_hooks; | |
| } | |
| bool Target::RunStopHooks(bool at_initial_stop) { | |
| if (m_suppress_stop_hooks) | |
| return false; | |
| if (!m_process_sp) | |
| return false; | |
| // Somebody might have restarted the process: | |
| // Still return false, the return value is about US restarting the target. | |
| lldb::StateType state = m_process_sp->GetState(); | |
| if (!(state == eStateStopped || state == eStateAttaching)) | |
| return false; | |
| auto is_active = [at_initial_stop](StopHookSP hook) { | |
| bool should_run_now = (!at_initial_stop || hook->GetRunAtInitialStop()); | |
| return hook->IsActive() && should_run_now; | |
| }; | |
| // Create list of active internal and user stop hooks. | |
| std::vector<StopHookSP> active_hooks; | |
| llvm::copy_if(m_internal_stop_hooks, std::back_inserter(active_hooks), | |
| is_active); | |
| for (auto &[_, hook] : m_stop_hooks) { | |
| if (is_active(hook)) | |
| active_hooks.push_back(hook); | |
| } | |
| // Also collect unified hooks that fire on process stop. | |
| std::vector<HookSP> active_unified_hooks; | |
| for (auto &[_, hook] : m_hooks) { | |
| if (hook->IsEnabled() && hook->FiresOn(Hook::kProcessStop) && | |
| (!at_initial_stop || hook->GetRunAtInitialStop())) | |
| active_unified_hooks.push_back(hook); | |
| } | |
| if (active_hooks.empty() && active_unified_hooks.empty()) | |
| return false; | |
| // Make sure we check that we are not stopped because of us running a user | |
| // expression since in that case we do not want to run the stop-hooks. Note, | |
| // you can't just check whether the last stop was for a User Expression, | |
| // because breakpoint commands get run before stop hooks, and one of them | |
| // might have run an expression. You have to ensure you run the stop hooks | |
| // once per natural stop. | |
| uint32_t last_natural_stop = m_process_sp->GetModIDRef().GetLastNaturalStopID(); | |
| if (last_natural_stop != 0 && m_latest_stop_hook_id == last_natural_stop) | |
| return false; | |
| std::vector<ExecutionContext> exc_ctx_with_reasons; | |
| ThreadList &cur_threadlist = m_process_sp->GetThreadList(); | |
| size_t num_threads = cur_threadlist.GetSize(); | |
| for (size_t i = 0; i < num_threads; i++) { | |
| lldb::ThreadSP cur_thread_sp = cur_threadlist.GetThreadAtIndex(i); | |
| if (cur_thread_sp->ThreadStoppedForAReason()) { | |
| lldb::StackFrameSP cur_frame_sp = cur_thread_sp->GetStackFrameAtIndex(0); | |
| exc_ctx_with_reasons.emplace_back(m_process_sp.get(), cur_thread_sp.get(), | |
| cur_frame_sp.get()); | |
| } | |
| } | |
| // If no threads stopped for a reason, don't run the stop-hooks. | |
| // However, if this is the FIRST stop for this process, then we are in the | |
| // state where an attach or a core file load was completed without designating | |
| // a particular thread as responsible for the stop. In that case, we do | |
| // want to run the stop hooks, but do so just on one thread. | |
| size_t num_exe_ctx = exc_ctx_with_reasons.size(); | |
| if (num_exe_ctx == 0) { | |
| if (at_initial_stop && num_threads > 0) { | |
| lldb::ThreadSP thread_to_use_sp = cur_threadlist.GetThreadAtIndex(0); | |
| exc_ctx_with_reasons.emplace_back( | |
| m_process_sp.get(), thread_to_use_sp.get(), | |
| thread_to_use_sp->GetStackFrameAtIndex(0).get()); | |
| num_exe_ctx = 1; | |
| } else { | |
| return false; | |
| } | |
| } | |
| m_latest_stop_hook_id = last_natural_stop; | |
| StreamSP output_sp = m_debugger.GetAsyncOutputStream(); | |
| llvm::scope_exit on_exit([output_sp] { output_sp->Flush(); }); | |
| size_t num_hooks_with_output = llvm::count_if( | |
| active_hooks, [](auto h) { return !h->GetSuppressOutput(); }); | |
| num_hooks_with_output += llvm::count_if( | |
| active_unified_hooks, [](auto h) { return !h->GetSuppressOutput(); }); | |
| bool print_hook_header = (num_hooks_with_output > 1); | |
| bool print_thread_header = (num_exe_ctx > 1); | |
| bool should_stop = false; | |
| bool requested_continue = false; | |
| // A stop hook might get deleted while running stop hooks. | |
| // We have to decide what that means. We will follow the rule that deleting | |
| // a stop hook while processing these stop hooks will delete it for FUTURE | |
| // stops but not this stop. Fortunately, copying the m_stop_hooks to the | |
| // active_hooks list before iterating over the hooks has this effect. | |
| for (auto cur_hook_sp : active_hooks) { | |
| bool any_thread_matched = false; | |
| for (auto exc_ctx : exc_ctx_with_reasons) { | |
| if (!cur_hook_sp->ExecutionContextPasses(exc_ctx)) | |
| continue; | |
| bool suppress_output = cur_hook_sp->GetSuppressOutput(); | |
| if (print_hook_header && !any_thread_matched && !suppress_output) { | |
| StreamString s; | |
| cur_hook_sp->GetDescription(s, eDescriptionLevelBrief); | |
| if (s.GetSize() != 0) | |
| output_sp->Printf("\n- Hook %" PRIu64 " (%s)\n", cur_hook_sp->GetID(), | |
| s.GetData()); | |
| else | |
| output_sp->Printf("\n- Hook %" PRIu64 "\n", cur_hook_sp->GetID()); | |
| any_thread_matched = true; | |
| } | |
| if (print_thread_header && !suppress_output) | |
| output_sp->Printf("-- Thread %d\n", | |
| exc_ctx.GetThreadPtr()->GetIndexID()); | |
| auto result = cur_hook_sp->HandleStop(exc_ctx, output_sp); | |
| switch (result) { | |
| case StopHook::StopHookResult::KeepStopped: | |
| if (cur_hook_sp->GetAutoContinue()) | |
| requested_continue = true; | |
| else | |
| should_stop = true; | |
| break; | |
| case StopHook::StopHookResult::RequestContinue: | |
| requested_continue = true; | |
| break; | |
| case StopHook::StopHookResult::NoPreference: | |
| // Do nothing | |
| break; | |
| case StopHook::StopHookResult::AlreadyContinued: | |
| // We don't have a good way to prohibit people from restarting the | |
| // target willy nilly in a stop hook. If the hook did so, give a | |
| // gentle suggestion here and back out of the hook processing. | |
| output_sp->Printf("\nAborting stop hooks, hook %" PRIu64 | |
| " set the program running.\n" | |
| " Consider using '-G true' to make " | |
| "stop hooks auto-continue.\n", | |
| cur_hook_sp->GetID()); | |
| // FIXME: if we are doing non-stop mode for real, we would have to | |
| // check that OUR thread was restarted, otherwise we should keep | |
| // processing stop hooks. | |
| return true; | |
| } | |
| } | |
| } | |
| // Run unified hooks that fire on process stop. | |
| for (auto cur_hook_sp : active_unified_hooks) { | |
| bool any_thread_matched = false; | |
| for (auto exc_ctx : exc_ctx_with_reasons) { | |
| if (!cur_hook_sp->ExecutionContextPasses(exc_ctx)) | |
| continue; | |
| bool suppress_output = cur_hook_sp->GetSuppressOutput(); | |
| if (print_hook_header && !any_thread_matched && !suppress_output) { | |
| StreamString s; | |
| cur_hook_sp->GetDescription(s, eDescriptionLevelBrief); | |
| if (s.GetSize() != 0) | |
| output_sp->Printf("\n- Hook %" PRIu64 " (%s)\n", cur_hook_sp->GetID(), | |
| s.GetData()); | |
| else | |
| output_sp->Printf("\n- Hook %" PRIu64 "\n", cur_hook_sp->GetID()); | |
| any_thread_matched = true; | |
| } | |
| if (print_thread_header && !suppress_output) | |
| output_sp->Printf("-- Thread %d\n", | |
| exc_ctx.GetThreadPtr()->GetIndexID()); | |
| auto result = cur_hook_sp->HandleStop(exc_ctx, output_sp); | |
| switch (result) { | |
| case StopHook::StopHookResult::KeepStopped: | |
| if (cur_hook_sp->GetAutoContinue()) | |
| requested_continue = true; | |
| else | |
| should_stop = true; | |
| break; | |
| case StopHook::StopHookResult::RequestContinue: | |
| requested_continue = true; | |
| break; | |
| case StopHook::StopHookResult::NoPreference: | |
| break; | |
| case StopHook::StopHookResult::AlreadyContinued: | |
| output_sp->Printf("\nAborting stop hooks, hook %" PRIu64 | |
| " set the program running.\n" | |
| " Consider using '-G true' to make " | |
| "stop hooks auto-continue.\n", | |
| cur_hook_sp->GetID()); | |
| return true; | |
| } | |
| } | |
| } | |
| // Resume iff at least one hook requested to continue and no hook asked to | |
| // stop. | |
| if (requested_continue && !should_stop) { | |
| Log *log = GetLog(LLDBLog::Process); | |
| Status error = m_process_sp->PrivateResume(); | |
| if (error.Success()) { | |
| LLDB_LOG(log, "Resuming from RunStopHooks"); | |
| return true; | |
| } else { | |
| LLDB_LOG(log, "Resuming from RunStopHooks failed: {0}", error); | |
| return false; | |
| } | |
| } | |
| return false; | |
| } | |
| TargetProperties &Target::GetGlobalProperties() { | |
| // NOTE: intentional leak so we don't crash if global destructor chain gets | |
| // called as other threads still use the result of this function | |
| static TargetProperties *g_settings_ptr = | |
| new TargetProperties(nullptr); | |
| return *g_settings_ptr; | |
| } | |
| Status Target::Install(ProcessLaunchInfo *launch_info) { | |
| Status error; | |
| PlatformSP platform_sp(GetPlatform()); | |
| if (!platform_sp || !platform_sp->IsRemote() || !platform_sp->IsConnected()) | |
| return error; | |
| // Install all files that have an install path when connected to a | |
| // remote platform. If target.auto-install-main-executable is set then | |
| // also install the main executable even if it does not have an explicit | |
| // install path specified. | |
| for (auto module_sp : GetImages().Modules()) { | |
| if (module_sp == GetExecutableModule()) { | |
| MainExecutableInstaller installer{platform_sp, module_sp, | |
| shared_from_this(), *launch_info}; | |
| error = installExecutable(installer); | |
| } else { | |
| ExecutableInstaller installer{platform_sp, module_sp}; | |
| error = installExecutable(installer); | |
| } | |
| if (error.Fail()) | |
| return error; | |
| } | |
| return error; | |
| } | |
| bool Target::ResolveLoadAddress(addr_t load_addr, Address &so_addr, | |
| uint32_t stop_id, bool allow_section_end) { | |
| return m_section_load_history.ResolveLoadAddress(stop_id, load_addr, so_addr, | |
| allow_section_end); | |
| } | |
| bool Target::ResolveFileAddress(lldb::addr_t file_addr, | |
| Address &resolved_addr) { | |
| return m_images.ResolveFileAddress(file_addr, resolved_addr); | |
| } | |
| bool Target::SetSectionLoadAddress(const SectionSP §ion_sp, | |
| addr_t new_section_load_addr, | |
| bool warn_multiple) { | |
| const addr_t old_section_load_addr = | |
| m_section_load_history.GetSectionLoadAddress( | |
| SectionLoadHistory::eStopIDNow, section_sp); | |
| if (old_section_load_addr != new_section_load_addr) { | |
| uint32_t stop_id = 0; | |
| ProcessSP process_sp(GetProcessSP()); | |
| if (process_sp) | |
| stop_id = process_sp->GetStopID(); | |
| else | |
| stop_id = m_section_load_history.GetLastStopID(); | |
| if (m_section_load_history.SetSectionLoadAddress( | |
| stop_id, section_sp, new_section_load_addr, warn_multiple)) | |
| return true; // Return true if the section load address was changed... | |
| } | |
| return false; // Return false to indicate nothing changed | |
| } | |
| size_t Target::UnloadModuleSections(const ModuleList &module_list) { | |
| size_t section_unload_count = 0; | |
| size_t num_modules = module_list.GetSize(); | |
| for (size_t i = 0; i < num_modules; ++i) { | |
| section_unload_count += | |
| UnloadModuleSections(module_list.GetModuleAtIndex(i)); | |
| } | |
| return section_unload_count; | |
| } | |
| size_t Target::UnloadModuleSections(const lldb::ModuleSP &module_sp) { | |
| uint32_t stop_id = 0; | |
| ProcessSP process_sp(GetProcessSP()); | |
| if (process_sp) | |
| stop_id = process_sp->GetStopID(); | |
| else | |
| stop_id = m_section_load_history.GetLastStopID(); | |
| SectionList *sections = module_sp->GetSectionList(); | |
| size_t section_unload_count = 0; | |
| if (sections) { | |
| const uint32_t num_sections = sections->GetNumSections(0); | |
| for (uint32_t i = 0; i < num_sections; ++i) { | |
| section_unload_count += m_section_load_history.SetSectionUnloaded( | |
| stop_id, sections->GetSectionAtIndex(i)); | |
| } | |
| } | |
| return section_unload_count; | |
| } | |
| bool Target::SetSectionUnloaded(const lldb::SectionSP §ion_sp) { | |
| uint32_t stop_id = 0; | |
| ProcessSP process_sp(GetProcessSP()); | |
| if (process_sp) | |
| stop_id = process_sp->GetStopID(); | |
| else | |
| stop_id = m_section_load_history.GetLastStopID(); | |
| return m_section_load_history.SetSectionUnloaded(stop_id, section_sp); | |
| } | |
| bool Target::SetSectionUnloaded(const lldb::SectionSP §ion_sp, | |
| addr_t load_addr) { | |
| uint32_t stop_id = 0; | |
| ProcessSP process_sp(GetProcessSP()); | |
| if (process_sp) | |
| stop_id = process_sp->GetStopID(); | |
| else | |
| stop_id = m_section_load_history.GetLastStopID(); | |
| return m_section_load_history.SetSectionUnloaded(stop_id, section_sp, | |
| load_addr); | |
| } | |
| void Target::ClearAllLoadedSections() { m_section_load_history.Clear(); } | |
| lldb_private::SummaryStatisticsSP Target::GetSummaryStatisticsSPForProviderName( | |
| lldb_private::TypeSummaryImpl &summary_provider) { | |
| return m_summary_statistics_cache.GetSummaryStatisticsForProvider( | |
| summary_provider); | |
| } | |
| SummaryStatisticsCache &Target::GetSummaryStatisticsCache() { | |
| return m_summary_statistics_cache; | |
| } | |
| void Target::SaveScriptedLaunchInfo(lldb_private::ProcessInfo &process_info) { | |
| if (process_info.IsScriptedProcess()) { | |
| // Only copy scripted process launch options. | |
| ProcessLaunchInfo &default_launch_info = const_cast<ProcessLaunchInfo &>( | |
| GetGlobalProperties().GetProcessLaunchInfo()); | |
| default_launch_info.SetProcessPluginName("ScriptedProcess"); | |
| default_launch_info.SetScriptedMetadata(process_info.GetScriptedMetadata()); | |
| SetProcessLaunchInfo(default_launch_info); | |
| } | |
| } | |
| Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) { | |
| m_stats.SetLaunchOrAttachTime(); | |
| Status error; | |
| Log *log = GetLog(LLDBLog::Target); | |
| LLDB_LOGF(log, "Target::%s() called for %s", __FUNCTION__, | |
| launch_info.GetExecutableFile().GetPath().c_str()); | |
| StateType state = eStateInvalid; | |
| // Scope to temporarily get the process state in case someone has manually | |
| // remotely connected already to a process and we can skip the platform | |
| // launching. | |
| { | |
| ProcessSP process_sp(GetProcessSP()); | |
| if (process_sp) { | |
| state = process_sp->GetState(); | |
| LLDB_LOGF(log, | |
| "Target::%s the process exists, and its current state is %s", | |
| __FUNCTION__, StateAsCString(state)); | |
| } else { | |
| LLDB_LOGF(log, "Target::%s the process instance doesn't currently exist.", | |
| __FUNCTION__); | |
| } | |
| } | |
| launch_info.GetFlags().Set(eLaunchFlagDebug); | |
| SaveScriptedLaunchInfo(launch_info); | |
| // Get the value of synchronous execution here. If you wait till after you | |
| // have started to run, then you could have hit a breakpoint, whose command | |
| // might switch the value, and then you'll pick up that incorrect value. | |
| Debugger &debugger = GetDebugger(); | |
| const bool synchronous_execution = | |
| debugger.GetCommandInterpreter().GetSynchronous(); | |
| PlatformSP platform_sp(GetPlatform()); | |
| FinalizeFileActions(launch_info); | |
| if (state == eStateConnected) { | |
| if (launch_info.GetFlags().Test(eLaunchFlagLaunchInTTY)) | |
| return Status::FromErrorString( | |
| "can't launch in tty when launching through a remote connection"); | |
| } | |
| if (!launch_info.GetArchitecture().IsValid()) | |
| launch_info.GetArchitecture() = GetArchitecture(); | |
| // Hijacking events of the process to be created to be sure that all events | |
| // until the first stop are intercepted (in case if platform doesn't define | |
| // its own hijacking listener or if the process is created by the target | |
| // manually, without the platform). | |
| if (!launch_info.GetHijackListener()) | |
| launch_info.SetHijackListener( | |
| Listener::MakeListener(Process::LaunchSynchronousHijackListenerName)); | |
| // 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() && !launch_info.IsScriptedProcess()) { | |
| LLDB_LOGF(log, "Target::%s asking the platform to debug the process", | |
| __FUNCTION__); | |
| // If there was a previous process, delete it before we make the new one. | |
| // One subtle point, we delete the process before we release the reference | |
| // to m_process_sp. That way even if we are the last owner, the process | |
| // will get Finalized before it gets destroyed. | |
| DeleteCurrentProcess(); | |
| m_process_sp = | |
| GetPlatform()->DebugProcess(launch_info, debugger, *this, error); | |
| } else { | |
| LLDB_LOGF(log, | |
| "Target::%s the platform doesn't know how to debug a " | |
| "process, getting a process plugin to do this for us.", | |
| __FUNCTION__); | |
| if (state == eStateConnected) { | |
| assert(m_process_sp); | |
| } else { | |
| // Use a Process plugin to construct the process. | |
| CreateProcess(launch_info.GetListener(), | |
| launch_info.GetProcessPluginName(), nullptr, false); | |
| } | |
| // Since we didn't have a platform launch the process, launch it here. | |
| if (m_process_sp) { | |
| m_process_sp->HijackProcessEvents(launch_info.GetHijackListener()); | |
| m_process_sp->SetShadowListener(launch_info.GetShadowListener()); | |
| error = m_process_sp->Launch(launch_info); | |
| } | |
| } | |
| if (!error.Success()) | |
| return error; | |
| if (!m_process_sp) | |
| return Status::FromErrorString("failed to launch or debug process"); | |
| bool rebroadcast_first_stop = | |
| !synchronous_execution && | |
| launch_info.GetFlags().Test(eLaunchFlagStopAtEntry); | |
| assert(launch_info.GetHijackListener()); | |
| EventSP first_stop_event_sp; | |
| state = m_process_sp->WaitForProcessToStop(std::nullopt, &first_stop_event_sp, | |
| rebroadcast_first_stop, | |
| launch_info.GetHijackListener()); | |
| m_process_sp->RestoreProcessEvents(); | |
| if (rebroadcast_first_stop) { | |
| // We don't need to run the stop hooks by hand here, they will get | |
| // triggered when this rebroadcast event gets fetched. | |
| assert(first_stop_event_sp); | |
| m_process_sp->BroadcastEvent(first_stop_event_sp); | |
| return error; | |
| } | |
| // Run the stop hooks that want to run at entry. | |
| RunStopHooks(true /* at entry point */); | |
| switch (state) { | |
| case eStateStopped: { | |
| if (launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) | |
| break; | |
| if (synchronous_execution) | |
| // Now we have handled the stop-from-attach, and we are just | |
| // switching to a synchronous resume. So we should switch to the | |
| // SyncResume hijacker. | |
| m_process_sp->ResumeSynchronous(stream); | |
| else | |
| error = m_process_sp->Resume(); | |
| if (!error.Success()) { | |
| error = Status::FromErrorStringWithFormat( | |
| "process resume at entry point failed: %s", error.AsCString()); | |
| } | |
| } break; | |
| case eStateExited: { | |
| bool with_shell = !!launch_info.GetShell(); | |
| const int exit_status = m_process_sp->GetExitStatus(); | |
| const char *exit_desc = m_process_sp->GetExitDescription(); | |
| std::string desc; | |
| if (exit_desc && exit_desc[0]) | |
| desc = " (" + std::string(exit_desc) + ')'; | |
| if (with_shell) | |
| error = Status::FromErrorStringWithFormat( | |
| "process exited with status %i%s\n" | |
| "'r' and 'run' are aliases that default to launching through a " | |
| "shell.\n" | |
| "Try launching without going through a shell by using " | |
| "'process launch'.", | |
| exit_status, desc.c_str()); | |
| else | |
| error = Status::FromErrorStringWithFormat( | |
| "process exited with status %i%s", exit_status, desc.c_str()); | |
| } break; | |
| default: | |
| error = Status::FromErrorStringWithFormat( | |
| "initial process state wasn't stopped: %s", StateAsCString(state)); | |
| break; | |
| } | |
| return error; | |
| } | |
| void Target::SetTrace(const TraceSP &trace_sp) { m_trace_sp = trace_sp; } | |
| TraceSP Target::GetTrace() { return m_trace_sp; } | |
| llvm::Expected<TraceSP> Target::CreateTrace() { | |
| if (!m_process_sp) | |
| return llvm::createStringError(llvm::inconvertibleErrorCode(), | |
| "A process is required for tracing"); | |
| if (m_trace_sp) | |
| return llvm::createStringError(llvm::inconvertibleErrorCode(), | |
| "A trace already exists for the target"); | |
| llvm::Expected<TraceSupportedResponse> trace_type = | |
| m_process_sp->TraceSupported(); | |
| if (!trace_type) | |
| return llvm::createStringError( | |
| llvm::inconvertibleErrorCode(), "Tracing is not supported. %s", | |
| llvm::toString(trace_type.takeError()).c_str()); | |
| if (llvm::Expected<TraceSP> trace_sp = | |
| Trace::FindPluginForLiveProcess(trace_type->name, *m_process_sp)) | |
| m_trace_sp = *trace_sp; | |
| else | |
| return llvm::createStringError( | |
| llvm::inconvertibleErrorCode(), | |
| "Couldn't create a Trace object for the process. %s", | |
| llvm::toString(trace_sp.takeError()).c_str()); | |
| return m_trace_sp; | |
| } | |
| llvm::Expected<TraceSP> Target::GetTraceOrCreate() { | |
| if (m_trace_sp) | |
| return m_trace_sp; | |
| return CreateTrace(); | |
| } | |
| Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) { | |
| Progress attach_progress("Waiting to attach to process"); | |
| m_stats.SetLaunchOrAttachTime(); | |
| auto state = eStateInvalid; | |
| auto process_sp = GetProcessSP(); | |
| if (process_sp) { | |
| state = process_sp->GetState(); | |
| if (process_sp->IsAlive() && state != eStateConnected) { | |
| if (state == eStateAttaching) | |
| return Status::FromErrorString("process attach is in progress"); | |
| return Status::FromErrorString("a process is already being debugged"); | |
| } | |
| } | |
| const ModuleSP old_exec_module_sp = GetExecutableModule(); | |
| // If no process info was specified, then use the target executable name as | |
| // the process to attach to by default | |
| if (!attach_info.ProcessInfoSpecified()) { | |
| if (old_exec_module_sp) | |
| attach_info.GetExecutableFile().SetFilename( | |
| old_exec_module_sp->GetPlatformFileSpec().GetFilename()); | |
| if (!attach_info.ProcessInfoSpecified()) { | |
| return Status::FromErrorString( | |
| "no process specified, create a target with a file, or " | |
| "specify the --pid or --name"); | |
| } | |
| } | |
| const auto platform_sp = | |
| GetDebugger().GetPlatformList().GetSelectedPlatform(); | |
| ListenerSP hijack_listener_sp; | |
| const bool async = attach_info.GetAsync(); | |
| if (!async) { | |
| hijack_listener_sp = | |
| Listener::MakeListener(Process::AttachSynchronousHijackListenerName); | |
| attach_info.SetHijackListener(hijack_listener_sp); | |
| } | |
| Status error; | |
| if (state != eStateConnected && platform_sp != nullptr && | |
| platform_sp->CanDebugProcess() && !attach_info.IsScriptedProcess()) { | |
| SetPlatform(platform_sp); | |
| process_sp = platform_sp->Attach(attach_info, GetDebugger(), this, error); | |
| } else { | |
| if (state != eStateConnected) { | |
| SaveScriptedLaunchInfo(attach_info); | |
| llvm::StringRef plugin_name = attach_info.GetProcessPluginName(); | |
| process_sp = | |
| CreateProcess(attach_info.GetListenerForProcess(GetDebugger()), | |
| plugin_name, nullptr, false); | |
| if (!process_sp) { | |
| error = Status::FromErrorStringWithFormatv( | |
| "failed to create process using plugin '{0}'", | |
| plugin_name.empty() ? "<empty>" : plugin_name); | |
| return error; | |
| } | |
| } | |
| if (hijack_listener_sp) | |
| process_sp->HijackProcessEvents(hijack_listener_sp); | |
| error = process_sp->Attach(attach_info); | |
| } | |
| if (error.Success() && process_sp) { | |
| if (async) { | |
| process_sp->RestoreProcessEvents(); | |
| } else { | |
| // We are stopping all the way out to the user, so update selected frames. | |
| state = process_sp->WaitForProcessToStop( | |
| std::nullopt, nullptr, false, attach_info.GetHijackListener(), stream, | |
| true, SelectMostRelevantFrame); | |
| process_sp->RestoreProcessEvents(); | |
| // Run the stop hooks here. Since we were hijacking the events, they | |
| // wouldn't have gotten run as part of event delivery. | |
| RunStopHooks(/* at_initial_stop= */ true); | |
| if (state != eStateStopped) { | |
| const char *exit_desc = process_sp->GetExitDescription(); | |
| if (exit_desc) | |
| error = Status::FromErrorStringWithFormat("%s", exit_desc); | |
| else | |
| error = Status::FromErrorString( | |
| "process did not stop (no such process or permission problem?)"); | |
| process_sp->Destroy(false); | |
| } | |
| } | |
| } | |
| return error; | |
| } | |
| llvm::Expected<uint32_t> Target::AddScriptedFrameProviderDescriptor( | |
| const ScriptedFrameProviderDescriptor &descriptor) { | |
| if (!descriptor.IsValid()) | |
| return llvm::createStringError("invalid frame provider descriptor"); | |
| llvm::StringRef name = descriptor.GetName(); | |
| if (name.empty()) | |
| return llvm::createStringError( | |
| "frame provider descriptor has no class name"); | |
| { | |
| std::unique_lock<std::recursive_mutex> guard( | |
| m_frame_provider_descriptors_mutex); | |
| // Check for duplicate: same class name and args (content hash). | |
| uint32_t descriptor_hash = descriptor.GetHash(); | |
| for (const auto &entry : m_frame_provider_descriptors) { | |
| if (entry.second.GetHash() == descriptor_hash) | |
| GetDebugger().ReportWarning( | |
| llvm::formatv("frame provider idx={0} with the same class name and " | |
| "arguments is already registered", | |
| entry.second.GetID()) | |
| .str()); | |
| } | |
| uint32_t descriptor_id = m_next_frame_provider_id++; | |
| ScriptedFrameProviderDescriptor new_descriptor = descriptor; | |
| new_descriptor.SetID(descriptor_id); | |
| m_frame_provider_descriptors[descriptor_id] = new_descriptor; | |
| InvalidateThreadFrameProviders(); | |
| return descriptor_id; | |
| } | |
| } | |
| bool Target::RemoveScriptedFrameProviderDescriptor(uint32_t id) { | |
| bool removed = false; | |
| { | |
| std::lock_guard<std::recursive_mutex> guard( | |
| m_frame_provider_descriptors_mutex); | |
| removed = m_frame_provider_descriptors.erase(id); | |
| } | |
| if (removed) | |
| InvalidateThreadFrameProviders(); | |
| return removed; | |
| } | |
| void Target::ClearScriptedFrameProviderDescriptors() { | |
| { | |
| std::lock_guard<std::recursive_mutex> guard( | |
| m_frame_provider_descriptors_mutex); | |
| m_frame_provider_descriptors.clear(); | |
| m_next_frame_provider_id = 1; | |
| } | |
| InvalidateThreadFrameProviders(); | |
| } | |
| const llvm::MapVector<uint32_t, ScriptedFrameProviderDescriptor> & | |
| Target::GetScriptedFrameProviderDescriptors() const { | |
| std::lock_guard<std::recursive_mutex> guard( | |
| m_frame_provider_descriptors_mutex); | |
| return m_frame_provider_descriptors; | |
| } | |
| void Target::InvalidateThreadFrameProviders() { | |
| ProcessSP process_sp = GetProcessSP(); | |
| if (!process_sp) | |
| return; | |
| for (ThreadSP thread_sp : process_sp->Threads()) { | |
| // Clear frame providers on existing threads so they reload with new config. | |
| thread_sp->ClearScriptedFrameProvider(); | |
| // Notify threads that the stack traces might have changed. | |
| if (thread_sp->EventTypeHasListeners(Thread::eBroadcastBitStackChanged)) { | |
| auto data_sp = std::make_shared<Thread::ThreadEventData>(thread_sp); | |
| thread_sp->BroadcastEvent(Thread::eBroadcastBitStackChanged, data_sp); | |
| } | |
| } | |
| } | |
| void Target::FinalizeFileActions(ProcessLaunchInfo &info) { | |
| Log *log = GetLog(LLDBLog::Process); | |
| // Finalize the file actions, and if none were given, default to opening up a | |
| // pseudo terminal | |
| PlatformSP platform_sp = GetPlatform(); | |
| const bool default_to_use_pty = | |
| m_platform_sp ? m_platform_sp->IsHost() : false; | |
| LLDB_LOG( | |
| log, | |
| "have platform={0}, platform_sp->IsHost()={1}, default_to_use_pty={2}", | |
| bool(platform_sp), | |
| platform_sp ? (platform_sp->IsHost() ? "true" : "false") : "n/a", | |
| default_to_use_pty); | |
| // If nothing for stdin or stdout or stderr was specified, then check the | |
| // process for any default settings that were set with "settings set" | |
| if (info.GetFileActionForFD(STDIN_FILENO) == nullptr || | |
| info.GetFileActionForFD(STDOUT_FILENO) == nullptr || | |
| info.GetFileActionForFD(STDERR_FILENO) == nullptr) { | |
| LLDB_LOG(log, "at least one of stdin/stdout/stderr was not set, evaluating " | |
| "default handling"); | |
| if (info.GetFlags().Test(eLaunchFlagLaunchInTTY)) { | |
| // Do nothing, if we are launching in a remote terminal no file actions | |
| // should be done at all. | |
| return; | |
| } | |
| if (info.GetFlags().Test(eLaunchFlagDisableSTDIO)) { | |
| LLDB_LOG(log, "eLaunchFlagDisableSTDIO set, adding suppression action " | |
| "for stdin, stdout and stderr"); | |
| info.AppendSuppressFileAction(STDIN_FILENO, true, false); | |
| info.AppendSuppressFileAction(STDOUT_FILENO, false, true); | |
| info.AppendSuppressFileAction(STDERR_FILENO, false, true); | |
| } else { | |
| // Check for any values that might have gotten set with any of: (lldb) | |
| // settings set target.input-path (lldb) settings set target.output-path | |
| // (lldb) settings set target.error-path | |
| FileSpec in_file_spec; | |
| FileSpec out_file_spec; | |
| FileSpec err_file_spec; | |
| // Only override with the target settings if we don't already have an | |
| // action for in, out or error | |
| if (info.GetFileActionForFD(STDIN_FILENO) == nullptr) | |
| in_file_spec = GetStandardInputPath(); | |
| if (info.GetFileActionForFD(STDOUT_FILENO) == nullptr) | |
| out_file_spec = GetStandardOutputPath(); | |
| if (info.GetFileActionForFD(STDERR_FILENO) == nullptr) | |
| err_file_spec = GetStandardErrorPath(); | |
| LLDB_LOG(log, "target stdin='{0}', target stdout='{1}', stderr='{2}'", | |
| in_file_spec, out_file_spec, err_file_spec); | |
| if (in_file_spec) { | |
| info.AppendOpenFileAction(STDIN_FILENO, in_file_spec, true, false); | |
| LLDB_LOG(log, "appended stdin open file action for {0}", in_file_spec); | |
| } | |
| if (out_file_spec) { | |
| info.AppendOpenFileAction(STDOUT_FILENO, out_file_spec, false, true); | |
| LLDB_LOG(log, "appended stdout open file action for {0}", | |
| out_file_spec); | |
| } | |
| if (err_file_spec) { | |
| info.AppendOpenFileAction(STDERR_FILENO, err_file_spec, false, true); | |
| LLDB_LOG(log, "appended stderr open file action for {0}", | |
| err_file_spec); | |
| } | |
| if (default_to_use_pty) { | |
| #ifdef _WIN32 | |
| if (info.GetFlags().Test(eLaunchFlagUsePipes) || | |
| ::getenv("LLDB_LAUNCH_FLAG_USE_PIPES")) { | |
| llvm::Error Err = info.SetUpPipeRedirection(); | |
| LLDB_LOG_ERROR(log, std::move(Err), | |
| "SetUpPipeRedirection failed: {0}"); | |
| } else { | |
| #endif | |
| llvm::Error Err = info.SetUpPtyRedirection(); | |
| LLDB_LOG_ERROR(log, std::move(Err), | |
| "SetUpPtyRedirection failed: {0}"); | |
| #ifdef _WIN32 | |
| } | |
| #endif | |
| } | |
| } | |
| } | |
| } | |
| void Target::AddDummySignal(llvm::StringRef name, LazyBool pass, LazyBool notify, | |
| LazyBool stop) { | |
| if (name.empty()) | |
| return; | |
| // Don't add a signal if all the actions are trivial: | |
| if (pass == eLazyBoolCalculate && notify == eLazyBoolCalculate | |
| && stop == eLazyBoolCalculate) | |
| return; | |
| auto& elem = m_dummy_signals[name]; | |
| elem.pass = pass; | |
| elem.notify = notify; | |
| elem.stop = stop; | |
| } | |
| bool Target::UpdateSignalFromDummy(UnixSignalsSP signals_sp, | |
| const DummySignalElement &elem) { | |
| if (!signals_sp) | |
| return false; | |
| int32_t signo | |
| = signals_sp->GetSignalNumberFromName(elem.first().str().c_str()); | |
| if (signo == LLDB_INVALID_SIGNAL_NUMBER) | |
| return false; | |
| if (elem.second.pass == eLazyBoolYes) | |
| signals_sp->SetShouldSuppress(signo, false); | |
| else if (elem.second.pass == eLazyBoolNo) | |
| signals_sp->SetShouldSuppress(signo, true); | |
| if (elem.second.notify == eLazyBoolYes) | |
| signals_sp->SetShouldNotify(signo, true); | |
| else if (elem.second.notify == eLazyBoolNo) | |
| signals_sp->SetShouldNotify(signo, false); | |
| if (elem.second.stop == eLazyBoolYes) | |
| signals_sp->SetShouldStop(signo, true); | |
| else if (elem.second.stop == eLazyBoolNo) | |
| signals_sp->SetShouldStop(signo, false); | |
| return true; | |
| } | |
| bool Target::ResetSignalFromDummy(UnixSignalsSP signals_sp, | |
| const DummySignalElement &elem) { | |
| if (!signals_sp) | |
| return false; | |
| int32_t signo | |
| = signals_sp->GetSignalNumberFromName(elem.first().str().c_str()); | |
| if (signo == LLDB_INVALID_SIGNAL_NUMBER) | |
| return false; | |
| bool do_pass = elem.second.pass != eLazyBoolCalculate; | |
| bool do_stop = elem.second.stop != eLazyBoolCalculate; | |
| bool do_notify = elem.second.notify != eLazyBoolCalculate; | |
| signals_sp->ResetSignal(signo, do_stop, do_notify, do_pass); | |
| return true; | |
| } | |
| void Target::UpdateSignalsFromDummy(UnixSignalsSP signals_sp, | |
| StreamSP warning_stream_sp) { | |
| if (!signals_sp) | |
| return; | |
| for (const auto &elem : m_dummy_signals) { | |
| if (!UpdateSignalFromDummy(signals_sp, elem)) | |
| warning_stream_sp->Printf("Target signal '%s' not found in process\n", | |
| elem.first().str().c_str()); | |
| } | |
| } | |
| void Target::ClearDummySignals(Args &signal_names) { | |
| ProcessSP process_sp = GetProcessSP(); | |
| // The simplest case, delete them all with no process to update. | |
| if (signal_names.GetArgumentCount() == 0 && !process_sp) { | |
| m_dummy_signals.clear(); | |
| return; | |
| } | |
| UnixSignalsSP signals_sp; | |
| if (process_sp) | |
| signals_sp = process_sp->GetUnixSignals(); | |
| for (const Args::ArgEntry &entry : signal_names) { | |
| const char *signal_name = entry.c_str(); | |
| auto elem = m_dummy_signals.find(signal_name); | |
| // If we didn't find it go on. | |
| // FIXME: Should I pipe error handling through here? | |
| if (elem == m_dummy_signals.end()) { | |
| continue; | |
| } | |
| if (signals_sp) | |
| ResetSignalFromDummy(signals_sp, *elem); | |
| m_dummy_signals.erase(elem); | |
| } | |
| } | |
| void Target::PrintDummySignals(Stream &strm, Args &signal_args) { | |
| strm.PutCString("NAME PASS STOP NOTIFY\n"); | |
| strm.PutCString("=========== ======= ======= =======\n"); | |
| auto str_for_lazy = [] (LazyBool lazy) -> const char * { | |
| switch (lazy) { | |
| case eLazyBoolCalculate: return "not set"; | |
| case eLazyBoolYes: return "true "; | |
| case eLazyBoolNo: return "false "; | |
| } | |
| llvm_unreachable("Fully covered switch above!"); | |
| }; | |
| size_t num_args = signal_args.GetArgumentCount(); | |
| for (const auto &elem : m_dummy_signals) { | |
| bool print_it = false; | |
| for (size_t idx = 0; idx < num_args; idx++) { | |
| if (elem.first() == signal_args.GetArgumentAtIndex(idx)) { | |
| print_it = true; | |
| break; | |
| } | |
| } | |
| if (print_it) { | |
| strm.Printf("%-11s ", elem.first().str().c_str()); | |
| strm.Printf("%s %s %s\n", str_for_lazy(elem.second.pass), | |
| str_for_lazy(elem.second.stop), | |
| str_for_lazy(elem.second.notify)); | |
| } | |
| } | |
| } | |
| // Target::StopHook | |
| Target::StopHook::StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid) | |
| : UserID(uid), m_target_sp(target_sp), m_specifier_sp(), | |
| m_thread_spec_up() {} | |
| Target::StopHook::StopHook(const StopHook &rhs) | |
| : UserID(rhs.GetID()), m_target_sp(rhs.m_target_sp), | |
| m_specifier_sp(rhs.m_specifier_sp), m_thread_spec_up(), | |
| m_active(rhs.m_active), m_auto_continue(rhs.m_auto_continue) { | |
| if (rhs.m_thread_spec_up) | |
| m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up); | |
| } | |
| void Target::StopHook::SetSpecifier(SymbolContextSpecifier *specifier) { | |
| m_specifier_sp.reset(specifier); | |
| } | |
| void Target::StopHook::SetThreadSpecifier(ThreadSpec *specifier) { | |
| m_thread_spec_up.reset(specifier); | |
| } | |
| bool Target::StopHook::ExecutionContextPasses(const ExecutionContext &exc_ctx) { | |
| SymbolContextSpecifier *specifier = GetSpecifier(); | |
| if (!specifier) | |
| return true; | |
| bool will_run = true; | |
| if (exc_ctx.GetFramePtr()) | |
| will_run = GetSpecifier()->SymbolContextMatches( | |
| exc_ctx.GetFramePtr()->GetSymbolContext(eSymbolContextEverything)); | |
| if (will_run && GetThreadSpecifier() != nullptr) | |
| will_run = | |
| GetThreadSpecifier()->ThreadPassesBasicTests(exc_ctx.GetThreadRef()); | |
| return will_run; | |
| } | |
| void Target::StopHook::GetDescription(Stream &s, | |
| lldb::DescriptionLevel level) const { | |
| // For brief descriptions, only print the subclass description: | |
| if (level == eDescriptionLevelBrief) { | |
| GetSubclassDescription(s, level); | |
| return; | |
| } | |
| auto indent_scope = s.MakeIndentScope(); | |
| s.Printf("Hook: %" PRIu64 "\n", GetID()); | |
| if (m_active) | |
| s.Indent("State: enabled\n"); | |
| else | |
| s.Indent("State: disabled\n"); | |
| if (m_auto_continue) | |
| s.Indent("AutoContinue on\n"); | |
| if (m_specifier_sp) { | |
| s.Indent(); | |
| s.PutCString("Specifier:\n"); | |
| auto indent_scope = s.MakeIndentScope(); | |
| m_specifier_sp->GetDescription(&s, level); | |
| } | |
| if (m_thread_spec_up) { | |
| StreamString tmp; | |
| s.Indent("Thread:\n"); | |
| m_thread_spec_up->GetDescription(&tmp, level); | |
| auto indent_scope = s.MakeIndentScope(); | |
| s.Indent(tmp.GetString()); | |
| s.PutCString("\n"); | |
| } | |
| GetSubclassDescription(s, level); | |
| } | |
| void Target::StopHookCommandLine::GetSubclassDescription( | |
| Stream &s, lldb::DescriptionLevel level) const { | |
| // The brief description just prints the first command. | |
| if (level == eDescriptionLevelBrief) { | |
| if (m_commands.GetSize() == 1) | |
| s.PutCString(m_commands.GetStringAtIndex(0)); | |
| return; | |
| } | |
| s.Indent("Commands:\n"); | |
| auto indent_scope = s.MakeIndentScope(4); | |
| uint32_t num_commands = m_commands.GetSize(); | |
| for (uint32_t i = 0; i < num_commands; i++) { | |
| s.Indent(m_commands.GetStringAtIndex(i)); | |
| s.PutCString("\n"); | |
| } | |
| } | |
| // Target::StopHookCommandLine | |
| void Target::StopHookCommandLine::SetActionFromString(const std::string &string) { | |
| GetCommands().SplitIntoLines(string); | |
| } | |
| void Target::StopHookCommandLine::SetActionFromStrings( | |
| const std::vector<std::string> &strings) { | |
| for (auto string : strings) | |
| GetCommands().AppendString(string.c_str()); | |
| } | |
| Target::StopHook::StopHookResult | |
| Target::StopHookCommandLine::HandleStop(ExecutionContext &exc_ctx, | |
| StreamSP output_sp) { | |
| assert(exc_ctx.GetTargetPtr() && "Can't call PerformAction on a context " | |
| "with no target"); | |
| if (!m_commands.GetSize()) | |
| return StopHookResult::KeepStopped; | |
| CommandReturnObject result(false); | |
| result.SetImmediateOutputStream(output_sp); | |
| result.SetImmediateErrorStream(output_sp); | |
| result.SetInteractive(false); | |
| Debugger &debugger = exc_ctx.GetTargetPtr()->GetDebugger(); | |
| CommandInterpreterRunOptions options; | |
| options.SetStopOnContinue(true); | |
| options.SetStopOnError(true); | |
| options.SetEchoCommands(false); | |
| options.SetPrintResults(true); | |
| options.SetPrintErrors(true); | |
| options.SetAddToHistory(false); | |
| // Force Async: | |
| bool old_async = debugger.GetAsyncExecution(); | |
| debugger.SetAsyncExecution(true); | |
| debugger.GetCommandInterpreter().HandleCommands(GetCommands(), exc_ctx, | |
| options, result); | |
| debugger.SetAsyncExecution(old_async); | |
| lldb::ReturnStatus status = result.GetStatus(); | |
| if (status == eReturnStatusSuccessContinuingNoResult || | |
| status == eReturnStatusSuccessContinuingResult) | |
| return StopHookResult::AlreadyContinued; | |
| return StopHookResult::KeepStopped; | |
| } | |
| // Target::StopHookScripted | |
| Status Target::StopHookScripted::SetScriptCallback( | |
| const ScriptedMetadata &scripted_metadata) { | |
| Status error; | |
| ScriptInterpreter *script_interp = | |
| GetTarget()->GetDebugger().GetScriptInterpreter(); | |
| if (!script_interp) { | |
| error = Status::FromErrorString("No script interpreter installed."); | |
| return error; | |
| } | |
| m_interface_sp = script_interp->CreateScriptedHookInterface(); | |
| if (!m_interface_sp) { | |
| error = Status::FromErrorStringWithFormat( | |
| "ScriptedStopHook::%s () - ERROR: %s", __FUNCTION__, | |
| "Script interpreter couldn't create Scripted Stop Hook Interface"); | |
| return error; | |
| } | |
| auto obj_or_err = | |
| m_interface_sp->CreatePluginObject(scripted_metadata, GetTarget()); | |
| if (!obj_or_err) { | |
| return Status::FromError(obj_or_err.takeError()); | |
| } | |
| StructuredData::ObjectSP object_sp = *obj_or_err; | |
| if (!object_sp || !object_sp->IsValid()) { | |
| error = Status::FromErrorStringWithFormat( | |
| "ScriptedStopHook::%s () - ERROR: %s", __FUNCTION__, | |
| "Failed to create valid script object"); | |
| return error; | |
| } | |
| return {}; | |
| } | |
| /// Hook callbacks have no caller to return an error to, so report a failure | |
| /// as a debugger diagnostic. \a what names the hook, e.g. "stop hook 1". | |
| static void ReportScriptedHookError(llvm::Error error, llvm::StringRef what, | |
| Debugger &debugger) { | |
| if (!error) | |
| return; | |
| Debugger::ReportError( | |
| llvm::formatv("{0} failed: {1}", what, llvm::toString(std::move(error))) | |
| .str(), | |
| debugger.GetID()); | |
| } | |
| Target::StopHook::StopHookResult | |
| Target::StopHookScripted::HandleStop(ExecutionContext &exc_ctx, | |
| StreamSP output_sp) { | |
| assert(exc_ctx.GetTargetPtr() && "Can't call HandleStop on a context " | |
| "with no target"); | |
| if (!m_interface_sp) | |
| return StopHookResult::KeepStopped; | |
| lldb::StreamSP stream = std::make_shared<lldb_private::StreamString>(); | |
| auto should_stop_or_err = m_interface_sp->HandleStop(exc_ctx, stream); | |
| output_sp->PutCString( | |
| reinterpret_cast<StreamString *>(stream.get())->GetData()); | |
| if (!should_stop_or_err) { | |
| ReportScriptedHookError(should_stop_or_err.takeError(), | |
| llvm::formatv("stop hook {0}", GetID()).str(), | |
| exc_ctx.GetTargetPtr()->GetDebugger()); | |
| return StopHookResult::KeepStopped; | |
| } | |
| return *should_stop_or_err ? StopHookResult::KeepStopped | |
| : StopHookResult::RequestContinue; | |
| } | |
| llvm::StringRef Target::StopHookScripted::GetScriptClassName() const { | |
| if (m_interface_sp && m_interface_sp->GetScriptedMetadata()) | |
| return m_interface_sp->GetScriptedMetadata()->GetClassName(); | |
| return "<unknown>"; | |
| } | |
| void Target::StopHookScripted::GetSubclassDescription( | |
| Stream &s, lldb::DescriptionLevel level) const { | |
| llvm::StringRef class_name = GetScriptClassName(); | |
| if (level == eDescriptionLevelBrief) { | |
| s.PutCString(class_name); | |
| return; | |
| } | |
| s.Indent("Class:"); | |
| s.Format("{0}\n", class_name); | |
| // Now print the extra args: | |
| // FIXME: We should use StructuredData.GetDescription on the args dict | |
| // but that seems to rely on some printing plugin that doesn't exist. | |
| StructuredData::DictionarySP as_dict = | |
| (m_interface_sp && m_interface_sp->GetScriptedMetadata()) | |
| ? m_interface_sp->GetScriptedMetadata()->GetArgsSP() | |
| : nullptr; | |
| if (!as_dict || !as_dict->IsValid()) | |
| return; | |
| uint32_t num_keys = as_dict->GetSize(); | |
| if (num_keys == 0) | |
| return; | |
| s.Indent("Args:\n"); | |
| auto indent_scope = s.MakeIndentScope(4); | |
| auto print_one_element = [&s](llvm::StringRef key, | |
| StructuredData::Object *object) { | |
| s.Indent(); | |
| s.Format("{0} : {1}\n", key, object->GetStringValue()); | |
| return true; | |
| }; | |
| as_dict->ForEach(print_one_element); | |
| } | |
| // Hook | |
| Target::Hook::Hook(lldb::TargetSP target_sp, lldb::user_id_t uid, HookKind kind) | |
| : UserID(uid), m_target_sp(std::move(target_sp)), m_kind(kind) {} | |
| Target::Hook::Hook(const Hook &rhs) | |
| : UserID(rhs.GetID()), m_target_sp(rhs.m_target_sp), m_kind(rhs.m_kind), | |
| m_enabled(rhs.m_enabled), m_trigger_mask(rhs.m_trigger_mask), | |
| m_sc_specifier_sp(rhs.m_sc_specifier_sp), | |
| m_at_initial_stop(rhs.m_at_initial_stop), | |
| m_auto_continue(rhs.m_auto_continue), | |
| m_suppress_output(rhs.m_suppress_output) { | |
| if (rhs.m_thread_spec_up) | |
| m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up); | |
| } | |
| void Target::Hook::SetSCSpecifier(SymbolContextSpecifier *specifier) { | |
| m_sc_specifier_sp.reset(specifier); | |
| } | |
| void Target::Hook::SetThreadSpecifier(ThreadSpec *specifier) { | |
| m_thread_spec_up.reset(specifier); | |
| } | |
| bool Target::Hook::ExecutionContextPasses(const ExecutionContext &exc_ctx) { | |
| SymbolContextSpecifier *specifier = GetSCSpecifier(); | |
| if (!specifier) | |
| return true; | |
| bool will_run = true; | |
| if (exc_ctx.GetFramePtr()) | |
| will_run = specifier->SymbolContextMatches( | |
| exc_ctx.GetFramePtr()->GetSymbolContext(eSymbolContextEverything)); | |
| if (will_run && GetThreadSpecifier() != nullptr) | |
| will_run = | |
| GetThreadSpecifier()->ThreadPassesBasicTests(exc_ctx.GetThreadRef()); | |
| return will_run; | |
| } | |
| void Target::Hook::GetDescription(Stream &s, | |
| lldb::DescriptionLevel level) const { | |
| s.Printf("Hook: %" PRIu64 "\n", GetID()); | |
| if (level == eDescriptionLevelBrief) | |
| return; | |
| s.IndentMore(); | |
| s.Indent(); | |
| s.Printf("State: %s\n", m_enabled ? "enabled" : "disabled"); | |
| { | |
| std::string fires_on; | |
| if (m_trigger_mask & kModulesLoaded) | |
| fires_on += "load"; | |
| if (m_trigger_mask & kModulesUnloaded) { | |
| if (!fires_on.empty()) | |
| fires_on += ", "; | |
| fires_on += "unload"; | |
| } | |
| if (m_trigger_mask & kProcessStop) { | |
| if (!fires_on.empty()) | |
| fires_on += ", "; | |
| fires_on += "stop"; | |
| } | |
| if (!fires_on.empty()) { | |
| s.Indent(); | |
| s.Printf("Triggers: %s\n", fires_on.c_str()); | |
| } | |
| } | |
| // Subclasses add their content (commands or class) then call | |
| // GetFilterDescription to print filters. | |
| s.IndentLess(); | |
| } | |
| void Target::Hook::GetFilterDescription(Stream &s, | |
| lldb::DescriptionLevel level) const { | |
| s.IndentMore(); | |
| if (m_auto_continue) | |
| s.Indent("AutoContinue on\n"); | |
| if (m_sc_specifier_sp) { | |
| s.Indent(); | |
| s.PutCString("Specifier:\n"); | |
| s.IndentMore(); | |
| m_sc_specifier_sp->GetDescription(&s, level); | |
| s.IndentLess(); | |
| } | |
| if (m_thread_spec_up) { | |
| StreamString tmp; | |
| s.Indent("Thread:\n"); | |
| m_thread_spec_up->GetDescription(&tmp, level); | |
| s.IndentMore(); | |
| s.Indent(tmp.GetString()); | |
| s.PutCString("\n"); | |
| s.IndentLess(); | |
| } | |
| s.IndentLess(); | |
| } | |
| void Target::HookCommandLine::GetDescription( | |
| Stream &s, lldb::DescriptionLevel level) const { | |
| Hook::GetDescription(s, level); | |
| if (level == eDescriptionLevelBrief) { | |
| if (m_commands.GetSize() == 1) | |
| s.PutCString(m_commands.GetStringAtIndex(0)); | |
| else | |
| s.Printf("%" PRIu64 " commands", (uint64_t)m_commands.GetSize()); | |
| return; | |
| } | |
| // Commands come after the header (ID, State, Triggers) but before filters. | |
| s.IndentMore(); | |
| s.Indent("Commands: \n"); | |
| s.IndentMore(); | |
| for (uint32_t i = 0; i < m_commands.GetSize(); i++) { | |
| s.Indent(m_commands.GetStringAtIndex(i)); | |
| s.PutCString("\n"); | |
| } | |
| s.IndentLess(); | |
| s.IndentLess(); | |
| GetFilterDescription(s, level); | |
| } | |
| // HookCommandLine | |
| void Target::HookCommandLine::SetActionFromString(const std::string &string) { | |
| GetCommands().SplitIntoLines(string); | |
| } | |
| void Target::HookCommandLine::SetActionFromStrings( | |
| const std::vector<std::string> &strings) { | |
| for (const auto &string : strings) | |
| GetCommands().AppendString(string.c_str()); | |
| } | |
| void Target::HookCommandLine::HandleModuleLoaded(StreamSP output_sp) { | |
| if (!m_commands.GetSize()) | |
| return; | |
| TargetSP target_sp = GetTarget(); | |
| if (!target_sp) | |
| return; | |
| CommandReturnObject result(false); | |
| result.SetImmediateOutputStream(output_sp); | |
| result.SetInteractive(false); | |
| Debugger &debugger = target_sp->GetDebugger(); | |
| ExecutionContext exe_ctx; | |
| if (target_sp->GetProcessSP()) | |
| exe_ctx.SetContext(target_sp->GetProcessSP()); | |
| else | |
| exe_ctx.SetContext(target_sp, false); | |
| CommandInterpreterRunOptions options; | |
| options.SetStopOnContinue(true); | |
| options.SetStopOnError(true); | |
| options.SetEchoCommands(false); | |
| options.SetPrintResults(true); | |
| options.SetPrintErrors(true); | |
| options.SetAddToHistory(false); | |
| bool old_async = debugger.GetAsyncExecution(); | |
| debugger.SetAsyncExecution(true); | |
| debugger.GetCommandInterpreter().HandleCommands(GetCommands(), exe_ctx, | |
| options, result); | |
| debugger.SetAsyncExecution(old_async); | |
| } | |
| void Target::HookCommandLine::HandleModuleUnloaded(StreamSP output_sp) { | |
| // Command-based hooks run the same commands on unload as on load. | |
| HandleModuleLoaded(output_sp); | |
| } | |
| Target::StopHook::StopHookResult | |
| Target::HookCommandLine::HandleStop(ExecutionContext &exc_ctx, | |
| StreamSP output_sp) { | |
| assert(exc_ctx.GetTargetPtr() && "Can't call HandleStop on a context " | |
| "with no target"); | |
| if (!m_commands.GetSize()) | |
| return StopHook::StopHookResult::KeepStopped; | |
| CommandReturnObject result(false); | |
| result.SetImmediateOutputStream(output_sp); | |
| result.SetImmediateErrorStream(output_sp); | |
| result.SetInteractive(false); | |
| Debugger &debugger = exc_ctx.GetTargetPtr()->GetDebugger(); | |
| CommandInterpreterRunOptions options; | |
| options.SetStopOnContinue(true); | |
| options.SetStopOnError(true); | |
| options.SetEchoCommands(false); | |
| options.SetPrintResults(true); | |
| options.SetPrintErrors(true); | |
| options.SetAddToHistory(false); | |
| bool old_async = debugger.GetAsyncExecution(); | |
| debugger.SetAsyncExecution(true); | |
| debugger.GetCommandInterpreter().HandleCommands(GetCommands(), exc_ctx, | |
| options, result); | |
| debugger.SetAsyncExecution(old_async); | |
| lldb::ReturnStatus status = result.GetStatus(); | |
| if (status == eReturnStatusSuccessContinuingNoResult || | |
| status == eReturnStatusSuccessContinuingResult) | |
| return StopHook::StopHookResult::AlreadyContinued; | |
| return StopHook::StopHookResult::KeepStopped; | |
| } | |
| // HookScripted | |
| Status Target::HookScripted::SetScriptCallback( | |
| const ScriptedMetadata &scripted_metadata) { | |
| ScriptInterpreter *script_interp = | |
| GetTarget()->GetDebugger().GetScriptInterpreter(); | |
| if (!script_interp) | |
| return Status::FromErrorString("No script interpreter installed."); | |
| m_interface_sp = script_interp->CreateScriptedHookInterface(); | |
| if (!m_interface_sp) | |
| return Status::FromErrorStringWithFormat( | |
| "ScriptedHook::%s () - ERROR: %s", __FUNCTION__, | |
| "Script interpreter couldn't create Scripted Hook Interface"); | |
| auto obj_or_err = | |
| m_interface_sp->CreatePluginObject(scripted_metadata, GetTarget()); | |
| if (!obj_or_err) | |
| return Status::FromError(obj_or_err.takeError()); | |
| StructuredData::ObjectSP object_sp = *obj_or_err; | |
| if (!object_sp || !object_sp->IsValid()) | |
| return Status::FromErrorStringWithFormat( | |
| "ScriptedHook::%s () - ERROR: %s", __FUNCTION__, | |
| "Failed to create valid script object"); | |
| // Determine which triggers the class supports by checking which callback | |
| // methods it implements. | |
| auto methods = m_interface_sp->GetSupportedMethods(); | |
| if (!methods.any()) | |
| return Status::FromErrorString( | |
| "hook class implements none of the expected methods " | |
| "(handle_module_loaded, handle_module_unloaded, handle_stop)"); | |
| if (methods.handle_module_loaded) | |
| m_trigger_mask |= kModulesLoaded; | |
| if (methods.handle_module_unloaded) | |
| m_trigger_mask |= kModulesUnloaded; | |
| if (methods.handle_stop) | |
| m_trigger_mask |= kProcessStop; | |
| return {}; | |
| } | |
| void Target::HookScripted::HandleModuleLoaded(StreamSP output_sp) { | |
| if (!m_interface_sp) | |
| return; | |
| TargetSP target_sp = GetTarget(); | |
| if (!target_sp) | |
| return; | |
| StreamSP stream = std::make_shared<StreamString>(); | |
| llvm::Error error = m_interface_sp->HandleModuleLoaded(stream); | |
| output_sp->PutCString(static_cast<StreamString *>(stream.get())->GetData()); | |
| ReportScriptedHookError(std::move(error), | |
| llvm::formatv("hook {0}", GetID()).str(), | |
| target_sp->GetDebugger()); | |
| } | |
| void Target::HookScripted::HandleModuleUnloaded(StreamSP output_sp) { | |
| if (!m_interface_sp) | |
| return; | |
| TargetSP target_sp = GetTarget(); | |
| if (!target_sp) | |
| return; | |
| StreamSP stream = std::make_shared<StreamString>(); | |
| llvm::Error error = m_interface_sp->HandleModuleUnloaded(stream); | |
| output_sp->PutCString(static_cast<StreamString *>(stream.get())->GetData()); | |
| ReportScriptedHookError(std::move(error), | |
| llvm::formatv("hook {0}", GetID()).str(), | |
| target_sp->GetDebugger()); | |
| } | |
| Target::StopHook::StopHookResult | |
| Target::HookScripted::HandleStop(ExecutionContext &exc_ctx, | |
| StreamSP output_sp) { | |
| assert(exc_ctx.GetTargetPtr() && "Can't call HandleStop on a context " | |
| "with no target"); | |
| if (!m_interface_sp) | |
| return StopHook::StopHookResult::KeepStopped; | |
| lldb::StreamSP stream = std::make_shared<lldb_private::StreamString>(); | |
| auto should_stop_or_err = m_interface_sp->HandleStop(exc_ctx, stream); | |
| output_sp->PutCString(static_cast<StreamString *>(stream.get())->GetData()); | |
| if (!should_stop_or_err) { | |
| ReportScriptedHookError(should_stop_or_err.takeError(), | |
| llvm::formatv("hook {0}", GetID()).str(), | |
| exc_ctx.GetTargetPtr()->GetDebugger()); | |
| return StopHook::StopHookResult::KeepStopped; | |
| } | |
| return *should_stop_or_err ? StopHook::StopHookResult::KeepStopped | |
| : StopHook::StopHookResult::RequestContinue; | |
| } | |
| llvm::StringRef Target::HookScripted::GetScriptClassName() const { | |
| if (m_interface_sp && m_interface_sp->GetScriptedMetadata()) | |
| return m_interface_sp->GetScriptedMetadata()->GetClassName(); | |
| return "<unknown>"; | |
| } | |
| void Target::HookScripted::GetDescription(Stream &s, | |
| lldb::DescriptionLevel level) const { | |
| Hook::GetDescription(s, level); | |
| llvm::StringRef class_name = GetScriptClassName(); | |
| if (level == eDescriptionLevelBrief) { | |
| s.PutCString(class_name); | |
| return; | |
| } | |
| // Class and args come after the header (ID, State, Triggers) but before | |
| // filters. | |
| s.IndentMore(); | |
| s.Indent("Class: "); | |
| s.Format("{0}\n", class_name); | |
| StructuredData::DictionarySP as_dict = | |
| (m_interface_sp && m_interface_sp->GetScriptedMetadata()) | |
| ? m_interface_sp->GetScriptedMetadata()->GetArgsSP() | |
| : nullptr; | |
| if (as_dict && as_dict->IsValid() && as_dict->GetSize() > 0) { | |
| s.Indent("Args:\n"); | |
| s.IndentMore(); | |
| auto print_one_element = [&s](llvm::StringRef key, | |
| StructuredData::Object *object) { | |
| s.Indent(); | |
| s.Format("{0} : {1}\n", key, object->GetStringValue()); | |
| return true; | |
| }; | |
| as_dict->ForEach(print_one_element); | |
| s.IndentLess(); | |
| } | |
| s.IndentLess(); | |
| GetFilterDescription(s, level); | |
| } | |
| // Hook management methods | |
| Target::HookSP Target::CreateHook(Hook::HookKind kind) { | |
| lldb::user_id_t new_uid = ++m_hook_next_id; | |
| HookSP hook_sp; | |
| switch (kind) { | |
| case Hook::HookKind::CommandBased: | |
| hook_sp.reset(new HookCommandLine(shared_from_this(), new_uid)); | |
| break; | |
| case Hook::HookKind::ScriptBased: | |
| hook_sp.reset(new HookScripted(shared_from_this(), new_uid)); | |
| break; | |
| } | |
| m_hooks[new_uid] = hook_sp; | |
| return hook_sp; | |
| } | |
| void Target::UndoCreateHook(lldb::user_id_t uid) { | |
| if (!RemoveHookByID(uid)) | |
| return; | |
| if (uid > 0) | |
| --m_hook_next_id; | |
| } | |
| bool Target::RemoveHookByID(lldb::user_id_t uid) { | |
| size_t num_removed = m_hooks.erase(uid); | |
| return (num_removed != 0); | |
| } | |
| void Target::RemoveAllHooks() { m_hooks.clear(); } | |
| Target::HookSP Target::GetHookByID(lldb::user_id_t uid) { | |
| auto iter = m_hooks.find(uid); | |
| if (iter == m_hooks.end()) | |
| return {}; | |
| return iter->second; | |
| } | |
| Target::HookSP Target::GetHookAtIndex(size_t index) { | |
| if (index >= m_hooks.size()) | |
| return {}; | |
| auto iter = m_hooks.begin(); | |
| std::advance(iter, index); | |
| return iter->second; | |
| } | |
| bool Target::SetHookEnabledStateByID(lldb::user_id_t uid, bool enabled) { | |
| auto iter = m_hooks.find(uid); | |
| if (iter == m_hooks.end()) | |
| return false; | |
| iter->second->SetIsEnabled(enabled); | |
| return true; | |
| } | |
| void Target::SetAllHooksEnabledState(bool enabled) { | |
| for (auto &[_, hook] : m_hooks) | |
| hook->SetIsEnabled(enabled); | |
| } | |
| void Target::RunModuleHooks(bool is_load) { | |
| if (m_hooks.empty()) | |
| return; | |
| uint32_t trigger = is_load ? Hook::kModulesLoaded : Hook::kModulesUnloaded; | |
| // Copy active hooks into a local vector before iterating, in case a | |
| // callback modifies m_hooks (same pattern as RunStopHooks). | |
| std::vector<HookSP> active_hooks; | |
| for (auto &[_, hook_sp] : m_hooks) { | |
| if (hook_sp->IsEnabled() && hook_sp->FiresOn(trigger)) | |
| active_hooks.push_back(hook_sp); | |
| } | |
| if (active_hooks.empty()) | |
| return; | |
| StreamSP output_sp = m_debugger.GetAsyncOutputStream(); | |
| for (auto &hook_sp : active_hooks) { | |
| if (is_load) | |
| hook_sp->HandleModuleLoaded(output_sp); | |
| else | |
| hook_sp->HandleModuleUnloaded(output_sp); | |
| } | |
| output_sp->Flush(); | |
| } | |
| static constexpr OptionEnumValueElement g_dynamic_value_types[] = { | |
| { | |
| eNoDynamicValues, | |
| "no-dynamic-values", | |
| "Don't calculate the dynamic type of values", | |
| }, | |
| { | |
| eDynamicCanRunTarget, | |
| "run-target", | |
| "Calculate the dynamic type of values " | |
| "even if you have to run the target.", | |
| }, | |
| { | |
| eDynamicDontRunTarget, | |
| "no-run-target", | |
| "Calculate the dynamic type of values, but don't run the target.", | |
| }, | |
| }; | |
| OptionEnumValues lldb_private::GetDynamicValueTypes() { | |
| return OptionEnumValues(g_dynamic_value_types); | |
| } | |
| static constexpr OptionEnumValueElement g_inline_breakpoint_enums[] = { | |
| { | |
| eInlineBreakpointsNever, | |
| "never", | |
| "Never look for inline breakpoint locations (fastest). This setting " | |
| "should only be used if you know that no inlining occurs in your" | |
| "programs.", | |
| }, | |
| { | |
| eInlineBreakpointsHeaders, | |
| "headers", | |
| "Only check for inline breakpoint locations when setting breakpoints " | |
| "in header files, but not when setting breakpoint in implementation " | |
| "source files (default).", | |
| }, | |
| { | |
| eInlineBreakpointsAlways, | |
| "always", | |
| "Always look for inline breakpoint locations when setting file and " | |
| "line breakpoints (slower but most accurate).", | |
| }, | |
| }; | |
| enum x86DisassemblyFlavor { | |
| eX86DisFlavorDefault, | |
| eX86DisFlavorIntel, | |
| eX86DisFlavorATT | |
| }; | |
| static constexpr OptionEnumValueElement g_x86_dis_flavor_value_types[] = { | |
| { | |
| eX86DisFlavorDefault, | |
| "default", | |
| "Disassembler default (currently att).", | |
| }, | |
| { | |
| eX86DisFlavorIntel, | |
| "intel", | |
| "Intel disassembler flavor.", | |
| }, | |
| { | |
| eX86DisFlavorATT, | |
| "att", | |
| "AT&T disassembler flavor.", | |
| }, | |
| }; | |
| static constexpr OptionEnumValueElement g_jit_engine_value_types[] = { | |
| { | |
| eJITEngineMCJIT, | |
| "mcjit", | |
| "Use LLVM's MCJIT execution engine.", | |
| }, | |
| { | |
| eJITEngineORC, | |
| "orc", | |
| "Use LLVM's ORC execution engine.", | |
| }, | |
| }; | |
| static constexpr OptionEnumValueElement g_import_std_module_value_types[] = { | |
| { | |
| eImportStdModuleFalse, | |
| "false", | |
| "Never import the 'std' C++ module in the expression parser.", | |
| }, | |
| { | |
| eImportStdModuleFallback, | |
| "fallback", | |
| "Retry evaluating expressions with an imported 'std' C++ module if they" | |
| " failed to parse without the module. This allows evaluating more " | |
| "complex expressions involving C++ standard library types." | |
| }, | |
| { | |
| eImportStdModuleTrue, | |
| "true", | |
| "Always import the 'std' C++ module. This allows evaluating more " | |
| "complex expressions involving C++ standard library types. This feature" | |
| " is experimental." | |
| }, | |
| }; | |
| static constexpr OptionEnumValueElement | |
| g_dynamic_class_info_helper_value_types[] = { | |
| { | |
| eDynamicClassInfoHelperAuto, | |
| "auto", | |
| "Automatically determine the most appropriate method for the " | |
| "target OS.", | |
| }, | |
| {eDynamicClassInfoHelperRealizedClassesStruct, "RealizedClassesStruct", | |
| "Prefer using the realized classes struct."}, | |
| {eDynamicClassInfoHelperCopyRealizedClassList, "CopyRealizedClassList", | |
| "Prefer using the CopyRealizedClassList API."}, | |
| {eDynamicClassInfoHelperGetRealizedClassList, "GetRealizedClassList", | |
| "Prefer using the GetRealizedClassList API."}, | |
| }; | |
| static constexpr OptionEnumValueElement g_hex_immediate_style_values[] = { | |
| { | |
| Disassembler::eHexStyleC, | |
| "c", | |
| "C-style (0xffff).", | |
| }, | |
| { | |
| Disassembler::eHexStyleAsm, | |
| "asm", | |
| "Asm-style (0ffffh).", | |
| }, | |
| }; | |
| static constexpr OptionEnumValueElement g_load_script_from_sym_file_values[] = { | |
| { | |
| eLoadScriptFromSymFileTrue, | |
| "true", | |
| "Load debug scripts inside symbol files", | |
| }, | |
| { | |
| eLoadScriptFromSymFileFalse, | |
| "false", | |
| "Do not load debug scripts inside symbol files.", | |
| }, | |
| { | |
| eLoadScriptFromSymFileWarn, | |
| "warn", | |
| "Warn about debug scripts inside symbol files but do not load them.", | |
| }, | |
| { | |
| eLoadScriptFromSymFileTrusted, | |
| "trusted", | |
| "Load debug scripts inside trusted symbol files, and warn about " | |
| "scripts from untrusted symbol files.", | |
| }, | |
| }; | |
| static constexpr OptionEnumValueElement g_load_cwd_lldbinit_values[] = { | |
| { | |
| eLoadCWDlldbinitTrue, | |
| "true", | |
| "Load .lldbinit files from current directory", | |
| }, | |
| { | |
| eLoadCWDlldbinitFalse, | |
| "false", | |
| "Do not load .lldbinit files from current directory", | |
| }, | |
| { | |
| eLoadCWDlldbinitWarn, | |
| "warn", | |
| "Warn about loading .lldbinit files from current directory", | |
| }, | |
| }; | |
| static constexpr OptionEnumValueElement g_memory_module_load_level_values[] = { | |
| { | |
| eMemoryModuleLoadLevelMinimal, | |
| "minimal", | |
| "Load minimal information when loading modules from memory. Currently " | |
| "this setting loads sections only.", | |
| }, | |
| { | |
| eMemoryModuleLoadLevelPartial, | |
| "partial", | |
| "Load partial information when loading modules from memory. Currently " | |
| "this setting loads sections and function bounds.", | |
| }, | |
| { | |
| eMemoryModuleLoadLevelComplete, | |
| "complete", | |
| "Load complete information when loading modules from memory. Currently " | |
| "this setting loads sections and all symbols.", | |
| }, | |
| }; | |
| #define LLDB_PROPERTIES_target | |
| #include "TargetProperties.inc" | |
| enum { | |
| #define LLDB_PROPERTIES_target | |
| #include "TargetPropertiesEnum.inc" | |
| ePropertyExperimental, | |
| }; | |
| class TargetOptionValueProperties | |
| : public Cloneable<TargetOptionValueProperties, OptionValueProperties> { | |
| public: | |
| TargetOptionValueProperties(llvm::StringRef name) : Cloneable(name) {} | |
| const Property * | |
| GetPropertyAtIndex(size_t idx, | |
| const ExecutionContext *exe_ctx = nullptr) const override { | |
| // When getting the value for a key from the target options, we will always | |
| // try and grab the setting from the current target if there is one. Else | |
| // we just use the one from this instance. | |
| if (exe_ctx) { | |
| Target *target = exe_ctx->GetTargetPtr(); | |
| if (target && !target->IsDummyTarget()) { | |
| TargetOptionValueProperties *target_properties = | |
| static_cast<TargetOptionValueProperties *>( | |
| target->GetValueProperties().get()); | |
| if (this != target_properties) | |
| return target_properties->ProtectedGetPropertyAtIndex(idx); | |
| } | |
| } | |
| return ProtectedGetPropertyAtIndex(idx); | |
| } | |
| }; | |
| // TargetProperties | |
| #define LLDB_PROPERTIES_target_experimental | |
| #include "TargetProperties.inc" | |
| enum { | |
| #define LLDB_PROPERTIES_target_experimental | |
| #include "TargetPropertiesEnum.inc" | |
| }; | |
| class TargetExperimentalOptionValueProperties | |
| : public Cloneable<TargetExperimentalOptionValueProperties, | |
| OptionValueProperties> { | |
| public: | |
| TargetExperimentalOptionValueProperties() | |
| : Cloneable(Properties::GetExperimentalSettingsName()) {} | |
| }; | |
| TargetExperimentalProperties::TargetExperimentalProperties() | |
| : Properties(OptionValuePropertiesSP( | |
| new TargetExperimentalOptionValueProperties())) { | |
| m_collection_sp->Initialize(g_target_experimental_properties_def); | |
| } | |
| // TargetProperties | |
| TargetProperties::TargetProperties(Target *target) | |
| : Properties(), m_launch_info(), m_target(target) { | |
| if (target) { | |
| m_collection_sp = | |
| OptionValueProperties::CreateLocalCopy(Target::GetGlobalProperties()); | |
| // Set callbacks to update launch_info whenever "settins set" updated any | |
| // of these properties | |
| m_collection_sp->SetValueChangedCallback( | |
| ePropertyArg0, [this] { Arg0ValueChangedCallback(); }); | |
| m_collection_sp->SetValueChangedCallback( | |
| ePropertyRunArgs, [this] { RunArgsValueChangedCallback(); }); | |
| m_collection_sp->SetValueChangedCallback( | |
| ePropertyEnvVars, [this] { EnvVarsValueChangedCallback(); }); | |
| m_collection_sp->SetValueChangedCallback( | |
| ePropertyUnsetEnvVars, [this] { EnvVarsValueChangedCallback(); }); | |
| m_collection_sp->SetValueChangedCallback( | |
| ePropertyInheritEnv, [this] { EnvVarsValueChangedCallback(); }); | |
| m_collection_sp->SetValueChangedCallback( | |
| ePropertyInputPath, [this] { InputPathValueChangedCallback(); }); | |
| m_collection_sp->SetValueChangedCallback( | |
| ePropertyOutputPath, [this] { OutputPathValueChangedCallback(); }); | |
| m_collection_sp->SetValueChangedCallback( | |
| ePropertyErrorPath, [this] { ErrorPathValueChangedCallback(); }); | |
| m_collection_sp->SetValueChangedCallback(ePropertyDetachOnError, [this] { | |
| DetachOnErrorValueChangedCallback(); | |
| }); | |
| m_collection_sp->SetValueChangedCallback( | |
| ePropertyDisableASLR, [this] { DisableASLRValueChangedCallback(); }); | |
| m_collection_sp->SetValueChangedCallback( | |
| ePropertyInheritTCC, [this] { InheritTCCValueChangedCallback(); }); | |
| m_collection_sp->SetValueChangedCallback( | |
| ePropertyDisableSTDIO, [this] { DisableSTDIOValueChangedCallback(); }); | |
| m_collection_sp->SetValueChangedCallback( | |
| ePropertySaveObjectsDir, [this] { CheckJITObjectsDir(); }); | |
| m_experimental_properties_up = | |
| std::make_unique<TargetExperimentalProperties>(); | |
| m_collection_sp->AppendProperty( | |
| Properties::GetExperimentalSettingsName(), | |
| "Experimental settings - setting these won't produce " | |
| "errors if the setting is not present.", | |
| true, m_experimental_properties_up->GetValueProperties()); | |
| } else { | |
| m_collection_sp = std::make_shared<TargetOptionValueProperties>("target"); | |
| m_collection_sp->Initialize(g_target_properties_def); | |
| m_experimental_properties_up = | |
| std::make_unique<TargetExperimentalProperties>(); | |
| m_collection_sp->AppendProperty( | |
| Properties::GetExperimentalSettingsName(), | |
| "Experimental settings - setting these won't produce " | |
| "errors if the setting is not present.", | |
| true, m_experimental_properties_up->GetValueProperties()); | |
| m_collection_sp->AppendProperty( | |
| "process", "Settings specific to processes.", true, | |
| Process::GetGlobalProperties().GetValueProperties()); | |
| m_collection_sp->SetValueChangedCallback( | |
| ePropertySaveObjectsDir, [this] { CheckJITObjectsDir(); }); | |
| } | |
| } | |
| TargetProperties::~TargetProperties() = default; | |
| void TargetProperties::UpdateLaunchInfoFromProperties() { | |
| Arg0ValueChangedCallback(); | |
| RunArgsValueChangedCallback(); | |
| EnvVarsValueChangedCallback(); | |
| InputPathValueChangedCallback(); | |
| OutputPathValueChangedCallback(); | |
| ErrorPathValueChangedCallback(); | |
| DetachOnErrorValueChangedCallback(); | |
| DisableASLRValueChangedCallback(); | |
| InheritTCCValueChangedCallback(); | |
| DisableSTDIOValueChangedCallback(); | |
| } | |
| std::optional<bool> TargetProperties::GetExperimentalPropertyValue( | |
| size_t prop_idx, ExecutionContext *exe_ctx) const { | |
| const Property *exp_property = | |
| m_collection_sp->GetPropertyAtIndex(ePropertyExperimental, exe_ctx); | |
| OptionValueProperties *exp_values = | |
| exp_property->GetValue()->GetAsProperties(); | |
| if (exp_values) | |
| return exp_values->GetPropertyAtIndexAs<bool>(prop_idx, exe_ctx); | |
| return std::nullopt; | |
| } | |
| bool TargetProperties::GetInjectLocalVariables( | |
| ExecutionContext *exe_ctx) const { | |
| return GetExperimentalPropertyValue(ePropertyInjectLocalVars, exe_ctx) | |
| .value_or(true); | |
| } | |
| bool TargetProperties::GetUseDIL(ExecutionContext *exe_ctx) const { | |
| const Property *exp_property = | |
| m_collection_sp->GetPropertyAtIndex(ePropertyExperimental, exe_ctx); | |
| OptionValueProperties *exp_values = | |
| exp_property->GetValue()->GetAsProperties(); | |
| if (exp_values) | |
| return exp_values->GetPropertyAtIndexAs<bool>(ePropertyUseDIL, exe_ctx) | |
| .value_or(false); | |
| else | |
| return true; | |
| } | |
| void TargetProperties::SetUseDIL(ExecutionContext *exe_ctx, bool b) { | |
| const Property *exp_property = | |
| m_collection_sp->GetPropertyAtIndex(ePropertyExperimental, exe_ctx); | |
| OptionValueProperties *exp_values = | |
| exp_property->GetValue()->GetAsProperties(); | |
| if (exp_values) | |
| exp_values->SetPropertyAtIndex(ePropertyUseDIL, true, exe_ctx); | |
| } | |
| bool TargetProperties::GetUseDILForCreatingValues() const { | |
| const Property *exp_property = | |
| m_collection_sp->GetPropertyAtIndex(ePropertyExperimental); | |
| OptionValueProperties *exp_values = | |
| exp_property->GetValue()->GetAsProperties(); | |
| if (exp_values) | |
| return exp_values | |
| ->GetPropertyAtIndexAs<bool>(ePropertyUseDILForCreatingValues) | |
| .value_or(false); | |
| else | |
| return true; | |
| } | |
| void TargetProperties::SetUseDILForCreatingValues(bool b) { | |
| const Property *exp_property = | |
| m_collection_sp->GetPropertyAtIndex(ePropertyExperimental); | |
| OptionValueProperties *exp_values = | |
| exp_property->GetValue()->GetAsProperties(); | |
| if (exp_values) | |
| exp_values->SetPropertyAtIndex(ePropertyUseDILForCreatingValues, b); | |
| } | |
| ArchSpec TargetProperties::GetDefaultArchitecture() const { | |
| const uint32_t idx = ePropertyDefaultArch; | |
| return GetPropertyAtIndexAs<ArchSpec>(idx, {}); | |
| } | |
| void TargetProperties::SetDefaultArchitecture(const ArchSpec &arch) { | |
| const uint32_t idx = ePropertyDefaultArch; | |
| SetPropertyAtIndex(idx, arch); | |
| } | |
| bool TargetProperties::GetMoveToNearestCode() const { | |
| const uint32_t idx = ePropertyMoveToNearestCode; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| lldb::DynamicValueType TargetProperties::GetPreferDynamicValue() const { | |
| const uint32_t idx = ePropertyPreferDynamic; | |
| return GetPropertyAtIndexAs<lldb::DynamicValueType>( | |
| idx, static_cast<lldb::DynamicValueType>( | |
| g_target_properties[idx].default_uint_value)); | |
| } | |
| bool TargetProperties::SetPreferDynamicValue(lldb::DynamicValueType d) { | |
| const uint32_t idx = ePropertyPreferDynamic; | |
| return SetPropertyAtIndex(idx, d); | |
| } | |
| bool TargetProperties::GetPreloadSymbols() const { | |
| if (INTERRUPT_REQUESTED(m_target->GetDebugger(), | |
| "Interrupted checking preload symbols")) { | |
| return false; | |
| } | |
| const uint32_t idx = ePropertyPreloadSymbols; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| void TargetProperties::SetPreloadSymbols(bool b) { | |
| const uint32_t idx = ePropertyPreloadSymbols; | |
| SetPropertyAtIndex(idx, b); | |
| } | |
| bool TargetProperties::GetDisableASLR() const { | |
| const uint32_t idx = ePropertyDisableASLR; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| void TargetProperties::SetDisableASLR(bool b) { | |
| const uint32_t idx = ePropertyDisableASLR; | |
| SetPropertyAtIndex(idx, b); | |
| } | |
| bool TargetProperties::GetInheritTCC() const { | |
| const uint32_t idx = ePropertyInheritTCC; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| void TargetProperties::SetInheritTCC(bool b) { | |
| const uint32_t idx = ePropertyInheritTCC; | |
| SetPropertyAtIndex(idx, b); | |
| } | |
| bool TargetProperties::GetDetachOnError() const { | |
| const uint32_t idx = ePropertyDetachOnError; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| void TargetProperties::SetDetachOnError(bool b) { | |
| const uint32_t idx = ePropertyDetachOnError; | |
| SetPropertyAtIndex(idx, b); | |
| } | |
| bool TargetProperties::GetDisableSTDIO() const { | |
| const uint32_t idx = ePropertyDisableSTDIO; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| void TargetProperties::SetDisableSTDIO(bool b) { | |
| const uint32_t idx = ePropertyDisableSTDIO; | |
| SetPropertyAtIndex(idx, b); | |
| } | |
| llvm::StringRef TargetProperties::GetLaunchWorkingDirectory() const { | |
| const uint32_t idx = ePropertyLaunchWorkingDir; | |
| return GetPropertyAtIndexAs<llvm::StringRef>( | |
| idx, g_target_properties[idx].default_cstr_value); | |
| } | |
| bool TargetProperties::GetParallelModuleLoad() const { | |
| const uint32_t idx = ePropertyParallelModuleLoad; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| const char *TargetProperties::GetDisassemblyFlavor() const { | |
| const uint32_t idx = ePropertyDisassemblyFlavor; | |
| const char *return_value; | |
| x86DisassemblyFlavor flavor_value = | |
| GetPropertyAtIndexAs<x86DisassemblyFlavor>( | |
| idx, static_cast<x86DisassemblyFlavor>( | |
| g_target_properties[idx].default_uint_value)); | |
| return_value = g_x86_dis_flavor_value_types[flavor_value].string_value; | |
| return return_value; | |
| } | |
| const char *TargetProperties::GetDisassemblyCPU() const { | |
| const uint32_t idx = ePropertyDisassemblyCPU; | |
| llvm::StringRef str = GetPropertyAtIndexAs<llvm::StringRef>( | |
| idx, g_target_properties[idx].default_cstr_value); | |
| return str.empty() ? nullptr : str.data(); | |
| } | |
| const char *TargetProperties::GetDisassemblyFeatures() const { | |
| const uint32_t idx = ePropertyDisassemblyFeatures; | |
| llvm::StringRef str = GetPropertyAtIndexAs<llvm::StringRef>( | |
| idx, g_target_properties[idx].default_cstr_value); | |
| return str.empty() ? nullptr : str.data(); | |
| } | |
| InlineStrategy TargetProperties::GetInlineStrategy() const { | |
| const uint32_t idx = ePropertyInlineStrategy; | |
| return GetPropertyAtIndexAs<InlineStrategy>( | |
| idx, | |
| static_cast<InlineStrategy>(g_target_properties[idx].default_uint_value)); | |
| } | |
| // Returning RealpathPrefixes, but the setting's type is FileSpecList. We do | |
| // this because we want the FileSpecList to normalize the file paths for us. | |
| RealpathPrefixes TargetProperties::GetSourceRealpathPrefixes() const { | |
| const uint32_t idx = ePropertySourceRealpathPrefixes; | |
| return RealpathPrefixes(GetPropertyAtIndexAs<FileSpecList>(idx, {})); | |
| } | |
| llvm::StringRef TargetProperties::GetArg0() const { | |
| const uint32_t idx = ePropertyArg0; | |
| return GetPropertyAtIndexAs<llvm::StringRef>( | |
| idx, g_target_properties[idx].default_cstr_value); | |
| } | |
| void TargetProperties::SetArg0(llvm::StringRef arg) { | |
| const uint32_t idx = ePropertyArg0; | |
| SetPropertyAtIndex(idx, arg); | |
| m_launch_info.SetArg0(arg); | |
| } | |
| bool TargetProperties::GetRunArguments(Args &args) const { | |
| const uint32_t idx = ePropertyRunArgs; | |
| return m_collection_sp->GetPropertyAtIndexAsArgs(idx, args); | |
| } | |
| void TargetProperties::SetRunArguments(const Args &args) { | |
| const uint32_t idx = ePropertyRunArgs; | |
| m_collection_sp->SetPropertyAtIndexFromArgs(idx, args); | |
| m_launch_info.GetArguments() = args; | |
| } | |
| Environment TargetProperties::ComputeEnvironment() const { | |
| Environment env; | |
| if (m_target && | |
| GetPropertyAtIndexAs<bool>( | |
| ePropertyInheritEnv, | |
| g_target_properties[ePropertyInheritEnv].default_uint_value != 0)) { | |
| if (auto platform_sp = m_target->GetPlatform()) { | |
| Environment platform_env = platform_sp->GetEnvironment(); | |
| for (const auto &KV : platform_env) | |
| env[KV.first()] = KV.second; | |
| } | |
| } | |
| Args property_unset_env; | |
| m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyUnsetEnvVars, | |
| property_unset_env); | |
| for (const auto &var : property_unset_env) | |
| env.erase(var.ref()); | |
| Args property_env; | |
| m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyEnvVars, property_env); | |
| for (const auto &KV : Environment(property_env)) | |
| env[KV.first()] = KV.second; | |
| return env; | |
| } | |
| Environment TargetProperties::GetEnvironment() const { | |
| return ComputeEnvironment(); | |
| } | |
| Environment TargetProperties::GetInheritedEnvironment() const { | |
| Environment environment; | |
| if (m_target == nullptr) | |
| return environment; | |
| if (!GetPropertyAtIndexAs<bool>( | |
| ePropertyInheritEnv, | |
| g_target_properties[ePropertyInheritEnv].default_uint_value != 0)) | |
| return environment; | |
| PlatformSP platform_sp = m_target->GetPlatform(); | |
| if (platform_sp == nullptr) | |
| return environment; | |
| Environment platform_environment = platform_sp->GetEnvironment(); | |
| for (const auto &KV : platform_environment) | |
| environment[KV.first()] = KV.second; | |
| Args property_unset_environment; | |
| m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyUnsetEnvVars, | |
| property_unset_environment); | |
| for (const auto &var : property_unset_environment) | |
| environment.erase(var.ref()); | |
| return environment; | |
| } | |
| Environment TargetProperties::GetTargetEnvironment() const { | |
| Args property_environment; | |
| m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyEnvVars, | |
| property_environment); | |
| Environment environment; | |
| for (const auto &KV : Environment(property_environment)) | |
| environment[KV.first()] = KV.second; | |
| return environment; | |
| } | |
| void TargetProperties::SetEnvironment(Environment env) { | |
| // TODO: Get rid of the Args intermediate step | |
| const uint32_t idx = ePropertyEnvVars; | |
| m_collection_sp->SetPropertyAtIndexFromArgs(idx, Args(env)); | |
| } | |
| bool TargetProperties::GetSkipPrologue() const { | |
| const uint32_t idx = ePropertySkipPrologue; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| PathMappingList &TargetProperties::GetSourcePathMap() const { | |
| const uint32_t idx = ePropertySourceMap; | |
| OptionValuePathMappings *option_value = | |
| m_collection_sp->GetPropertyAtIndexAsOptionValuePathMappings(idx); | |
| assert(option_value); | |
| return option_value->GetCurrentValue(); | |
| } | |
| PathMappingList &TargetProperties::GetObjectPathMap() const { | |
| const uint32_t idx = ePropertyObjectMap; | |
| OptionValuePathMappings *option_value = | |
| m_collection_sp->GetPropertyAtIndexAsOptionValuePathMappings(idx); | |
| assert(option_value); | |
| return option_value->GetCurrentValue(); | |
| } | |
| bool TargetProperties::GetAutoSourceMapRelative() const { | |
| const uint32_t idx = ePropertyAutoSourceMapRelative; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| void TargetProperties::AppendExecutableSearchPaths(const FileSpec &dir) { | |
| const uint32_t idx = ePropertyExecutableSearchPaths; | |
| OptionValueFileSpecList *option_value = | |
| m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(idx); | |
| assert(option_value); | |
| option_value->AppendCurrentValue(dir); | |
| } | |
| FileSpecList TargetProperties::GetExecutableSearchPaths() { | |
| const uint32_t idx = ePropertyExecutableSearchPaths; | |
| return GetPropertyAtIndexAs<FileSpecList>(idx, {}); | |
| } | |
| FileSpecList TargetProperties::GetDebugFileSearchPaths() { | |
| const uint32_t idx = ePropertyDebugFileSearchPaths; | |
| return GetPropertyAtIndexAs<FileSpecList>(idx, {}); | |
| } | |
| FileSpecList TargetProperties::GetClangModuleSearchPaths() { | |
| const uint32_t idx = ePropertyClangModuleSearchPaths; | |
| return GetPropertyAtIndexAs<FileSpecList>(idx, {}); | |
| } | |
| bool TargetProperties::GetEnableAutoImportClangModules() const { | |
| const uint32_t idx = ePropertyAutoImportClangModules; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| ImportStdModule TargetProperties::GetImportStdModule() const { | |
| const uint32_t idx = ePropertyImportStdModule; | |
| return GetPropertyAtIndexAs<ImportStdModule>( | |
| idx, static_cast<ImportStdModule>( | |
| g_target_properties[idx].default_uint_value)); | |
| } | |
| DynamicClassInfoHelper TargetProperties::GetDynamicClassInfoHelper() const { | |
| const uint32_t idx = ePropertyDynamicClassInfoHelper; | |
| return GetPropertyAtIndexAs<DynamicClassInfoHelper>( | |
| idx, static_cast<DynamicClassInfoHelper>( | |
| g_target_properties[idx].default_uint_value)); | |
| } | |
| bool TargetProperties::GetEnableAutoApplyFixIts() const { | |
| const uint32_t idx = ePropertyAutoApplyFixIts; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| uint64_t TargetProperties::GetNumberOfRetriesWithFixits() const { | |
| const uint32_t idx = ePropertyRetriesWithFixIts; | |
| return GetPropertyAtIndexAs<uint64_t>( | |
| idx, g_target_properties[idx].default_uint_value); | |
| } | |
| bool TargetProperties::GetEnableNotifyAboutFixIts() const { | |
| const uint32_t idx = ePropertyNotifyAboutFixIts; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| FileSpec TargetProperties::GetSaveJITObjectsDir() const { | |
| const uint32_t idx = ePropertySaveObjectsDir; | |
| return GetPropertyAtIndexAs<FileSpec>(idx, {}); | |
| } | |
| JITEngine TargetProperties::GetJITEngine() const { | |
| const uint32_t idx = ePropertyJITEngine; | |
| return GetPropertyAtIndexAs<JITEngine>( | |
| idx, static_cast<JITEngine>(g_target_properties[idx].default_uint_value)); | |
| } | |
| void TargetProperties::CheckJITObjectsDir() { | |
| FileSpec new_dir = GetSaveJITObjectsDir(); | |
| if (!new_dir) | |
| return; | |
| const FileSystem &instance = FileSystem::Instance(); | |
| bool exists = instance.Exists(new_dir); | |
| bool is_directory = instance.IsDirectory(new_dir); | |
| std::string path = new_dir.GetPath(true); | |
| bool writable = llvm::sys::fs::can_write(path); | |
| if (exists && is_directory && writable) | |
| return; | |
| m_collection_sp->GetPropertyAtIndex(ePropertySaveObjectsDir) | |
| ->GetValue() | |
| ->Clear(); | |
| std::string buffer; | |
| llvm::raw_string_ostream os(buffer); | |
| os << "JIT object dir '" << path << "' "; | |
| if (!exists) | |
| os << "does not exist"; | |
| else if (!is_directory) | |
| os << "is not a directory"; | |
| else if (!writable) | |
| os << "is not writable"; | |
| std::optional<lldb::user_id_t> debugger_id; | |
| if (m_target) | |
| debugger_id = m_target->GetDebugger().GetID(); | |
| Debugger::ReportError(buffer, debugger_id); | |
| } | |
| bool TargetProperties::GetEnableSyntheticValue() const { | |
| const uint32_t idx = ePropertyEnableSynthetic; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| bool TargetProperties::ShowHexVariableValuesWithLeadingZeroes() const { | |
| const uint32_t idx = ePropertyShowHexVariableValuesWithLeadingZeroes; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| uint32_t TargetProperties::GetMaxZeroPaddingInFloatFormat() const { | |
| const uint32_t idx = ePropertyMaxZeroPaddingInFloatFormat; | |
| return GetPropertyAtIndexAs<uint64_t>( | |
| idx, g_target_properties[idx].default_uint_value); | |
| } | |
| uint32_t TargetProperties::GetMaximumNumberOfChildrenToDisplay() const { | |
| const uint32_t idx = ePropertyMaxChildrenCount; | |
| return GetPropertyAtIndexAs<uint64_t>( | |
| idx, g_target_properties[idx].default_uint_value); | |
| } | |
| std::pair<uint32_t, bool> | |
| TargetProperties::GetMaximumDepthOfChildrenToDisplay() const { | |
| const uint32_t idx = ePropertyMaxChildrenDepth; | |
| auto *option_value = | |
| m_collection_sp->GetPropertyAtIndexAsOptionValueUInt64(idx); | |
| bool is_default = !option_value->OptionWasSet(); | |
| return {option_value->GetCurrentValue(), is_default}; | |
| } | |
| uint32_t TargetProperties::GetMaximumSizeOfStringSummary() const { | |
| const uint32_t idx = ePropertyMaxSummaryLength; | |
| return GetPropertyAtIndexAs<uint64_t>( | |
| idx, g_target_properties[idx].default_uint_value); | |
| } | |
| uint32_t TargetProperties::GetMaximumMemReadSize() const { | |
| const uint32_t idx = ePropertyMaxMemReadSize; | |
| return GetPropertyAtIndexAs<uint64_t>( | |
| idx, g_target_properties[idx].default_uint_value); | |
| } | |
| FileSpec TargetProperties::GetStandardInputPath() const { | |
| const uint32_t idx = ePropertyInputPath; | |
| return GetPropertyAtIndexAs<FileSpec>(idx, {}); | |
| } | |
| void TargetProperties::SetStandardInputPath(llvm::StringRef path) { | |
| const uint32_t idx = ePropertyInputPath; | |
| SetPropertyAtIndex(idx, path); | |
| } | |
| FileSpec TargetProperties::GetStandardOutputPath() const { | |
| const uint32_t idx = ePropertyOutputPath; | |
| return GetPropertyAtIndexAs<FileSpec>(idx, {}); | |
| } | |
| void TargetProperties::SetStandardOutputPath(llvm::StringRef path) { | |
| const uint32_t idx = ePropertyOutputPath; | |
| SetPropertyAtIndex(idx, path); | |
| } | |
| FileSpec TargetProperties::GetStandardErrorPath() const { | |
| const uint32_t idx = ePropertyErrorPath; | |
| return GetPropertyAtIndexAs<FileSpec>(idx, {}); | |
| } | |
| void TargetProperties::SetStandardErrorPath(llvm::StringRef path) { | |
| const uint32_t idx = ePropertyErrorPath; | |
| SetPropertyAtIndex(idx, path); | |
| } | |
| SourceLanguage TargetProperties::GetLanguage() const { | |
| const uint32_t idx = ePropertyLanguage; | |
| return SourceLanguage{GetPropertyAtIndexAs<LanguageType>(idx, {})}; | |
| } | |
| llvm::StringRef TargetProperties::GetExpressionPrefixContents() { | |
| const uint32_t idx = ePropertyExprPrefix; | |
| OptionValueFileSpec *file = | |
| m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpec(idx); | |
| if (file) { | |
| DataBufferSP data_sp(file->GetFileContents()); | |
| if (data_sp) | |
| return llvm::StringRef( | |
| reinterpret_cast<const char *>(data_sp->GetBytes()), | |
| data_sp->GetByteSize()); | |
| } | |
| return ""; | |
| } | |
| uint64_t TargetProperties::GetExprErrorLimit() const { | |
| const uint32_t idx = ePropertyExprErrorLimit; | |
| return GetPropertyAtIndexAs<uint64_t>( | |
| idx, g_target_properties[idx].default_uint_value); | |
| } | |
| uint64_t TargetProperties::GetExprAllocAddress() const { | |
| const uint32_t idx = ePropertyExprAllocAddress; | |
| return GetPropertyAtIndexAs<uint64_t>( | |
| idx, g_target_properties[idx].default_uint_value); | |
| } | |
| uint64_t TargetProperties::GetExprAllocSize() const { | |
| const uint32_t idx = ePropertyExprAllocSize; | |
| return GetPropertyAtIndexAs<uint64_t>( | |
| idx, g_target_properties[idx].default_uint_value); | |
| } | |
| uint64_t TargetProperties::GetExprAllocAlign() const { | |
| const uint32_t idx = ePropertyExprAllocAlign; | |
| return GetPropertyAtIndexAs<uint64_t>( | |
| idx, g_target_properties[idx].default_uint_value); | |
| } | |
| bool TargetProperties::GetBreakpointsConsultPlatformAvoidList() { | |
| const uint32_t idx = ePropertyBreakpointUseAvoidList; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| BreakpointConditionMode TargetProperties::GetBreakpointsConditionMode() const { | |
| const uint32_t idx = ePropertyBreakpointsConditionMode; | |
| return GetPropertyAtIndexAs<BreakpointConditionMode>( | |
| idx, static_cast<BreakpointConditionMode>( | |
| g_target_properties[idx].default_uint_value)); | |
| } | |
| bool TargetProperties::GetUseHexImmediates() const { | |
| const uint32_t idx = ePropertyUseHexImmediates; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| bool TargetProperties::GetUseFastStepping() const { | |
| const uint32_t idx = ePropertyUseFastStepping; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| bool TargetProperties::GetDisplayExpressionsInCrashlogs() const { | |
| const uint32_t idx = ePropertyDisplayExpressionsInCrashlogs; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| LoadScriptFromSymFile TargetProperties::GetLoadScriptFromSymbolFile() const { | |
| const uint32_t idx = ePropertyLoadScriptFromSymbolFile; | |
| return GetPropertyAtIndexAs<LoadScriptFromSymFile>( | |
| idx, static_cast<LoadScriptFromSymFile>( | |
| g_target_properties[idx].default_uint_value)); | |
| } | |
| void TargetProperties::SetLoadScriptFromSymbolFile( | |
| LoadScriptFromSymFile load_style) { | |
| const uint32_t idx = ePropertyLoadScriptFromSymbolFile; | |
| SetPropertyAtIndex(idx, load_style); | |
| } | |
| LoadCWDlldbinitFile TargetProperties::GetLoadCWDlldbinitFile() const { | |
| const uint32_t idx = ePropertyLoadCWDlldbinitFile; | |
| return GetPropertyAtIndexAs<LoadCWDlldbinitFile>( | |
| idx, static_cast<LoadCWDlldbinitFile>( | |
| g_target_properties[idx].default_uint_value)); | |
| } | |
| Disassembler::HexImmediateStyle TargetProperties::GetHexImmediateStyle() const { | |
| const uint32_t idx = ePropertyHexImmediateStyle; | |
| return GetPropertyAtIndexAs<Disassembler::HexImmediateStyle>( | |
| idx, static_cast<Disassembler::HexImmediateStyle>( | |
| g_target_properties[idx].default_uint_value)); | |
| } | |
| MemoryModuleLoadLevel TargetProperties::GetMemoryModuleLoadLevel() const { | |
| const uint32_t idx = ePropertyMemoryModuleLoadLevel; | |
| return GetPropertyAtIndexAs<MemoryModuleLoadLevel>( | |
| idx, static_cast<MemoryModuleLoadLevel>( | |
| g_target_properties[idx].default_uint_value)); | |
| } | |
| bool TargetProperties::GetUserSpecifiedTrapHandlerNames(Args &args) const { | |
| const uint32_t idx = ePropertyTrapHandlerNames; | |
| return m_collection_sp->GetPropertyAtIndexAsArgs(idx, args); | |
| } | |
| void TargetProperties::SetUserSpecifiedTrapHandlerNames(const Args &args) { | |
| const uint32_t idx = ePropertyTrapHandlerNames; | |
| m_collection_sp->SetPropertyAtIndexFromArgs(idx, args); | |
| } | |
| bool TargetProperties::GetDisplayRuntimeSupportValues() const { | |
| const uint32_t idx = ePropertyDisplayRuntimeSupportValues; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| void TargetProperties::SetDisplayRuntimeSupportValues(bool b) { | |
| const uint32_t idx = ePropertyDisplayRuntimeSupportValues; | |
| SetPropertyAtIndex(idx, b); | |
| } | |
| bool TargetProperties::GetDisplayRecognizedArguments() const { | |
| const uint32_t idx = ePropertyDisplayRecognizedArguments; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| void TargetProperties::SetDisplayRecognizedArguments(bool b) { | |
| const uint32_t idx = ePropertyDisplayRecognizedArguments; | |
| SetPropertyAtIndex(idx, b); | |
| } | |
| const ProcessLaunchInfo &TargetProperties::GetProcessLaunchInfo() const { | |
| return m_launch_info; | |
| } | |
| void TargetProperties::SetProcessLaunchInfo( | |
| const ProcessLaunchInfo &launch_info) { | |
| m_launch_info = launch_info; | |
| SetArg0(launch_info.GetArg0()); | |
| SetRunArguments(launch_info.GetArguments()); | |
| SetEnvironment(launch_info.GetEnvironment()); | |
| const FileAction *input_file_action = | |
| launch_info.GetFileActionForFD(STDIN_FILENO); | |
| if (input_file_action) { | |
| SetStandardInputPath(input_file_action->GetFileSpec().GetPath()); | |
| } | |
| const FileAction *output_file_action = | |
| launch_info.GetFileActionForFD(STDOUT_FILENO); | |
| if (output_file_action) { | |
| SetStandardOutputPath(output_file_action->GetFileSpec().GetPath()); | |
| } | |
| const FileAction *error_file_action = | |
| launch_info.GetFileActionForFD(STDERR_FILENO); | |
| if (error_file_action) { | |
| SetStandardErrorPath(error_file_action->GetFileSpec().GetPath()); | |
| } | |
| SetDetachOnError(launch_info.GetFlags().Test(lldb::eLaunchFlagDetachOnError)); | |
| SetDisableASLR(launch_info.GetFlags().Test(lldb::eLaunchFlagDisableASLR)); | |
| SetInheritTCC( | |
| launch_info.GetFlags().Test(lldb::eLaunchFlagInheritTCCFromParent)); | |
| SetDisableSTDIO(launch_info.GetFlags().Test(lldb::eLaunchFlagDisableSTDIO)); | |
| } | |
| bool TargetProperties::GetRequireHardwareBreakpoints() const { | |
| const uint32_t idx = ePropertyRequireHardwareBreakpoints; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| void TargetProperties::SetRequireHardwareBreakpoints(bool b) { | |
| const uint32_t idx = ePropertyRequireHardwareBreakpoints; | |
| m_collection_sp->SetPropertyAtIndex(idx, b); | |
| } | |
| bool TargetProperties::GetAutoInstallMainExecutable() const { | |
| const uint32_t idx = ePropertyAutoInstallMainExecutable; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| void TargetProperties::Arg0ValueChangedCallback() { | |
| m_launch_info.SetArg0(GetArg0()); | |
| } | |
| void TargetProperties::RunArgsValueChangedCallback() { | |
| Args args; | |
| if (GetRunArguments(args)) | |
| m_launch_info.GetArguments() = args; | |
| } | |
| void TargetProperties::EnvVarsValueChangedCallback() { | |
| m_launch_info.GetEnvironment() = ComputeEnvironment(); | |
| } | |
| void TargetProperties::InputPathValueChangedCallback() { | |
| m_launch_info.AppendOpenFileAction(STDIN_FILENO, GetStandardInputPath(), true, | |
| false); | |
| } | |
| void TargetProperties::OutputPathValueChangedCallback() { | |
| m_launch_info.AppendOpenFileAction(STDOUT_FILENO, GetStandardOutputPath(), | |
| false, true); | |
| } | |
| void TargetProperties::ErrorPathValueChangedCallback() { | |
| m_launch_info.AppendOpenFileAction(STDERR_FILENO, GetStandardErrorPath(), | |
| false, true); | |
| } | |
| void TargetProperties::DetachOnErrorValueChangedCallback() { | |
| if (GetDetachOnError()) | |
| m_launch_info.GetFlags().Set(lldb::eLaunchFlagDetachOnError); | |
| else | |
| m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDetachOnError); | |
| } | |
| void TargetProperties::DisableASLRValueChangedCallback() { | |
| if (GetDisableASLR()) | |
| m_launch_info.GetFlags().Set(lldb::eLaunchFlagDisableASLR); | |
| else | |
| m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDisableASLR); | |
| } | |
| void TargetProperties::InheritTCCValueChangedCallback() { | |
| if (GetInheritTCC()) | |
| m_launch_info.GetFlags().Set(lldb::eLaunchFlagInheritTCCFromParent); | |
| else | |
| m_launch_info.GetFlags().Clear(lldb::eLaunchFlagInheritTCCFromParent); | |
| } | |
| void TargetProperties::DisableSTDIOValueChangedCallback() { | |
| if (GetDisableSTDIO()) | |
| m_launch_info.GetFlags().Set(lldb::eLaunchFlagDisableSTDIO); | |
| else | |
| m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDisableSTDIO); | |
| } | |
| bool TargetProperties::GetDebugUtilityExpression() const { | |
| const uint32_t idx = ePropertyDebugUtilityExpression; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| void TargetProperties::SetDebugUtilityExpression(bool debug) { | |
| const uint32_t idx = ePropertyDebugUtilityExpression; | |
| SetPropertyAtIndex(idx, debug); | |
| } | |
| bool TargetProperties::GetCheckValueObjectOwnership() const { | |
| const uint32_t idx = ePropertyCheckValueObjectOwnership; | |
| return GetPropertyAtIndexAs<bool>( | |
| idx, g_target_properties[idx].default_uint_value != 0); | |
| } | |
| void TargetProperties::SetCheckValueObjectOwnership(bool check) { | |
| const uint32_t idx = ePropertyCheckValueObjectOwnership; | |
| SetPropertyAtIndex(idx, check); | |
| } | |
| std::optional<LoadScriptFromSymFile> | |
| TargetProperties::GetAutoLoadScriptsForModule( | |
| llvm::StringRef module_name) const { | |
| auto *dict = m_collection_sp->GetPropertyAtIndexAsOptionValueDictionary( | |
| ePropertyAutoLoadScriptsForModules); | |
| if (!dict) | |
| return std::nullopt; | |
| OptionValueSP value_sp = dict->GetValueForKey(module_name); | |
| if (!value_sp) | |
| return std::nullopt; | |
| return value_sp->GetValueAs<LoadScriptFromSymFile>(); | |
| } | |
| void TargetProperties::SetAutoLoadScriptsForModule( | |
| llvm::StringRef module_name, LoadScriptFromSymFile load_style) { | |
| auto *dict = m_collection_sp->GetPropertyAtIndexAsOptionValueDictionary( | |
| ePropertyAutoLoadScriptsForModules); | |
| if (!dict) | |
| return; | |
| dict->SetValueForKey(module_name, | |
| std::make_shared<OptionValueEnumeration>( | |
| g_load_script_from_sym_file_values, load_style)); | |
| } | |
| // Target::TargetEventData | |
| Target::TargetEventData::TargetEventData(const lldb::TargetSP &target_sp) | |
| : EventData(), m_target_sp(target_sp), m_module_list() {} | |
| Target::TargetEventData::TargetEventData(const lldb::TargetSP &target_sp, | |
| const ModuleList &module_list) | |
| : EventData(), m_target_sp(target_sp), m_module_list(module_list) {} | |
| Target::TargetEventData::TargetEventData( | |
| const lldb::TargetSP &target_sp, const lldb::TargetSP &created_target_sp) | |
| : EventData(), m_target_sp(target_sp), | |
| m_created_target_sp(created_target_sp), m_module_list() {} | |
| Target::TargetEventData::~TargetEventData() = default; | |
| llvm::StringRef Target::TargetEventData::GetFlavorString() { | |
| return "Target::TargetEventData"; | |
| } | |
| void Target::TargetEventData::Dump(Stream *s) const { | |
| for (size_t i = 0; i < m_module_list.GetSize(); ++i) { | |
| if (i != 0) | |
| *s << ", "; | |
| m_module_list.GetModuleAtIndex(i)->GetDescription( | |
| s->AsRawOstream(), lldb::eDescriptionLevelBrief); | |
| } | |
| } | |
| const Target::TargetEventData * | |
| Target::TargetEventData::GetEventDataFromEvent(const Event *event_ptr) { | |
| if (event_ptr) { | |
| const EventData *event_data = event_ptr->GetData(); | |
| if (event_data && | |
| event_data->GetFlavor() == TargetEventData::GetFlavorString()) | |
| return static_cast<const TargetEventData *>(event_ptr->GetData()); | |
| } | |
| return nullptr; | |
| } | |
| TargetSP Target::TargetEventData::GetTargetFromEvent(const Event *event_ptr) { | |
| TargetSP target_sp; | |
| const TargetEventData *event_data = GetEventDataFromEvent(event_ptr); | |
| if (event_data) | |
| target_sp = event_data->m_target_sp; | |
| return target_sp; | |
| } | |
| TargetSP | |
| Target::TargetEventData::GetCreatedTargetFromEvent(const Event *event_ptr) { | |
| TargetSP created_target_sp; | |
| const TargetEventData *event_data = GetEventDataFromEvent(event_ptr); | |
| if (event_data) | |
| created_target_sp = event_data->m_created_target_sp; | |
| return created_target_sp; | |
| } | |
| ModuleList | |
| Target::TargetEventData::GetModuleListFromEvent(const Event *event_ptr) { | |
| ModuleList module_list; | |
| const TargetEventData *event_data = GetEventDataFromEvent(event_ptr); | |
| if (event_data) | |
| module_list = event_data->m_module_list; | |
| return module_list; | |
| } | |
| TargetAPIMutex Target::GetAPIMutex() { | |
| return TargetAPIMutex(shared_from_this()); | |
| } | |
| std::recursive_mutex *Target::GetAPIMutexForCurrentPolicy() { | |
| Policy policy = PolicyStack::Get().Current(); | |
| if (policy.capabilities.can_bypass_target_api_mutex) | |
| return nullptr; | |
| return policy.view == Policy::View::Private ? &m_private_mutex : &m_mutex; | |
| } | |
| /// Get metrics associated with this target in JSON format. | |
| llvm::json::Value | |
| Target::ReportStatistics(const lldb_private::StatisticsOptions &options) { | |
| return m_stats.ToJSON(*this, options); | |
| } | |
| void Target::ResetStatistics() { m_stats.Reset(*this); } | |
| bool Target::HasLoadedSections() { return !GetSectionLoadList().IsEmpty(); } | |
| lldb::addr_t Target::GetSectionLoadAddress(const lldb::SectionSP §ion_sp) { | |
| return GetSectionLoadList().GetSectionLoadAddress(section_sp); | |
| } | |
| void Target::ClearSectionLoadList() { GetSectionLoadList().Clear(); } | |
| void Target::DumpSectionLoadList(Stream &s) { | |
| GetSectionLoadList().Dump(s, this); | |
| } | |
| void Target::NotifyBreakpointChanged(Breakpoint &bp, | |
| lldb::BreakpointEventType eventKind) { | |
| if (EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) { | |
| std::shared_ptr<Breakpoint::BreakpointEventData> data_sp = | |
| std::make_shared<Breakpoint::BreakpointEventData>( | |
| eventKind, bp.shared_from_this()); | |
| BroadcastEvent(Target::eBroadcastBitBreakpointChanged, data_sp); | |
| } | |
| } | |
| void Target::NotifyBreakpointChanged( | |
| Breakpoint &bp, const lldb::EventDataSP &breakpoint_data_sp) { | |
| if (EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) | |
| BroadcastEvent(Target::eBroadcastBitBreakpointChanged, breakpoint_data_sp); | |
| } | |
| FileSpecList Target::GetSafeAutoLoadPaths() const { | |
| FileSpecList fspecs = Debugger::GetDefaultSafeAutoLoadPaths(); | |
| // Add platform-specific safe-paths. | |
| if (m_platform_sp) { | |
| if (auto platform_fspecs_or_err = | |
| m_platform_sp->GetSafeAutoLoadPaths(*this)) | |
| fspecs.Append(*platform_fspecs_or_err); | |
| else | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Modules | LLDBLog::Platform), | |
| platform_fspecs_or_err.takeError(), | |
| "Skipping safe auto-load: {0}"); | |
| } | |
| // Properties for testing get added last so they take priority. | |
| #ifndef NDEBUG | |
| for (const auto &fspec : | |
| TestingProperties::GetGlobalTestingProperties().GetSafeAutoLoadPaths()) | |
| fspecs.Append(fspec); | |
| #endif | |
| return fspecs; | |
| } | |
| // FIXME: the language plugin should expression options dynamically and | |
| // we should validate here (by asking the language plugin) that the options | |
| // being set/retrieved are actually valid options. | |
| llvm::Error | |
| EvaluateExpressionOptions::SetBooleanLanguageOption(llvm::StringRef option_name, | |
| bool value) { | |
| if (option_name.empty()) | |
| return llvm::createStringError("can't set an option with an empty name"); | |
| if (StructuredData::ObjectSP existing_sp = | |
| GetLanguageOptions().GetValueForKey(option_name); | |
| existing_sp && existing_sp->GetType() != eStructuredDataTypeBoolean) | |
| return llvm::createStringErrorV("trying to override existing option '{0}' " | |
| "of type '{1}' with a boolean value", | |
| option_name, existing_sp->GetType()); | |
| GetLanguageOptions().AddBooleanItem(option_name, value); | |
| return llvm::Error::success(); | |
| } | |
| llvm::Expected<bool> EvaluateExpressionOptions::GetBooleanLanguageOption( | |
| llvm::StringRef option_name) const { | |
| const StructuredData::Dictionary &opts = GetLanguageOptions(); | |
| if (!opts.HasKey(option_name)) | |
| return llvm::createStringErrorV("option '{0}' does not exist", option_name); | |
| bool result; | |
| if (!opts.GetValueForKeyAsBoolean(option_name, result)) | |
| return llvm::createStringErrorV("failed to get option '{0}' as boolean", | |
| option_name); | |
| return result; | |
| } | |
| const StructuredData::Dictionary & | |
| EvaluateExpressionOptions::GetLanguageOptions() const { | |
| assert(m_language_options_sp); | |
| return *m_language_options_sp; | |
| } | |
| StructuredData::Dictionary &EvaluateExpressionOptions::GetLanguageOptions() { | |
| assert(m_language_options_sp); | |
| return *m_language_options_sp; | |
| } | |
| // FIXME: this option is C++ plugin specific and should be registered by it, | |
| // instead of hard-coding it here. | |
| constexpr llvm::StringLiteral s_cpp_ignore_context_qualifiers_option = | |
| "c++-ignore-context-qualifiers"; | |
| EvaluateExpressionOptions::EvaluateExpressionOptions() | |
| : m_language_options_sp(std::make_shared<StructuredData::Dictionary>()) { | |
| SetCppIgnoreContextQualifiers(false); | |
| } | |
| void EvaluateExpressionOptions::SetCppIgnoreContextQualifiers(bool value) { | |
| llvm::cantFail( | |
| SetBooleanLanguageOption(s_cpp_ignore_context_qualifiers_option, value)); | |
| } | |
| bool EvaluateExpressionOptions::GetCppIgnoreContextQualifiers() const { | |
| return llvm::cantFail( | |
| GetBooleanLanguageOption(s_cpp_ignore_context_qualifiers_option)); | |
| } |