Index: lldb/include/lldb/Target/Target.h =================================================================== --- lldb/include/lldb/Target/Target.h +++ lldb/include/lldb/Target/Target.h @@ -1459,6 +1459,8 @@ PathMappingList m_image_search_paths; TypeSystemMap m_scratch_type_system_map; + lldb::ListenerSP m_hijacker_on_process_creation_sp; + typedef std::map REPLMap; REPLMap m_repl_map; Index: lldb/packages/Python/lldbsuite/test/lldbtest.py =================================================================== --- lldb/packages/Python/lldbsuite/test/lldbtest.py +++ lldb/packages/Python/lldbsuite/test/lldbtest.py @@ -231,6 +231,11 @@ def is_exe(fpath): """Returns true if fpath is an executable.""" + if fpath == None: + return False + if sys.platform == 'win32': + if not fpath.endswith(".exe"): + fpath += ".exe" return os.path.isfile(fpath) and os.access(fpath, os.X_OK) Index: lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py =================================================================== --- lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py +++ lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py @@ -11,8 +11,7 @@ def create_debug_adaptor(self, lldbVSCodeEnv=None): '''Create the Visual Studio Code debug adaptor''' - self.assertTrue(os.path.exists(self.lldbVSCodeExec), - 'lldb-vscode must exist') + self.assertTrue(is_exe(self.lldbVSCodeExec), 'lldb-vscode must exist') log_file_path = self.getBuildArtifact('vscode.txt') self.vscode = vscode.DebugAdaptor( executable=self.lldbVSCodeExec, init_commands=self.setUpCommands(), Index: lldb/source/Target/Process.cpp =================================================================== --- lldb/source/Target/Process.cpp +++ lldb/source/Target/Process.cpp @@ -2535,6 +2535,8 @@ // We are done with the launch hijack listener, and this stop should // go to the public state listener: RestoreProcessEvents(); + // Releasing the scope guard to avoid double restoring. + on_exit.release(); SetPublicState(state, false); if (PrivateStateThreadIsValid()) Index: lldb/source/Target/Target.cpp =================================================================== --- lldb/source/Target/Target.cpp +++ lldb/source/Target/Target.cpp @@ -206,6 +206,10 @@ DeleteCurrentProcess(); m_process_sp = Process::FindPlugin(shared_from_this(), plugin_name, listener_sp, crash_file, can_connect); + + if (m_process_sp && m_hijacker_on_process_creation_sp) + m_process_sp->HijackProcessEvents(m_hijacker_on_process_creation_sp); + return m_process_sp; } @@ -2962,6 +2966,8 @@ void Target::ClearAllLoadedSections() { m_section_load_history.Clear(); } Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) { + constexpr char hijacking_listener_name[] = "lldb.Target.Launch.hijack"; + m_stats.SetLaunchOrAttachTime(); Status error; Log *log = GetLog(LLDBLog::Target); @@ -3026,6 +3032,26 @@ 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 (synchronous_execution && !launch_info.GetHijackListener()) { + // The hijacker is passed this way and then set up in CreateProcess because + // it should be activated before Process::Launch is invoked. Platforms don't + // activate the existing hijacker passed via launch info on their own before + // they invoke Process::Launch. + m_hijacker_on_process_creation_sp = + Listener::MakeListener(hijacking_listener_name); + launch_info.SetHijackListener(m_hijacker_on_process_creation_sp); + } + + auto hijacker_on_process_creation_guard = + llvm::make_scope_exit([&m_hijacker_on_process_creation_sp = + m_hijacker_on_process_creation_sp]() { + m_hijacker_on_process_creation_sp.reset(); + }); + // 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 && @@ -3076,26 +3102,28 @@ ListenerSP hijack_listener_sp(launch_info.GetHijackListener()); if (!hijack_listener_sp) { - hijack_listener_sp = Listener::MakeListener("lldb.Target.Launch.hijack"); + hijack_listener_sp = Listener::MakeListener(hijacking_listener_name); launch_info.SetHijackListener(hijack_listener_sp); m_process_sp->HijackProcessEvents(hijack_listener_sp); } - switch (m_process_sp->WaitForProcessToStop(llvm::None, nullptr, false, - hijack_listener_sp, nullptr)) { + state = m_process_sp->WaitForProcessToStop(llvm::None, nullptr, false, + hijack_listener_sp, nullptr); + + at_exit.release(); + m_process_sp->RestoreProcessEvents(); + + switch (state) { case eStateStopped: { if (launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) break; - if (synchronous_execution) { + 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->RestoreProcessEvents(); m_process_sp->ResumeSynchronous(stream); - } else { - m_process_sp->RestoreProcessEvents(); + else error = m_process_sp->PrivateResume(); - } if (!error.Success()) { Status error2; error2.SetErrorStringWithFormat( Index: lldb/test/API/tools/lldb-vscode/stop-hooks/Makefile =================================================================== --- /dev/null +++ lldb/test/API/tools/lldb-vscode/stop-hooks/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules Index: lldb/test/API/tools/lldb-vscode/stop-hooks/TestVSCode_stop_hooks.py =================================================================== --- /dev/null +++ lldb/test/API/tools/lldb-vscode/stop-hooks/TestVSCode_stop_hooks.py @@ -0,0 +1,35 @@ +""" +Test stop hooks +""" + + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +import lldbvscode_testcase + + +class TestVSCode_stop_hooks(lldbvscode_testcase.VSCodeTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfRemote + def test_stop_hooks_before_run(self): + ''' + Test that there is no race condition between lldb-vscode and + stop hooks executor + ''' + program = self.getBuildArtifact("a.out") + preRunCommands = ['target stop-hook add -o help'] + self.build_and_launch(program, stopOnEntry=True, preRunCommands=preRunCommands) + + # The first stop is on entry. + self.continue_to_next_stop() + + breakpoint_ids = self.set_function_breakpoints(['main']) + # This request hangs if the race happens, because, in that case, the + # command interpreter is in synchronous mode while lldb-vscode expects + # it to be in asynchronous mode, so, the process doesn't send the stop + # event to "lldb.Debugger" listener (which is monitored by lldb-vscode). + self.continue_to_breakpoints(breakpoint_ids) + + self.continue_to_exit() Index: lldb/test/API/tools/lldb-vscode/stop-hooks/main.c =================================================================== --- /dev/null +++ lldb/test/API/tools/lldb-vscode/stop-hooks/main.c @@ -0,0 +1 @@ +int main() { return 0; } Index: vscode-race-fix-3.patch =================================================================== --- /dev/null +++ vscode-race-fix-3.patch @@ -0,0 +1,15221 @@ +diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h +index 42a641f6d52a..a37fb7408e1d 100644 +--- a/lldb/include/lldb/Target/Target.h ++++ b/lldb/include/lldb/Target/Target.h +@@ -1,1535 +1,1537 @@ + //===-- Target.h ------------------------------------------------*- C++ -*-===// + // + // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + // See https://llvm.org/LICENSE.txt for license information. + // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + // + //===----------------------------------------------------------------------===// + + #ifndef LLDB_TARGET_TARGET_H + #define LLDB_TARGET_TARGET_H + + #include + #include + #include + #include + #include + + #include "lldb/Breakpoint/BreakpointList.h" + #include "lldb/Breakpoint/BreakpointName.h" + #include "lldb/Breakpoint/WatchpointList.h" + #include "lldb/Core/Architecture.h" + #include "lldb/Core/Disassembler.h" + #include "lldb/Core/ModuleList.h" + #include "lldb/Core/StructuredDataImpl.h" + #include "lldb/Core/UserSettingsController.h" + #include "lldb/Expression/Expression.h" + #include "lldb/Host/ProcessLaunchInfo.h" + #include "lldb/Symbol/TypeSystem.h" + #include "lldb/Target/ExecutionContextScope.h" + #include "lldb/Target/PathMappingList.h" + #include "lldb/Target/SectionLoadHistory.h" + #include "lldb/Target/Statistics.h" + #include "lldb/Target/ThreadSpec.h" + #include "lldb/Utility/ArchSpec.h" + #include "lldb/Utility/Broadcaster.h" + #include "lldb/Utility/LLDBAssert.h" + #include "lldb/Utility/Timeout.h" + #include "lldb/lldb-public.h" + + namespace lldb_private { + + OptionEnumValues GetDynamicValueTypes(); + + enum InlineStrategy { + eInlineBreakpointsNever = 0, + eInlineBreakpointsHeaders, + eInlineBreakpointsAlways + }; + + enum LoadScriptFromSymFile { + eLoadScriptFromSymFileTrue, + eLoadScriptFromSymFileFalse, + eLoadScriptFromSymFileWarn + }; + + enum LoadCWDlldbinitFile { + eLoadCWDlldbinitTrue, + eLoadCWDlldbinitFalse, + eLoadCWDlldbinitWarn + }; + + enum LoadDependentFiles { + eLoadDependentsDefault, + eLoadDependentsYes, + eLoadDependentsNo, + }; + + enum ImportStdModule { + eImportStdModuleFalse, + eImportStdModuleFallback, + eImportStdModuleTrue, + }; + + class TargetExperimentalProperties : public Properties { + public: + TargetExperimentalProperties(); + }; + + class TargetProperties : public Properties { + public: + TargetProperties(Target *target); + + ~TargetProperties() override; + + ArchSpec GetDefaultArchitecture() const; + + void SetDefaultArchitecture(const ArchSpec &arch); + + bool GetMoveToNearestCode() const; + + lldb::DynamicValueType GetPreferDynamicValue() const; + + bool SetPreferDynamicValue(lldb::DynamicValueType d); + + bool GetPreloadSymbols() const; + + void SetPreloadSymbols(bool b); + + bool GetDisableASLR() const; + + void SetDisableASLR(bool b); + + bool GetInheritTCC() const; + + void SetInheritTCC(bool b); + + bool GetDetachOnError() const; + + void SetDetachOnError(bool b); + + bool GetDisableSTDIO() const; + + void SetDisableSTDIO(bool b); + + const char *GetDisassemblyFlavor() const; + + InlineStrategy GetInlineStrategy() const; + + llvm::StringRef GetArg0() const; + + void SetArg0(llvm::StringRef arg); + + bool GetRunArguments(Args &args) const; + + void SetRunArguments(const Args &args); + + // Get the whole environment including the platform inherited environment and + // the target specific environment, excluding the unset environment variables. + Environment GetEnvironment() const; + // Get the platform inherited environment, excluding the unset environment + // variables. + Environment GetInheritedEnvironment() const; + // Get the target specific environment only, without the platform inherited + // environment. + Environment GetTargetEnvironment() const; + // Set the target specific environment. + void SetEnvironment(Environment env); + + bool GetSkipPrologue() const; + + PathMappingList &GetSourcePathMap() const; + + FileSpecList GetExecutableSearchPaths(); + + void AppendExecutableSearchPaths(const FileSpec &); + + FileSpecList GetDebugFileSearchPaths(); + + FileSpecList GetClangModuleSearchPaths(); + + bool GetEnableAutoImportClangModules() const; + + ImportStdModule GetImportStdModule() const; + + bool GetEnableAutoApplyFixIts() const; + + uint64_t GetNumberOfRetriesWithFixits() const; + + bool GetEnableNotifyAboutFixIts() const; + + bool GetEnableSaveObjects() const; + + bool GetEnableSyntheticValue() const; + + uint32_t GetMaxZeroPaddingInFloatFormat() const; + + uint32_t GetMaximumNumberOfChildrenToDisplay() const; + + uint32_t GetMaximumSizeOfStringSummary() const; + + uint32_t GetMaximumMemReadSize() const; + + FileSpec GetStandardInputPath() const; + FileSpec GetStandardErrorPath() const; + FileSpec GetStandardOutputPath() const; + + void SetStandardInputPath(llvm::StringRef path); + void SetStandardOutputPath(llvm::StringRef path); + void SetStandardErrorPath(llvm::StringRef path); + + void SetStandardInputPath(const char *path) = delete; + void SetStandardOutputPath(const char *path) = delete; + void SetStandardErrorPath(const char *path) = delete; + + bool GetBreakpointsConsultPlatformAvoidList(); + + lldb::LanguageType GetLanguage() const; + + llvm::StringRef GetExpressionPrefixContents(); + + uint64_t GetExprErrorLimit() const; + + bool GetUseHexImmediates() const; + + bool GetUseFastStepping() const; + + bool GetDisplayExpressionsInCrashlogs() const; + + LoadScriptFromSymFile GetLoadScriptFromSymbolFile() const; + + LoadCWDlldbinitFile GetLoadCWDlldbinitFile() const; + + Disassembler::HexImmediateStyle GetHexImmediateStyle() const; + + MemoryModuleLoadLevel GetMemoryModuleLoadLevel() const; + + bool GetUserSpecifiedTrapHandlerNames(Args &args) const; + + void SetUserSpecifiedTrapHandlerNames(const Args &args); + + bool GetDisplayRuntimeSupportValues() const; + + void SetDisplayRuntimeSupportValues(bool b); + + bool GetDisplayRecognizedArguments() const; + + void SetDisplayRecognizedArguments(bool b); + + const ProcessLaunchInfo &GetProcessLaunchInfo() const; + + void SetProcessLaunchInfo(const ProcessLaunchInfo &launch_info); + + bool GetInjectLocalVariables(ExecutionContext *exe_ctx) const; + + void SetInjectLocalVariables(ExecutionContext *exe_ctx, bool b); + + void SetRequireHardwareBreakpoints(bool b); + + bool GetRequireHardwareBreakpoints() const; + + bool GetAutoInstallMainExecutable() const; + + void UpdateLaunchInfoFromProperties(); + + void SetDebugUtilityExpression(bool debug); + + bool GetDebugUtilityExpression() const; + + private: + // Callbacks for m_launch_info. + void Arg0ValueChangedCallback(); + void RunArgsValueChangedCallback(); + void EnvVarsValueChangedCallback(); + void InputPathValueChangedCallback(); + void OutputPathValueChangedCallback(); + void ErrorPathValueChangedCallback(); + void DetachOnErrorValueChangedCallback(); + void DisableASLRValueChangedCallback(); + void InheritTCCValueChangedCallback(); + void DisableSTDIOValueChangedCallback(); + + Environment ComputeEnvironment() const; + + // Member variables. + ProcessLaunchInfo m_launch_info; + std::unique_ptr m_experimental_properties_up; + Target *m_target; + }; + + class EvaluateExpressionOptions { + public: + // MSVC has a bug here that reports C4268: 'const' static/global data + // initialized with compiler generated default constructor fills the object + // with zeros. Confirmed that MSVC is *not* zero-initializing, it's just a + // bogus warning. + #if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable : 4268) + #endif + static constexpr std::chrono::milliseconds default_timeout{500}; + #if defined(_MSC_VER) + #pragma warning(pop) + #endif + + static constexpr ExecutionPolicy default_execution_policy = + eExecutionPolicyOnlyWhenNeeded; + + EvaluateExpressionOptions() = default; + + ExecutionPolicy GetExecutionPolicy() const { return m_execution_policy; } + + void SetExecutionPolicy(ExecutionPolicy policy = eExecutionPolicyAlways) { + m_execution_policy = policy; + } + + lldb::LanguageType GetLanguage() const { return m_language; } + + void SetLanguage(lldb::LanguageType language) { m_language = language; } + + bool DoesCoerceToId() const { return m_coerce_to_id; } + + const char *GetPrefix() const { + return (m_prefix.empty() ? nullptr : m_prefix.c_str()); + } + + void SetPrefix(const char *prefix) { + if (prefix && prefix[0]) + m_prefix = prefix; + else + m_prefix.clear(); + } + + void SetCoerceToId(bool coerce = true) { m_coerce_to_id = coerce; } + + bool DoesUnwindOnError() const { return m_unwind_on_error; } + + void SetUnwindOnError(bool unwind = false) { m_unwind_on_error = unwind; } + + bool DoesIgnoreBreakpoints() const { return m_ignore_breakpoints; } + + void SetIgnoreBreakpoints(bool ignore = false) { + m_ignore_breakpoints = ignore; + } + + bool DoesKeepInMemory() const { return m_keep_in_memory; } + + void SetKeepInMemory(bool keep = true) { m_keep_in_memory = keep; } + + lldb::DynamicValueType GetUseDynamic() const { return m_use_dynamic; } + + void + SetUseDynamic(lldb::DynamicValueType dynamic = lldb::eDynamicCanRunTarget) { + m_use_dynamic = dynamic; + } + + const Timeout &GetTimeout() const { return m_timeout; } + + void SetTimeout(const Timeout &timeout) { m_timeout = timeout; } + + const Timeout &GetOneThreadTimeout() const { + return m_one_thread_timeout; + } + + void SetOneThreadTimeout(const Timeout &timeout) { + m_one_thread_timeout = timeout; + } + + bool GetTryAllThreads() const { return m_try_others; } + + void SetTryAllThreads(bool try_others = true) { m_try_others = try_others; } + + bool GetStopOthers() const { return m_stop_others; } + + void SetStopOthers(bool stop_others = true) { m_stop_others = stop_others; } + + bool GetDebug() const { return m_debug; } + + void SetDebug(bool b) { + m_debug = b; + if (m_debug) + m_generate_debug_info = true; + } + + bool GetGenerateDebugInfo() const { return m_generate_debug_info; } + + void SetGenerateDebugInfo(bool b) { m_generate_debug_info = b; } + + bool GetColorizeErrors() const { return m_ansi_color_errors; } + + void SetColorizeErrors(bool b) { m_ansi_color_errors = b; } + + bool GetTrapExceptions() const { return m_trap_exceptions; } + + void SetTrapExceptions(bool b) { m_trap_exceptions = b; } + + bool GetREPLEnabled() const { return m_repl; } + + void SetREPLEnabled(bool b) { m_repl = b; } + + void SetCancelCallback(lldb::ExpressionCancelCallback callback, void *baton) { + m_cancel_callback_baton = baton; + m_cancel_callback = callback; + } + + bool InvokeCancelCallback(lldb::ExpressionEvaluationPhase phase) const { + return ((m_cancel_callback != nullptr) + ? m_cancel_callback(phase, m_cancel_callback_baton) + : false); + } + + // Allows the expression contents to be remapped to point to the specified + // file and line using #line directives. + void SetPoundLine(const char *path, uint32_t line) const { + if (path && path[0]) { + m_pound_line_file = path; + m_pound_line_line = line; + } else { + m_pound_line_file.clear(); + m_pound_line_line = 0; + } + } + + const char *GetPoundLineFilePath() const { + return (m_pound_line_file.empty() ? nullptr : m_pound_line_file.c_str()); + } + + uint32_t GetPoundLineLine() const { return m_pound_line_line; } + + void SetResultIsInternal(bool b) { m_result_is_internal = b; } + + bool GetResultIsInternal() const { return m_result_is_internal; } + + void SetAutoApplyFixIts(bool b) { m_auto_apply_fixits = b; } + + bool GetAutoApplyFixIts() const { return m_auto_apply_fixits; } + + void SetRetriesWithFixIts(uint64_t number_of_retries) { + m_retries_with_fixits = number_of_retries; + } + + uint64_t GetRetriesWithFixIts() const { return m_retries_with_fixits; } + + bool IsForUtilityExpr() const { return m_running_utility_expression; } + + void SetIsForUtilityExpr(bool b) { m_running_utility_expression = b; } + + private: + ExecutionPolicy m_execution_policy = default_execution_policy; + lldb::LanguageType m_language = lldb::eLanguageTypeUnknown; + std::string m_prefix; + bool m_coerce_to_id = false; + bool m_unwind_on_error = true; + bool m_ignore_breakpoints = false; + bool m_keep_in_memory = false; + bool m_try_others = true; + bool m_stop_others = true; + bool m_debug = false; + bool m_trap_exceptions = true; + bool m_repl = false; + bool m_generate_debug_info = false; + bool m_ansi_color_errors = false; + bool m_result_is_internal = false; + bool m_auto_apply_fixits = true; + uint64_t m_retries_with_fixits = 1; + /// True if the executed code should be treated as utility code that is only + /// used by LLDB internally. + bool m_running_utility_expression = false; + + lldb::DynamicValueType m_use_dynamic = lldb::eNoDynamicValues; + Timeout m_timeout = default_timeout; + Timeout m_one_thread_timeout = llvm::None; + lldb::ExpressionCancelCallback m_cancel_callback = nullptr; + void *m_cancel_callback_baton = nullptr; + // If m_pound_line_file is not empty and m_pound_line_line is non-zero, use + // #line %u "%s" before the expression content to remap where the source + // originates + mutable std::string m_pound_line_file; + mutable uint32_t m_pound_line_line; + }; + + // Target + class Target : public std::enable_shared_from_this, + public TargetProperties, + public Broadcaster, + public ExecutionContextScope, + public ModuleList::Notifier { + public: + friend class TargetList; + friend class Debugger; + + /// Broadcaster event bits definitions. + enum { + eBroadcastBitBreakpointChanged = (1 << 0), + eBroadcastBitModulesLoaded = (1 << 1), + eBroadcastBitModulesUnloaded = (1 << 2), + eBroadcastBitWatchpointChanged = (1 << 3), + eBroadcastBitSymbolsLoaded = (1 << 4) + }; + + // These two functions fill out the Broadcaster interface: + + static ConstString &GetStaticBroadcasterClass(); + + ConstString &GetBroadcasterClass() const override { + return GetStaticBroadcasterClass(); + } + + // This event data class is for use by the TargetList to broadcast new target + // notifications. + class TargetEventData : public EventData { + public: + TargetEventData(const lldb::TargetSP &target_sp); + + TargetEventData(const lldb::TargetSP &target_sp, + const ModuleList &module_list); + + ~TargetEventData() override; + + static ConstString GetFlavorString(); + + ConstString GetFlavor() const override { + return TargetEventData::GetFlavorString(); + } + + void Dump(Stream *s) const override; + + static const TargetEventData *GetEventDataFromEvent(const Event *event_ptr); + + static lldb::TargetSP GetTargetFromEvent(const Event *event_ptr); + + static ModuleList GetModuleListFromEvent(const Event *event_ptr); + + const lldb::TargetSP &GetTarget() const { return m_target_sp; } + + const ModuleList &GetModuleList() const { return m_module_list; } + + private: + lldb::TargetSP m_target_sp; + ModuleList m_module_list; + + TargetEventData(const TargetEventData &) = delete; + const TargetEventData &operator=(const TargetEventData &) = delete; + }; + + ~Target() override; + + static void SettingsInitialize(); + + static void SettingsTerminate(); + + static FileSpecList GetDefaultExecutableSearchPaths(); + + static FileSpecList GetDefaultDebugFileSearchPaths(); + + static ArchSpec GetDefaultArchitecture(); + + static void SetDefaultArchitecture(const ArchSpec &arch); + + bool IsDummyTarget() const { return m_is_dummy_target; } + + /// Find a binary on the system and return its Module, + /// or return an existing Module that is already in the Target. + /// + /// Given a ModuleSpec, find a binary satisifying that specification, + /// or identify a matching Module already present in the Target, + /// and return a shared pointer to it. + /// + /// \param[in] module_spec + /// The criteria that must be matched for the binary being loaded. + /// e.g. UUID, architecture, file path. + /// + /// \param[in] notify + /// If notify is true, and the Module is new to this Target, + /// Target::ModulesDidLoad will be called. + /// If notify is false, it is assumed that the caller is adding + /// multiple Modules and will call ModulesDidLoad with the + /// full list at the end. + /// ModulesDidLoad must be called when a Module/Modules have + /// been added to the target, one way or the other. + /// + /// \param[out] error_ptr + /// Optional argument, pointing to a Status object to fill in + /// with any results / messages while attempting to find/load + /// this binary. Many callers will be internal functions that + /// will handle / summarize the failures in a custom way and + /// don't use these messages. + /// + /// \return + /// An empty ModuleSP will be returned if no matching file + /// was found. If error_ptr was non-nullptr, an error message + /// will likely be provided. + lldb::ModuleSP GetOrCreateModule(const ModuleSpec &module_spec, bool notify, + Status *error_ptr = nullptr); + + // Settings accessors + + static TargetProperties &GetGlobalProperties(); + + std::recursive_mutex &GetAPIMutex(); + + void DeleteCurrentProcess(); + + void CleanupProcess(); + + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. The dumped content will be only what has + /// been loaded or parsed up to this point at which this function + /// is called, so this is a good way to see what has been parsed + /// in a target. + /// + /// \param[in] s + /// The stream to which to dump the object description. + void Dump(Stream *s, lldb::DescriptionLevel description_level); + + // If listener_sp is null, the listener of the owning Debugger object will be + // used. + const lldb::ProcessSP &CreateProcess(lldb::ListenerSP listener_sp, + llvm::StringRef plugin_name, + const FileSpec *crash_file, + bool can_connect); + + const lldb::ProcessSP &GetProcessSP() const; + + bool IsValid() { return m_valid; } + + void Destroy(); + + Status Launch(ProcessLaunchInfo &launch_info, + Stream *stream); // Optional stream to receive first stop info + + Status Attach(ProcessAttachInfo &attach_info, + Stream *stream); // Optional stream to receive first stop info + + // This part handles the breakpoints. + + BreakpointList &GetBreakpointList(bool internal = false); + + const BreakpointList &GetBreakpointList(bool internal = false) const; + + lldb::BreakpointSP GetLastCreatedBreakpoint() { + return m_last_created_breakpoint; + } + + lldb::BreakpointSP GetBreakpointByID(lldb::break_id_t break_id); + + // Use this to create a file and line breakpoint to a given module or all + // module it is nullptr + lldb::BreakpointSP 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 request_hardware, + LazyBool move_to_nearest_code); + + // Use this to create breakpoint that matches regex against the source lines + // in files given in source_file_list: If function_names is non-empty, also + // filter by function after the matches are made. + lldb::BreakpointSP CreateSourceRegexBreakpoint( + const FileSpecList *containingModules, + const FileSpecList *source_file_list, + const std::unordered_set &function_names, + RegularExpression source_regex, bool internal, bool request_hardware, + LazyBool move_to_nearest_code); + + // Use this to create a breakpoint from a load address + lldb::BreakpointSP CreateBreakpoint(lldb::addr_t load_addr, bool internal, + bool request_hardware); + + // Use this to create a breakpoint from a load address and a module file spec + lldb::BreakpointSP CreateAddressInModuleBreakpoint(lldb::addr_t file_addr, + bool internal, + const FileSpec *file_spec, + bool request_hardware); + + // Use this to create Address breakpoints: + lldb::BreakpointSP CreateBreakpoint(const Address &addr, bool internal, + bool request_hardware); + + // Use this to create a function breakpoint by regexp in + // containingModule/containingSourceFiles, or all modules if it is nullptr + // When "skip_prologue is set to eLazyBoolCalculate, we use the current + // target setting, else we use the values passed in + lldb::BreakpointSP CreateFuncRegexBreakpoint( + const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, RegularExpression func_regexp, + lldb::LanguageType requested_language, LazyBool skip_prologue, + bool internal, bool request_hardware); + + // Use this to create a function breakpoint by name in containingModule, or + // all modules if it is nullptr When "skip_prologue is set to + // eLazyBoolCalculate, we use the current target setting, else we use the + // values passed in. func_name_type_mask is or'ed values from the + // FunctionNameType enum. + lldb::BreakpointSP CreateBreakpoint( + const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, const char *func_name, + lldb::FunctionNameType func_name_type_mask, lldb::LanguageType language, + lldb::addr_t offset, LazyBool skip_prologue, bool internal, + bool request_hardware); + + lldb::BreakpointSP + CreateExceptionBreakpoint(enum lldb::LanguageType language, bool catch_bp, + bool throw_bp, bool internal, + Args *additional_args = nullptr, + Status *additional_args_error = nullptr); + + lldb::BreakpointSP 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 = nullptr); + + // This is the same as the func_name breakpoint except that you can specify a + // vector of names. This is cheaper than a regular expression breakpoint in + // the case where you just want to set a breakpoint on a set of names you + // already know. func_name_type_mask is or'ed values from the + // FunctionNameType enum. + lldb::BreakpointSP CreateBreakpoint( + const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, const char *func_names[], + size_t num_names, lldb::FunctionNameType func_name_type_mask, + lldb::LanguageType language, lldb::addr_t offset, LazyBool skip_prologue, + bool internal, bool request_hardware); + + lldb::BreakpointSP + CreateBreakpoint(const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, + const std::vector &func_names, + lldb::FunctionNameType func_name_type_mask, + lldb::LanguageType language, lldb::addr_t m_offset, + LazyBool skip_prologue, bool internal, + bool request_hardware); + + // Use this to create a general breakpoint: + lldb::BreakpointSP CreateBreakpoint(lldb::SearchFilterSP &filter_sp, + lldb::BreakpointResolverSP &resolver_sp, + bool internal, bool request_hardware, + bool resolve_indirect_symbols); + + // Use this to create a watchpoint: + lldb::WatchpointSP CreateWatchpoint(lldb::addr_t addr, size_t size, + const CompilerType *type, uint32_t kind, + Status &error); + + lldb::WatchpointSP GetLastCreatedWatchpoint() { + return m_last_created_watchpoint; + } + + WatchpointList &GetWatchpointList() { return m_watchpoint_list; } + + // Manages breakpoint names: + void AddNameToBreakpoint(BreakpointID &id, const char *name, Status &error); + + void AddNameToBreakpoint(lldb::BreakpointSP &bp_sp, const char *name, + Status &error); + + void RemoveNameFromBreakpoint(lldb::BreakpointSP &bp_sp, ConstString name); + + BreakpointName *FindBreakpointName(ConstString name, bool can_create, + Status &error); + + void DeleteBreakpointName(ConstString name); + + void ConfigureBreakpointName(BreakpointName &bp_name, + const BreakpointOptions &options, + const BreakpointName::Permissions &permissions); + void ApplyNameToBreakpoints(BreakpointName &bp_name); + + // This takes ownership of the name obj passed in. + void AddBreakpointName(BreakpointName *bp_name); + + void GetBreakpointNames(std::vector &names); + + // This call removes ALL breakpoints regardless of permission. + void RemoveAllBreakpoints(bool internal_also = false); + + // This removes all the breakpoints, but obeys the ePermDelete on them. + void RemoveAllowedBreakpoints(); + + void DisableAllBreakpoints(bool internal_also = false); + + void DisableAllowedBreakpoints(); + + void EnableAllBreakpoints(bool internal_also = false); + + void EnableAllowedBreakpoints(); + + bool DisableBreakpointByID(lldb::break_id_t break_id); + + bool EnableBreakpointByID(lldb::break_id_t break_id); + + bool RemoveBreakpointByID(lldb::break_id_t break_id); + + // The flag 'end_to_end', default to true, signifies that the operation is + // performed end to end, for both the debugger and the debuggee. + + bool RemoveAllWatchpoints(bool end_to_end = true); + + bool DisableAllWatchpoints(bool end_to_end = true); + + bool EnableAllWatchpoints(bool end_to_end = true); + + bool ClearAllWatchpointHitCounts(); + + bool ClearAllWatchpointHistoricValues(); + + bool IgnoreAllWatchpoints(uint32_t ignore_count); + + bool DisableWatchpointByID(lldb::watch_id_t watch_id); + + bool EnableWatchpointByID(lldb::watch_id_t watch_id); + + bool RemoveWatchpointByID(lldb::watch_id_t watch_id); + + bool IgnoreWatchpointByID(lldb::watch_id_t watch_id, uint32_t ignore_count); + + Status SerializeBreakpointsToFile(const FileSpec &file, + const BreakpointIDList &bp_ids, + bool append); + + Status CreateBreakpointsFromFile(const FileSpec &file, + BreakpointIDList &new_bps); + + Status CreateBreakpointsFromFile(const FileSpec &file, + std::vector &names, + BreakpointIDList &new_bps); + + /// Get \a load_addr as a callable code load address for this target + /// + /// Take \a load_addr and potentially add any address bits that are + /// needed to make the address callable. For ARM this can set bit + /// zero (if it already isn't) if \a load_addr is a thumb function. + /// If \a addr_class is set to AddressClass::eInvalid, then the address + /// adjustment will always happen. If it is set to an address class + /// that doesn't have code in it, LLDB_INVALID_ADDRESS will be + /// returned. + lldb::addr_t GetCallableLoadAddress( + lldb::addr_t load_addr, + AddressClass addr_class = AddressClass::eInvalid) const; + + /// Get \a load_addr as an opcode for this target. + /// + /// Take \a load_addr and potentially strip any address bits that are + /// needed to make the address point to an opcode. For ARM this can + /// clear bit zero (if it already isn't) if \a load_addr is a + /// thumb function and load_addr is in code. + /// If \a addr_class is set to AddressClass::eInvalid, then the address + /// adjustment will always happen. If it is set to an address class + /// that doesn't have code in it, LLDB_INVALID_ADDRESS will be + /// returned. + lldb::addr_t + GetOpcodeLoadAddress(lldb::addr_t load_addr, + AddressClass addr_class = AddressClass::eInvalid) const; + + // Get load_addr as breakable load address for this target. Take a addr and + // check if for any reason there is a better address than this to put a + // breakpoint on. If there is then return that address. For MIPS, if + // instruction at addr is a delay slot instruction then this method will find + // the address of its previous instruction and return that address. + lldb::addr_t GetBreakableLoadAddress(lldb::addr_t addr); + + void ModulesDidLoad(ModuleList &module_list); + + void ModulesDidUnload(ModuleList &module_list, bool delete_locations); + + void SymbolsDidLoad(ModuleList &module_list); + + void ClearModules(bool delete_locations); + + /// Called as the last function in Process::DidExec(). + /// + /// Process::DidExec() will clear a lot of state in the process, + /// then try to reload a dynamic loader plugin to discover what + /// binaries are currently available and then this function should + /// be called to allow the target to do any cleanup after everything + /// has been figured out. It can remove breakpoints that no longer + /// make sense as the exec might have changed the target + /// architecture, and unloaded some modules that might get deleted. + void DidExec(); + + /// Gets the module for the main executable. + /// + /// Each process has a notion of a main executable that is the file + /// that will be executed or attached to. Executable files can have + /// dependent modules that are discovered from the object files, or + /// discovered at runtime as things are dynamically loaded. + /// + /// \return + /// The shared pointer to the executable module which can + /// contains a nullptr Module object if no executable has been + /// set. + /// + /// \see DynamicLoader + /// \see ObjectFile::GetDependentModules (FileSpecList&) + /// \see Process::SetExecutableModule(lldb::ModuleSP&) + lldb::ModuleSP GetExecutableModule(); + + Module *GetExecutableModulePointer(); + + /// Set the main executable module. + /// + /// Each process has a notion of a main executable that is the file + /// that will be executed or attached to. Executable files can have + /// dependent modules that are discovered from the object files, or + /// discovered at runtime as things are dynamically loaded. + /// + /// Setting the executable causes any of the current dependent + /// image information to be cleared and replaced with the static + /// dependent image information found by calling + /// ObjectFile::GetDependentModules (FileSpecList&) on the main + /// executable and any modules on which it depends. Calling + /// Process::GetImages() will return the newly found images that + /// were obtained from all of the object files. + /// + /// \param[in] module_sp + /// A shared pointer reference to the module that will become + /// the main executable for this process. + /// + /// \param[in] load_dependent_files + /// If \b true then ask the object files to track down any + /// known dependent files. + /// + /// \see ObjectFile::GetDependentModules (FileSpecList&) + /// \see Process::GetImages() + void SetExecutableModule( + lldb::ModuleSP &module_sp, + LoadDependentFiles load_dependent_files = eLoadDependentsDefault); + + bool LoadScriptingResources(std::list &errors, + Stream *feedback_stream = nullptr, + bool continue_on_error = true) { + return m_images.LoadScriptingResourcesInTarget( + this, errors, feedback_stream, continue_on_error); + } + + /// Get accessor for the images for this process. + /// + /// Each process has a notion of a main executable that is the file + /// that will be executed or attached to. Executable files can have + /// dependent modules that are discovered from the object files, or + /// discovered at runtime as things are dynamically loaded. After + /// a main executable has been set, the images will contain a list + /// of all the files that the executable depends upon as far as the + /// object files know. These images will usually contain valid file + /// virtual addresses only. When the process is launched or attached + /// to, the DynamicLoader plug-in will discover where these images + /// were loaded in memory and will resolve the load virtual + /// addresses is each image, and also in images that are loaded by + /// code. + /// + /// \return + /// A list of Module objects in a module list. + const ModuleList &GetImages() const { return m_images; } + + ModuleList &GetImages() { return m_images; } + + /// Return whether this FileSpec corresponds to a module that should be + /// considered for general searches. + /// + /// This API will be consulted by the SearchFilterForUnconstrainedSearches + /// and any module that returns \b true will not be searched. Note the + /// SearchFilterForUnconstrainedSearches is the search filter that + /// gets used in the CreateBreakpoint calls when no modules is provided. + /// + /// The target call at present just consults the Platform's call of the + /// same name. + /// + /// \param[in] module_spec + /// Path to the module. + /// + /// \return \b true if the module should be excluded, \b false otherwise. + bool ModuleIsExcludedForUnconstrainedSearches(const FileSpec &module_spec); + + /// Return whether this module should be considered for general searches. + /// + /// This API will be consulted by the SearchFilterForUnconstrainedSearches + /// and any module that returns \b true will not be searched. Note the + /// SearchFilterForUnconstrainedSearches is the search filter that + /// gets used in the CreateBreakpoint calls when no modules is provided. + /// + /// The target call at present just consults the Platform's call of the + /// same name. + /// + /// FIXME: When we get time we should add a way for the user to set modules + /// that they + /// don't want searched, in addition to or instead of the platform ones. + /// + /// \param[in] module_sp + /// A shared pointer reference to the module that checked. + /// + /// \return \b true if the module should be excluded, \b false otherwise. + bool + ModuleIsExcludedForUnconstrainedSearches(const lldb::ModuleSP &module_sp); + + const ArchSpec &GetArchitecture() const { return m_arch.GetSpec(); } + + /// Set the architecture for this target. + /// + /// If the current target has no Images read in, then this just sets the + /// architecture, which will be used to select the architecture of the + /// ExecutableModule when that is set. If the current target has an + /// ExecutableModule, then calling SetArchitecture with a different + /// architecture from the currently selected one will reset the + /// ExecutableModule to that slice of the file backing the ExecutableModule. + /// If the file backing the ExecutableModule does not contain a fork of this + /// architecture, then this code will return false, and the architecture + /// won't be changed. If the input arch_spec is the same as the already set + /// architecture, this is a no-op. + /// + /// \param[in] arch_spec + /// The new architecture. + /// + /// \param[in] set_platform + /// If \b true, then the platform will be adjusted if the currently + /// selected platform is not compatible with the architecture being set. + /// If \b false, then just the architecture will be set even if the + /// currently selected platform isn't compatible (in case it might be + /// manually set following this function call). + /// + /// \return + /// \b true if the architecture was successfully set, \b false otherwise. + bool SetArchitecture(const ArchSpec &arch_spec, bool set_platform = false); + + bool MergeArchitecture(const ArchSpec &arch_spec); + + Architecture *GetArchitecturePlugin() const { return m_arch.GetPlugin(); } + + Debugger &GetDebugger() { return m_debugger; } + + size_t ReadMemoryFromFileCache(const Address &addr, void *dst, size_t dst_len, + Status &error); + + // Reading memory through the target allows us to skip going to the process + // for reading memory if possible and it allows us to try and read from any + // constant sections in our object files on disk. If you always want live + // program memory, read straight from the process. If you possibly want to + // read from const sections in object files, read from the target. This + // version of ReadMemory will try and read memory from the process if the + // process is alive. The order is: + // 1 - if (force_live_memory == false) and the address falls in a read-only + // section, then read from the file cache + // 2 - if there is a process, then read from memory + // 3 - if there is no process, then read from the file cache + size_t ReadMemory(const Address &addr, void *dst, size_t dst_len, + Status &error, bool force_live_memory = false, + lldb::addr_t *load_addr_ptr = nullptr); + + size_t ReadCStringFromMemory(const Address &addr, std::string &out_str, + Status &error, bool force_live_memory = false); + + size_t ReadCStringFromMemory(const Address &addr, char *dst, + size_t dst_max_len, Status &result_error, + bool force_live_memory = false); + + /// Read a NULL terminated string from memory + /// + /// This function will read a cache page at a time until a NULL string + /// terminator is found. It will stop reading if an aligned sequence of NULL + /// termination \a type_width bytes is not found before reading \a + /// cstr_max_len bytes. The results are always guaranteed to be NULL + /// terminated, and that no more than (max_bytes - type_width) bytes will be + /// read. + /// + /// \param[in] addr + /// The address to start the memory read. + /// + /// \param[in] dst + /// A character buffer containing at least max_bytes. + /// + /// \param[in] max_bytes + /// The maximum number of bytes to read. + /// + /// \param[in] error + /// The error status of the read operation. + /// + /// \param[in] type_width + /// The size of the null terminator (1 to 4 bytes per + /// character). Defaults to 1. + /// + /// \return + /// The error status or the number of bytes prior to the null terminator. + size_t ReadStringFromMemory(const Address &addr, char *dst, size_t max_bytes, + Status &error, size_t type_width, + bool force_live_memory = true); + + size_t ReadScalarIntegerFromMemory(const Address &addr, uint32_t byte_size, + bool is_signed, Scalar &scalar, + Status &error, + bool force_live_memory = false); + + uint64_t ReadUnsignedIntegerFromMemory(const Address &addr, + size_t integer_byte_size, + uint64_t fail_value, Status &error, + bool force_live_memory = false); + + bool ReadPointerFromMemory(const Address &addr, Status &error, + Address &pointer_addr, + bool force_live_memory = false); + + SectionLoadList &GetSectionLoadList() { + return m_section_load_history.GetCurrentSectionLoadList(); + } + + static Target *GetTargetFromContexts(const ExecutionContext *exe_ctx_ptr, + const SymbolContext *sc_ptr); + + // lldb::ExecutionContextScope pure virtual functions + lldb::TargetSP CalculateTarget() override; + + lldb::ProcessSP CalculateProcess() override; + + lldb::ThreadSP CalculateThread() override; + + lldb::StackFrameSP CalculateStackFrame() override; + + void CalculateExecutionContext(ExecutionContext &exe_ctx) override; + + PathMappingList &GetImageSearchPathList(); + + llvm::Expected + GetScratchTypeSystemForLanguage(lldb::LanguageType language, + bool create_on_demand = true); + + std::vector GetScratchTypeSystems(bool create_on_demand = true); + + PersistentExpressionState * + GetPersistentExpressionStateForLanguage(lldb::LanguageType language); + + // Creates a UserExpression for the given language, the rest of the + // parameters have the same meaning as for the UserExpression constructor. + // Returns a new-ed object which the caller owns. + + UserExpression * + GetUserExpressionForLanguage(llvm::StringRef expr, llvm::StringRef prefix, + lldb::LanguageType language, + Expression::ResultType desired_type, + const EvaluateExpressionOptions &options, + ValueObject *ctx_obj, Status &error); + + // Creates a FunctionCaller for the given language, the rest of the + // parameters have the same meaning as for the FunctionCaller constructor. + // Since a FunctionCaller can't be + // IR Interpreted, it makes no sense to call this with an + // ExecutionContextScope that lacks + // a Process. + // Returns a new-ed object which the caller owns. + + FunctionCaller *GetFunctionCallerForLanguage(lldb::LanguageType language, + const CompilerType &return_type, + const Address &function_address, + const ValueList &arg_value_list, + const char *name, Status &error); + + /// Creates and installs a UtilityFunction for the given language. + llvm::Expected> + CreateUtilityFunction(std::string expression, std::string name, + lldb::LanguageType language, ExecutionContext &exe_ctx); + + // Install any files through the platform that need be to installed prior to + // launching or attaching. + Status Install(ProcessLaunchInfo *launch_info); + + bool ResolveFileAddress(lldb::addr_t load_addr, Address &so_addr); + + bool ResolveLoadAddress(lldb::addr_t load_addr, Address &so_addr, + uint32_t stop_id = SectionLoadHistory::eStopIDNow); + + bool SetSectionLoadAddress(const lldb::SectionSP §ion, + lldb::addr_t load_addr, + bool warn_multiple = false); + + size_t UnloadModuleSections(const lldb::ModuleSP &module_sp); + + size_t UnloadModuleSections(const ModuleList &module_list); + + bool SetSectionUnloaded(const lldb::SectionSP §ion_sp); + + bool SetSectionUnloaded(const lldb::SectionSP §ion_sp, + lldb::addr_t load_addr); + + void ClearAllLoadedSections(); + + /// Set the \a Trace object containing processor trace information of this + /// target. + /// + /// \param[in] trace_sp + /// The trace object. + void SetTrace(const lldb::TraceSP &trace_sp); + + /// Get the \a Trace object containing processor trace information of this + /// target. + /// + /// \return + /// The trace object. It might be undefined. + lldb::TraceSP GetTrace(); + + /// Create a \a Trace object for the current target using the using the + /// default supported tracing technology for this process. + /// + /// \return + /// The new \a Trace or an \a llvm::Error if a \a Trace already exists or + /// the trace couldn't be created. + llvm::Expected CreateTrace(); + + /// If a \a Trace object is present, this returns it, otherwise a new Trace is + /// created with \a Trace::CreateTrace. + llvm::Expected GetTraceOrCreate(); + + // Since expressions results can persist beyond the lifetime of a process, + // and the const expression results are available after a process is gone, we + // provide a way for expressions to be evaluated from the Target itself. If + // an expression is going to be run, then it should have a frame filled in in + // the execution context. + lldb::ExpressionResults EvaluateExpression( + llvm::StringRef expression, ExecutionContextScope *exe_scope, + lldb::ValueObjectSP &result_valobj_sp, + const EvaluateExpressionOptions &options = EvaluateExpressionOptions(), + std::string *fixed_expression = nullptr, ValueObject *ctx_obj = nullptr); + + lldb::ExpressionVariableSP GetPersistentVariable(ConstString name); + + lldb::addr_t GetPersistentSymbol(ConstString name); + + /// This method will return the address of the starting function for + /// this binary, e.g. main() or its equivalent. This can be used as + /// an address of a function that is not called once a binary has + /// started running - e.g. as a return address for inferior function + /// calls that are unambiguous completion of the function call, not + /// called during the course of the inferior function code running. + /// + /// If no entry point can be found, an invalid address is returned. + /// + /// \param [out] err + /// This object will be set to failure if no entry address could + /// be found, and may contain a helpful error message. + // + /// \return + /// Returns the entry address for this program, or an error + /// if none can be found. + llvm::Expected GetEntryPointAddress(); + + // Target Stop Hooks + class StopHook : public UserID { + public: + StopHook(const StopHook &rhs); + virtual ~StopHook() = default; + + enum class StopHookKind : uint32_t { CommandBased = 0, ScriptBased }; + enum class StopHookResult : uint32_t { + KeepStopped = 0, + RequestContinue, + AlreadyContinued + }; + + lldb::TargetSP &GetTarget() { return m_target_sp; } + + // Set the specifier. The stop hook will own the specifier, and is + // responsible for deleting it when we're done. + void SetSpecifier(SymbolContextSpecifier *specifier); + + SymbolContextSpecifier *GetSpecifier() { return m_specifier_sp.get(); } + + bool ExecutionContextPasses(const ExecutionContext &exe_ctx); + + // Called on stop, this gets passed the ExecutionContext for each "stop + // with a reason" thread. It should add to the stream whatever text it + // wants to show the user, and return False to indicate it wants the target + // not to stop. + virtual StopHookResult HandleStop(ExecutionContext &exe_ctx, + lldb::StreamSP output) = 0; + + // Set the Thread Specifier. The stop hook will own the thread specifier, + // and is responsible for deleting it when we're done. + void SetThreadSpecifier(ThreadSpec *specifier); + + ThreadSpec *GetThreadSpecifier() { return m_thread_spec_up.get(); } + + bool IsActive() { return m_active; } + + void SetIsActive(bool is_active) { m_active = is_active; } + + void SetAutoContinue(bool auto_continue) { + m_auto_continue = auto_continue; + } + + bool GetAutoContinue() const { return m_auto_continue; } + + void GetDescription(Stream *s, lldb::DescriptionLevel level) const; + virtual void GetSubclassDescription(Stream *s, + lldb::DescriptionLevel level) const = 0; + + protected: + lldb::TargetSP m_target_sp; + lldb::SymbolContextSpecifierSP m_specifier_sp; + std::unique_ptr m_thread_spec_up; + bool m_active = true; + bool m_auto_continue = false; + + StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid); + }; + + class StopHookCommandLine : public StopHook { + public: + virtual ~StopHookCommandLine() = default; + + StringList &GetCommands() { return m_commands; } + void SetActionFromString(const std::string &strings); + void SetActionFromStrings(const std::vector &strings); + + StopHookResult HandleStop(ExecutionContext &exc_ctx, + lldb::StreamSP output_sp) override; + void GetSubclassDescription(Stream *s, + lldb::DescriptionLevel level) const override; + + private: + StringList m_commands; + // Use CreateStopHook to make a new empty stop hook. The GetCommandPointer + // and fill it with commands, and SetSpecifier to set the specifier shared + // pointer (can be null, that will match anything.) + StopHookCommandLine(lldb::TargetSP target_sp, lldb::user_id_t uid) + : StopHook(target_sp, uid) {} + friend class Target; + }; + + class StopHookScripted : public StopHook { + public: + virtual ~StopHookScripted() = default; + StopHookResult HandleStop(ExecutionContext &exc_ctx, + lldb::StreamSP output) override; + + Status SetScriptCallback(std::string class_name, + StructuredData::ObjectSP extra_args_sp); + + void GetSubclassDescription(Stream *s, + lldb::DescriptionLevel level) const override; + + private: + std::string m_class_name; + /// This holds the dictionary of keys & values that can be used to + /// parametrize any given callback's behavior. + StructuredDataImpl m_extra_args; + /// This holds the python callback object. + StructuredData::GenericSP m_implementation_sp; + + /// Use CreateStopHook to make a new empty stop hook. The GetCommandPointer + /// and fill it with commands, and SetSpecifier to set the specifier shared + /// pointer (can be null, that will match anything.) + StopHookScripted(lldb::TargetSP target_sp, lldb::user_id_t uid) + : StopHook(target_sp, uid) {} + friend class Target; + }; + + typedef std::shared_ptr StopHookSP; + + /// Add an empty stop hook to the Target's stop hook list, and returns a + /// shared pointer to it in new_hook. Returns the id of the new hook. + StopHookSP CreateStopHook(StopHook::StopHookKind kind); + + /// If you tried to create a stop hook, and that failed, call this to + /// remove the stop hook, as it will also reset the stop hook counter. + void UndoCreateStopHook(lldb::user_id_t uid); + + // Runs the stop hooks that have been registered for this target. + // Returns true if the stop hooks cause the target to resume. + bool RunStopHooks(); + + size_t GetStopHookSize(); + + bool SetSuppresStopHooks(bool suppress) { + bool old_value = m_suppress_stop_hooks; + m_suppress_stop_hooks = suppress; + return old_value; + } + + bool GetSuppressStopHooks() { return m_suppress_stop_hooks; } + + bool RemoveStopHookByID(lldb::user_id_t uid); + + void RemoveAllStopHooks(); + + StopHookSP GetStopHookByID(lldb::user_id_t uid); + + bool SetStopHookActiveStateByID(lldb::user_id_t uid, bool active_state); + + void SetAllStopHooksActiveState(bool active_state); + + size_t GetNumStopHooks() const { return m_stop_hooks.size(); } + + StopHookSP GetStopHookAtIndex(size_t index) { + if (index >= GetNumStopHooks()) + return StopHookSP(); + StopHookCollection::iterator pos = m_stop_hooks.begin(); + + while (index > 0) { + pos++; + index--; + } + return (*pos).second; + } + + lldb::PlatformSP GetPlatform() { return m_platform_sp; } + + void SetPlatform(const lldb::PlatformSP &platform_sp) { + m_platform_sp = platform_sp; + } + + SourceManager &GetSourceManager(); + + // Methods. + lldb::SearchFilterSP + GetSearchFilterForModule(const FileSpec *containingModule); + + lldb::SearchFilterSP + GetSearchFilterForModuleList(const FileSpecList *containingModuleList); + + lldb::SearchFilterSP + GetSearchFilterForModuleAndCUList(const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles); + + lldb::REPLSP GetREPL(Status &err, lldb::LanguageType language, + const char *repl_options, bool can_create); + + void SetREPL(lldb::LanguageType language, lldb::REPLSP repl_sp); + + StackFrameRecognizerManager &GetFrameRecognizerManager() { + return *m_frame_recognizer_manager_up; + } + + protected: + /// Implementing of ModuleList::Notifier. + + void NotifyModuleAdded(const ModuleList &module_list, + const lldb::ModuleSP &module_sp) override; + + void NotifyModuleRemoved(const ModuleList &module_list, + const lldb::ModuleSP &module_sp) override; + + void NotifyModuleUpdated(const ModuleList &module_list, + const lldb::ModuleSP &old_module_sp, + const lldb::ModuleSP &new_module_sp) override; + + void NotifyWillClearList(const ModuleList &module_list) override; + + void NotifyModulesRemoved(lldb_private::ModuleList &module_list) override; + + class Arch { + public: + explicit Arch(const ArchSpec &spec); + const Arch &operator=(const ArchSpec &spec); + + const ArchSpec &GetSpec() const { return m_spec; } + Architecture *GetPlugin() const { return m_plugin_up.get(); } + + private: + ArchSpec m_spec; + std::unique_ptr m_plugin_up; + }; + // Member variables. + Debugger &m_debugger; + lldb::PlatformSP m_platform_sp; ///< The platform for this target. + std::recursive_mutex m_mutex; ///< An API mutex that is used by the lldb::SB* + /// classes make the SB interface thread safe + /// When the private state thread calls SB API's - usually because it is + /// running OS plugin or Python ThreadPlan code - it should not block on the + /// API mutex that is held by the code that kicked off the sequence of events + /// that led us to run the code. We hand out this mutex instead when we + /// detect that code is running on the private state thread. + std::recursive_mutex m_private_mutex; + Arch m_arch; + ModuleList m_images; ///< The list of images for this process (shared + /// libraries and anything dynamically loaded). + SectionLoadHistory m_section_load_history; + BreakpointList m_breakpoint_list; + BreakpointList m_internal_breakpoint_list; + using BreakpointNameList = std::map; + BreakpointNameList m_breakpoint_names; + + lldb::BreakpointSP m_last_created_breakpoint; + WatchpointList m_watchpoint_list; + lldb::WatchpointSP m_last_created_watchpoint; + // We want to tightly control the process destruction process so we can + // correctly tear down everything that we need to, so the only class that + // knows about the process lifespan is this target class. + lldb::ProcessSP m_process_sp; + lldb::SearchFilterSP m_search_filter_sp; + PathMappingList m_image_search_paths; + TypeSystemMap m_scratch_type_system_map; + ++ lldb::ListenerSP m_hijacker_on_process_creation_sp; ++ + typedef std::map REPLMap; + REPLMap m_repl_map; + + lldb::SourceManagerUP m_source_manager_up; + + typedef std::map StopHookCollection; + StopHookCollection m_stop_hooks; + lldb::user_id_t m_stop_hook_next_id; + uint32_t m_latest_stop_hook_id; /// This records the last natural stop at + /// which we ran a stop-hook. + bool m_valid; + bool m_suppress_stop_hooks; /// Used to not run stop hooks for expressions + bool m_is_dummy_target; + unsigned m_next_persistent_variable_index = 0; + /// An optional \a lldb_private::Trace object containing processor trace + /// information of this target. + lldb::TraceSP m_trace_sp; + /// Stores the frame recognizers of this target. + lldb::StackFrameRecognizerManagerUP m_frame_recognizer_manager_up; + + static void ImageSearchPathsChanged(const PathMappingList &path_list, + void *baton); + + // Utilities for `statistics` command. + private: + // Target metrics storage. + TargetStats m_stats; + + public: + /// Get metrics associated with this target in JSON format. + /// + /// Target metrics help measure timings and information that is contained in + /// a target. These are designed to help measure performance of a debug + /// session as well as represent the current state of the target, like + /// information on the currently modules, currently set breakpoints and more. + /// + /// \return + /// Returns a JSON value that contains all target metrics. + llvm::json::Value ReportStatistics(); + + TargetStats &GetStatistics() { return m_stats; } + + private: + /// Construct with optional file and arch. + /// + /// This member is private. Clients must use + /// TargetList::CreateTarget(const FileSpec*, const ArchSpec*) + /// so all targets can be tracked from the central target list. + /// + /// \see TargetList::CreateTarget(const FileSpec*, const ArchSpec*) + Target(Debugger &debugger, const ArchSpec &target_arch, + const lldb::PlatformSP &platform_sp, bool is_dummy_target); + + // Helper function. + bool ProcessIsValid(); + + // Copy breakpoints, stop hooks and so forth from the dummy target: + void PrimeFromDummyTarget(Target &target); + + void AddBreakpoint(lldb::BreakpointSP breakpoint_sp, bool internal); + + void FinalizeFileActions(ProcessLaunchInfo &info); + + /// Return a recommended size for memory reads at \a addr, optimizing for + /// cache usage. + lldb::addr_t GetReasonableReadSize(const Address &addr); + + Target(const Target &) = delete; + const Target &operator=(const Target &) = delete; + }; + + } // namespace lldb_private + + #endif // LLDB_TARGET_TARGET_H +diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py +index 950dd41666fd..98b0922e9cfe 100644 +--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py ++++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py +@@ -1,2569 +1,2574 @@ + """ + LLDB module which provides the abstract base class of lldb test case. + + The concrete subclass can override lldbtest.TestBase in order to inherit the + common behavior for unitest.TestCase.setUp/tearDown implemented in this file. + + The subclass should override the attribute mydir in order for the python runtime + to locate the individual test cases when running as part of a large test suite + or when running each test case as a separate python invocation. + + ./dotest.py provides a test driver which sets up the environment to run the + entire of part of the test suite . Example: + + # Exercises the test suite in the types directory.... + /Volumes/data/lldb/svn/ToT/test $ ./dotest.py -A x86_64 types + ... + + Session logs for test failures/errors/unexpected successes will go into directory '2012-05-16-13_35_42' + Command invoked: python ./dotest.py -A x86_64 types + compilers=['clang'] + + Configuration: arch=x86_64 compiler=clang + ---------------------------------------------------------------------- + Collected 72 tests + + ........................................................................ + ---------------------------------------------------------------------- + Ran 72 tests in 135.468s + + OK + $ + """ + + from __future__ import absolute_import + from __future__ import print_function + + # System modules + import abc + from distutils.version import LooseVersion + from functools import wraps + import gc + import glob + import io + import os.path + import re + import shutil + import signal + from subprocess import * + import sys + import time + import traceback + import distutils.spawn + + # Third-party modules + import unittest2 + from six import add_metaclass + from six import StringIO as SixStringIO + import six + + # LLDB modules + import lldb + from . import configuration + from . import decorators + from . import lldbplatformutil + from . import lldbtest_config + from . import lldbutil + from . import test_categories + from lldbsuite.support import encoded_file + from lldbsuite.support import funcutils + from lldbsuite.support import seven + from lldbsuite.test.builders import get_builder + from lldbsuite.test_event import build_exception + + # See also dotest.parseOptionsAndInitTestdirs(), where the environment variables + # LLDB_COMMAND_TRACE is set from '-t' option. + + # By default, traceAlways is False. + if "LLDB_COMMAND_TRACE" in os.environ and os.environ[ + "LLDB_COMMAND_TRACE"] == "YES": + traceAlways = True + else: + traceAlways = False + + # By default, doCleanup is True. + if "LLDB_DO_CLEANUP" in os.environ and os.environ["LLDB_DO_CLEANUP"] == "NO": + doCleanup = False + else: + doCleanup = True + + + # + # Some commonly used assert messages. + # + + COMMAND_FAILED_AS_EXPECTED = "Command has failed as expected" + + CURRENT_EXECUTABLE_SET = "Current executable set successfully" + + PROCESS_IS_VALID = "Process is valid" + + PROCESS_KILLED = "Process is killed successfully" + + PROCESS_EXITED = "Process exited successfully" + + PROCESS_STOPPED = "Process status should be stopped" + + RUN_SUCCEEDED = "Process is launched successfully" + + RUN_COMPLETED = "Process exited successfully" + + BACKTRACE_DISPLAYED_CORRECTLY = "Backtrace displayed correctly" + + BREAKPOINT_CREATED = "Breakpoint created successfully" + + BREAKPOINT_STATE_CORRECT = "Breakpoint state is correct" + + BREAKPOINT_PENDING_CREATED = "Pending breakpoint created successfully" + + BREAKPOINT_HIT_ONCE = "Breakpoint resolved with hit count = 1" + + BREAKPOINT_HIT_TWICE = "Breakpoint resolved with hit count = 2" + + BREAKPOINT_HIT_THRICE = "Breakpoint resolved with hit count = 3" + + MISSING_EXPECTED_REGISTERS = "At least one expected register is unavailable." + + OBJECT_PRINTED_CORRECTLY = "Object printed correctly" + + SOURCE_DISPLAYED_CORRECTLY = "Source code displayed correctly" + + STEP_IN_SUCCEEDED = "Thread step-in succeeded" + + STEP_OUT_SUCCEEDED = "Thread step-out succeeded" + + STOPPED_DUE_TO_EXC_BAD_ACCESS = "Process should be stopped due to bad access exception" + + STOPPED_DUE_TO_ASSERT = "Process should be stopped due to an assertion" + + STOPPED_DUE_TO_BREAKPOINT = "Process should be stopped due to breakpoint" + + STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS = "%s, %s" % ( + STOPPED_DUE_TO_BREAKPOINT, "instead, the actual stop reason is: '%s'") + + STOPPED_DUE_TO_BREAKPOINT_CONDITION = "Stopped due to breakpoint condition" + + STOPPED_DUE_TO_BREAKPOINT_IGNORE_COUNT = "Stopped due to breakpoint and ignore count" + + STOPPED_DUE_TO_BREAKPOINT_JITTED_CONDITION = "Stopped due to breakpoint jitted condition" + + STOPPED_DUE_TO_SIGNAL = "Process state is stopped due to signal" + + STOPPED_DUE_TO_STEP_IN = "Process state is stopped due to step in" + + STOPPED_DUE_TO_WATCHPOINT = "Process should be stopped due to watchpoint" + + DATA_TYPES_DISPLAYED_CORRECTLY = "Data type(s) displayed correctly" + + VALID_BREAKPOINT = "Got a valid breakpoint" + + VALID_BREAKPOINT_LOCATION = "Got a valid breakpoint location" + + VALID_COMMAND_INTERPRETER = "Got a valid command interpreter" + + VALID_FILESPEC = "Got a valid filespec" + + VALID_MODULE = "Got a valid module" + + VALID_PROCESS = "Got a valid process" + + VALID_SYMBOL = "Got a valid symbol" + + VALID_TARGET = "Got a valid target" + + VALID_PLATFORM = "Got a valid platform" + + VALID_TYPE = "Got a valid type" + + VALID_VARIABLE = "Got a valid variable" + + VARIABLES_DISPLAYED_CORRECTLY = "Variable(s) displayed correctly" + + WATCHPOINT_CREATED = "Watchpoint created successfully" + + + def CMD_MSG(str): + '''A generic "Command '%s' did not return successfully" message generator.''' + return "Command '%s' did not return successfully" % str + + + def COMPLETION_MSG(str_before, str_after, completions): + '''A generic assertion failed message generator for the completion mechanism.''' + return ("'%s' successfully completes to '%s', but completions were:\n%s" + % (str_before, str_after, "\n".join(completions))) + + + def EXP_MSG(str, actual, exe): + '''A generic "'%s' returned unexpected result" message generator if exe. + Otherwise, it generates "'%s' does not match expected result" message.''' + + return "'%s' %s result, got '%s'" % ( + str, 'returned unexpected' if exe else 'does not match expected', actual.strip()) + + + def SETTING_MSG(setting): + '''A generic "Value of setting '%s' is not correct" message generator.''' + return "Value of setting '%s' is not correct" % setting + + + def line_number(filename, string_to_match): + """Helper function to return the line number of the first matched string.""" + with io.open(filename, mode='r', encoding="utf-8") as f: + for i, line in enumerate(f): + if line.find(string_to_match) != -1: + # Found our match. + return i + 1 + raise Exception( + "Unable to find '%s' within file %s" % + (string_to_match, filename)) + + def get_line(filename, line_number): + """Return the text of the line at the 1-based line number.""" + with io.open(filename, mode='r', encoding="utf-8") as f: + return f.readlines()[line_number - 1] + + def pointer_size(): + """Return the pointer size of the host system.""" + import ctypes + a_pointer = ctypes.c_void_p(0xffff) + return 8 * ctypes.sizeof(a_pointer) + + + def is_exe(fpath): + """Returns true if fpath is an executable.""" ++ if fpath == None: ++ return False ++ if sys.platform == 'win32': ++ if not fpath.endswith(".exe"): ++ fpath += ".exe" + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + + + def which(program): + """Returns the full path to a program; None otherwise.""" + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return None + + class ValueCheck: + def __init__(self, name=None, value=None, type=None, summary=None, + children=None): + """ + :param name: The name that the SBValue should have. None if the summary + should not be checked. + :param summary: The summary that the SBValue should have. None if the + summary should not be checked. + :param value: The value that the SBValue should have. None if the value + should not be checked. + :param type: The type that the SBValue result should have. None if the + type should not be checked. + :param children: A list of ValueChecks that need to match the children + of this SBValue. None if children shouldn't be checked. + The order of checks is the order of the checks in the + list. The number of checks has to match the number of + children. + """ + self.expect_name = name + self.expect_value = value + self.expect_type = type + self.expect_summary = summary + self.children = children + + def check_value(self, test_base, val, error_msg=None): + """ + Checks that the given value matches the currently set properties + of this ValueCheck. If a match failed, the given TestBase will + be used to emit an error. A custom error message can be specified + that will be used to describe failed check for this SBValue (but + not errors in the child values). + """ + + this_error_msg = error_msg if error_msg else "" + this_error_msg += "\nChecking SBValue: " + str(val) + + test_base.assertSuccess(val.GetError()) + + if self.expect_name: + test_base.assertEqual(self.expect_name, val.GetName(), + this_error_msg) + if self.expect_value: + test_base.assertEqual(self.expect_value, val.GetValue(), + this_error_msg) + if self.expect_type: + test_base.assertEqual(self.expect_type, val.GetDisplayTypeName(), + this_error_msg) + if self.expect_summary: + test_base.assertEqual(self.expect_summary, val.GetSummary(), + this_error_msg) + if self.children is not None: + self.check_value_children(test_base, val, error_msg) + + def check_value_children(self, test_base, val, error_msg=None): + """ + Checks that the children of a SBValue match a certain structure and + have certain properties. + + :param test_base: The current test's TestBase object. + :param val: The SBValue to check. + """ + + this_error_msg = error_msg if error_msg else "" + this_error_msg += "\nChecking SBValue: " + str(val) + + test_base.assertEqual(len(self.children), val.GetNumChildren(), this_error_msg) + + for i in range(0, val.GetNumChildren()): + expected_child = self.children[i] + actual_child = val.GetChildAtIndex(i) + child_error = "Checking child with index " + str(i) + ":\n" + error_msg + expected_child.check_value(test_base, actual_child, child_error) + + class recording(SixStringIO): + """ + A nice little context manager for recording the debugger interactions into + our session object. If trace flag is ON, it also emits the interactions + into the stderr. + """ + + def __init__(self, test, trace): + """Create a SixStringIO instance; record the session obj and trace flag.""" + SixStringIO.__init__(self) + # The test might not have undergone the 'setUp(self)' phase yet, so that + # the attribute 'session' might not even exist yet. + self.session = getattr(test, "session", None) if test else None + self.trace = trace + + def __enter__(self): + """ + Context management protocol on entry to the body of the with statement. + Just return the SixStringIO object. + """ + return self + + def __exit__(self, type, value, tb): + """ + Context management protocol on exit from the body of the with statement. + If trace is ON, it emits the recordings into stderr. Always add the + recordings to our session object. And close the SixStringIO object, too. + """ + if self.trace: + print(self.getvalue(), file=sys.stderr) + if self.session: + print(self.getvalue(), file=self.session) + self.close() + + + @add_metaclass(abc.ABCMeta) + class _BaseProcess(object): + + @abc.abstractproperty + def pid(self): + """Returns process PID if has been launched already.""" + + @abc.abstractmethod + def launch(self, executable, args, extra_env): + """Launches new process with given executable and args.""" + + @abc.abstractmethod + def terminate(self): + """Terminates previously launched process..""" + + + class _LocalProcess(_BaseProcess): + + def __init__(self, trace_on): + self._proc = None + self._trace_on = trace_on + self._delayafterterminate = 0.1 + + @property + def pid(self): + return self._proc.pid + + def launch(self, executable, args, extra_env): + env=None + if extra_env: + env = dict(os.environ) + env.update([kv.split("=", 1) for kv in extra_env]) + + self._proc = Popen( + [executable] + args, + stdout=open( + os.devnull) if not self._trace_on else None, + stdin=PIPE, + preexec_fn=lldbplatformutil.enable_attach, + env=env) + + def terminate(self): + if self._proc.poll() is None: + # Terminate _proc like it does the pexpect + signals_to_try = [ + sig for sig in [ + 'SIGHUP', + 'SIGCONT', + 'SIGINT'] if sig in dir(signal)] + for sig in signals_to_try: + try: + self._proc.send_signal(getattr(signal, sig)) + time.sleep(self._delayafterterminate) + if self._proc.poll() is not None: + return + except ValueError: + pass # Windows says SIGINT is not a valid signal to send + self._proc.terminate() + time.sleep(self._delayafterterminate) + if self._proc.poll() is not None: + return + self._proc.kill() + time.sleep(self._delayafterterminate) + + def poll(self): + return self._proc.poll() + + def wait(self, timeout=None): + return self._proc.wait(timeout) + + + class _RemoteProcess(_BaseProcess): + + def __init__(self, install_remote): + self._pid = None + self._install_remote = install_remote + + @property + def pid(self): + return self._pid + + def launch(self, executable, args, extra_env): + if self._install_remote: + src_path = executable + dst_path = lldbutil.join_remote_paths( + lldb.remote_platform.GetWorkingDirectory(), os.path.basename(executable)) + + dst_file_spec = lldb.SBFileSpec(dst_path, False) + err = lldb.remote_platform.Install( + lldb.SBFileSpec(src_path, True), dst_file_spec) + if err.Fail(): + raise Exception( + "remote_platform.Install('%s', '%s') failed: %s" % + (src_path, dst_path, err)) + else: + dst_path = executable + dst_file_spec = lldb.SBFileSpec(executable, False) + + launch_info = lldb.SBLaunchInfo(args) + launch_info.SetExecutableFile(dst_file_spec, True) + launch_info.SetWorkingDirectory( + lldb.remote_platform.GetWorkingDirectory()) + + # Redirect stdout and stderr to /dev/null + launch_info.AddSuppressFileAction(1, False, True) + launch_info.AddSuppressFileAction(2, False, True) + + if extra_env: + launch_info.SetEnvironmentEntries(extra_env, True) + + err = lldb.remote_platform.Launch(launch_info) + if err.Fail(): + raise Exception( + "remote_platform.Launch('%s', '%s') failed: %s" % + (dst_path, args, err)) + self._pid = launch_info.GetProcessID() + + def terminate(self): + lldb.remote_platform.Kill(self._pid) + + def getsource_if_available(obj): + """ + Return the text of the source code for an object if available. Otherwise, + a print representation is returned. + """ + import inspect + try: + return inspect.getsource(obj) + except: + return repr(obj) + + + def builder_module(): + return get_builder(sys.platform) + + + class Base(unittest2.TestCase): + """ + Abstract base for performing lldb (see TestBase) or other generic tests (see + BenchBase for one example). lldbtest.Base works with the test driver to + accomplish things. + + """ + + # The concrete subclass should override this attribute. + mydir = None + + # Keep track of the old current working directory. + oldcwd = None + + @staticmethod + def compute_mydir(test_file): + '''Subclasses should call this function to correctly calculate the + required "mydir" attribute as follows: + + mydir = TestBase.compute_mydir(__file__) + ''' + # /abs/path/to/packages/group/subdir/mytest.py -> group/subdir + lldb_test_src = configuration.test_src_root + if not test_file.startswith(lldb_test_src): + raise Exception( + "Test file '%s' must reside within lldb_test_src " + "(which is '%s')." % (test_file, lldb_test_src)) + return os.path.dirname(os.path.relpath(test_file, start=lldb_test_src)) + + def TraceOn(self): + """Returns True if we are in trace mode (tracing detailed test execution).""" + return traceAlways + + def trace(self, *args,**kwargs): + with recording(self, self.TraceOn()) as sbuf: + print(*args, file=sbuf, **kwargs) + + @classmethod + def setUpClass(cls): + """ + Python unittest framework class setup fixture. + Do current directory manipulation. + """ + # Fail fast if 'mydir' attribute is not overridden. + if not cls.mydir or len(cls.mydir) == 0: + raise Exception("Subclasses must override the 'mydir' attribute.") + + # Save old working directory. + cls.oldcwd = os.getcwd() + + full_dir = os.path.join(configuration.test_src_root, cls.mydir) + if traceAlways: + print("Change dir to:", full_dir, file=sys.stderr) + os.chdir(full_dir) + lldb.SBReproducer.SetWorkingDirectory(full_dir) + + # Set platform context. + cls.platformContext = lldbplatformutil.createPlatformContext() + + @classmethod + def tearDownClass(cls): + """ + Python unittest framework class teardown fixture. + Do class-wide cleanup. + """ + + if doCleanup: + # First, let's do the platform-specific cleanup. + module = builder_module() + module.cleanup() + + # Subclass might have specific cleanup function defined. + if getattr(cls, "classCleanup", None): + if traceAlways: + print( + "Call class-specific cleanup function for class:", + cls, + file=sys.stderr) + try: + cls.classCleanup() + except: + exc_type, exc_value, exc_tb = sys.exc_info() + traceback.print_exception(exc_type, exc_value, exc_tb) + + # Restore old working directory. + if traceAlways: + print("Restore dir to:", cls.oldcwd, file=sys.stderr) + os.chdir(cls.oldcwd) + + def enableLogChannelsForCurrentTest(self): + if len(lldbtest_config.channels) == 0: + return + + # if debug channels are specified in lldbtest_config.channels, + # create a new set of log files for every test + log_basename = self.getLogBasenameForCurrentTest() + + # confirm that the file is writeable + host_log_path = "{}-host.log".format(log_basename) + open(host_log_path, 'w').close() + self.log_files.append(host_log_path) + + log_enable = "log enable -Tpn -f {} ".format(host_log_path) + for channel_with_categories in lldbtest_config.channels: + channel_then_categories = channel_with_categories.split(' ', 1) + channel = channel_then_categories[0] + if len(channel_then_categories) > 1: + categories = channel_then_categories[1] + else: + categories = "default" + + if channel == "gdb-remote" and lldb.remote_platform is None: + # communicate gdb-remote categories to debugserver + os.environ["LLDB_DEBUGSERVER_LOG_FLAGS"] = categories + + self.ci.HandleCommand( + log_enable + channel_with_categories, self.res) + if not self.res.Succeeded(): + raise Exception( + 'log enable failed (check LLDB_LOG_OPTION env variable)') + + # Communicate log path name to debugserver & lldb-server + # For remote debugging, these variables need to be set when starting the platform + # instance. + if lldb.remote_platform is None: + server_log_path = "{}-server.log".format(log_basename) + open(server_log_path, 'w').close() + self.log_files.append(server_log_path) + os.environ["LLDB_DEBUGSERVER_LOG_FILE"] = server_log_path + + # Communicate channels to lldb-server + os.environ["LLDB_SERVER_LOG_CHANNELS"] = ":".join( + lldbtest_config.channels) + + self.addTearDownHook(self.disableLogChannelsForCurrentTest) + + def disableLogChannelsForCurrentTest(self): + # close all log files that we opened + for channel_and_categories in lldbtest_config.channels: + # channel format - [ [ ...]] + channel = channel_and_categories.split(' ', 1)[0] + self.ci.HandleCommand("log disable " + channel, self.res) + if not self.res.Succeeded(): + raise Exception( + 'log disable failed (check LLDB_LOG_OPTION env variable)') + + # Retrieve the server log (if any) from the remote system. It is assumed the server log + # is writing to the "server.log" file in the current test directory. This can be + # achieved by setting LLDB_DEBUGSERVER_LOG_FILE="server.log" when starting remote + # platform. + if lldb.remote_platform: + server_log_path = self.getLogBasenameForCurrentTest() + "-server.log" + if lldb.remote_platform.Get( + lldb.SBFileSpec("server.log"), + lldb.SBFileSpec(server_log_path)).Success(): + self.log_files.append(server_log_path) + + def setPlatformWorkingDir(self): + if not lldb.remote_platform or not configuration.lldb_platform_working_dir: + return + + components = self.mydir.split(os.path.sep) + [str(self.test_number), self.getBuildDirBasename()] + remote_test_dir = configuration.lldb_platform_working_dir + for c in components: + remote_test_dir = lldbutil.join_remote_paths(remote_test_dir, c) + error = lldb.remote_platform.MakeDirectory( + remote_test_dir, 448) # 448 = 0o700 + if error.Fail(): + raise Exception("making remote directory '%s': %s" % ( + remote_test_dir, error)) + + lldb.remote_platform.SetWorkingDirectory(remote_test_dir) + + # This function removes all files from the current working directory while leaving + # the directories in place. The cleanup is required to reduce the disk space required + # by the test suite while leaving the directories untouched is neccessary because + # sub-directories might belong to an other test + def clean_working_directory(): + # TODO: Make it working on Windows when we need it for remote debugging support + # TODO: Replace the heuristic to remove the files with a logic what collects the + # list of files we have to remove during test runs. + shell_cmd = lldb.SBPlatformShellCommand( + "rm %s/*" % remote_test_dir) + lldb.remote_platform.Run(shell_cmd) + self.addTearDownHook(clean_working_directory) + + def getSourceDir(self): + """Return the full path to the current test.""" + return os.path.join(configuration.test_src_root, self.mydir) + + def getBuildDirBasename(self): + return self.__class__.__module__ + "." + self.testMethodName + + def getBuildDir(self): + """Return the full path to the current test.""" + return os.path.join(configuration.test_build_dir, self.mydir, + self.getBuildDirBasename()) + + def makeBuildDir(self): + """Create the test-specific working directory, deleting any previous + contents.""" + bdir = self.getBuildDir() + if os.path.isdir(bdir): + shutil.rmtree(bdir) + lldbutil.mkdir_p(bdir) + + def getBuildArtifact(self, name="a.out"): + """Return absolute path to an artifact in the test's build directory.""" + return os.path.join(self.getBuildDir(), name) + + def getSourcePath(self, name): + """Return absolute path to a file in the test's source directory.""" + return os.path.join(self.getSourceDir(), name) + + @classmethod + def setUpCommands(cls): + commands = [ + # First of all, clear all settings to have clean state of global properties. + "settings clear -all", + + # Disable Spotlight lookup. The testsuite creates + # different binaries with the same UUID, because they only + # differ in the debug info, which is not being hashed. + "settings set symbols.enable-external-lookup false", + + # Inherit the TCC permissions from the inferior's parent. + "settings set target.inherit-tcc true", + + # Kill rather than detach from the inferior if something goes wrong. + "settings set target.detach-on-error false", + + # Disable fix-its by default so that incorrect expressions in tests don't + # pass just because Clang thinks it has a fix-it. + "settings set target.auto-apply-fixits false", + + # Testsuite runs in parallel and the host can have also other load. + "settings set plugin.process.gdb-remote.packet-timeout 60", + + 'settings set symbols.clang-modules-cache-path "{}"'.format( + configuration.lldb_module_cache_dir), + "settings set use-color false", + ] + + # Set any user-overridden settings. + for setting, value in configuration.settings: + commands.append('setting set %s %s'%(setting, value)) + + # Make sure that a sanitizer LLDB's environment doesn't get passed on. + if cls.platformContext and cls.platformContext.shlib_environment_var in os.environ: + commands.append('settings set target.env-vars {}='.format( + cls.platformContext.shlib_environment_var)) + + # Set environment variables for the inferior. + if lldbtest_config.inferior_env: + commands.append('settings set target.env-vars {}'.format( + lldbtest_config.inferior_env)) + return commands + + def setUp(self): + """Fixture for unittest test case setup. + + It works with the test driver to conditionally skip tests and does other + initializations.""" + #import traceback + # traceback.print_stack() + + if "LIBCXX_PATH" in os.environ: + self.libcxxPath = os.environ["LIBCXX_PATH"] + else: + self.libcxxPath = None + + if "LLDBVSCODE_EXEC" in os.environ: + self.lldbVSCodeExec = os.environ["LLDBVSCODE_EXEC"] + else: + self.lldbVSCodeExec = None + + self.lldbOption = " ".join( + "-o '" + s + "'" for s in self.setUpCommands()) + + # If we spawn an lldb process for test (via pexpect), do not load the + # init file unless told otherwise. + if os.environ.get("NO_LLDBINIT") != "NO": + self.lldbOption += " --no-lldbinit" + + # Assign the test method name to self.testMethodName. + # + # For an example of the use of this attribute, look at test/types dir. + # There are a bunch of test cases under test/types and we don't want the + # module cacheing subsystem to be confused with executable name "a.out" + # used for all the test cases. + self.testMethodName = self._testMethodName + + # This is for the case of directly spawning 'lldb'/'gdb' and interacting + # with it using pexpect. + self.child = None + self.child_prompt = "(lldb) " + # If the child is interacting with the embedded script interpreter, + # there are two exits required during tear down, first to quit the + # embedded script interpreter and second to quit the lldb command + # interpreter. + self.child_in_script_interpreter = False + + # These are for customized teardown cleanup. + self.dict = None + self.doTearDownCleanup = False + # And in rare cases where there are multiple teardown cleanups. + self.dicts = [] + self.doTearDownCleanups = False + + # List of spawned subproces.Popen objects + self.subprocesses = [] + + # List of log files produced by the current test. + self.log_files = [] + + # Create the build directory. + # The logs are stored in the build directory, so we have to create it + # before creating the first log file. + self.makeBuildDir() + + session_file = self.getLogBasenameForCurrentTest()+".log" + self.log_files.append(session_file) + + # Python 3 doesn't support unbuffered I/O in text mode. Open buffered. + self.session = encoded_file.open(session_file, "utf-8", mode="w") + + # Optimistically set __errored__, __failed__, __expected__ to False + # initially. If the test errored/failed, the session info + # (self.session) is then dumped into a session specific file for + # diagnosis. + self.__cleanup_errored__ = False + self.__errored__ = False + self.__failed__ = False + self.__expected__ = False + # We are also interested in unexpected success. + self.__unexpected__ = False + # And skipped tests. + self.__skipped__ = False + + # See addTearDownHook(self, hook) which allows the client to add a hook + # function to be run during tearDown() time. + self.hooks = [] + + # See HideStdout(self). + self.sys_stdout_hidden = False + + if self.platformContext: + # set environment variable names for finding shared libraries + self.dylibPath = self.platformContext.shlib_environment_var + + # Create the debugger instance. + self.dbg = lldb.SBDebugger.Create() + # Copy selected platform from a global instance if it exists. + if lldb.selected_platform is not None: + self.dbg.SetSelectedPlatform(lldb.selected_platform) + + if not self.dbg: + raise Exception('Invalid debugger instance') + + # Retrieve the associated command interpreter instance. + self.ci = self.dbg.GetCommandInterpreter() + if not self.ci: + raise Exception('Could not get the command interpreter') + + # And the result object. + self.res = lldb.SBCommandReturnObject() + + self.setPlatformWorkingDir() + self.enableLogChannelsForCurrentTest() + + self.lib_lldb = None + self.framework_dir = None + self.darwinWithFramework = False + + if sys.platform.startswith("darwin") and configuration.lldb_framework_path: + framework = configuration.lldb_framework_path + lib = os.path.join(framework, 'LLDB') + if os.path.exists(lib): + self.framework_dir = os.path.dirname(framework) + self.lib_lldb = lib + self.darwinWithFramework = self.platformIsDarwin() + + def setAsync(self, value): + """ Sets async mode to True/False and ensures it is reset after the testcase completes.""" + old_async = self.dbg.GetAsync() + self.dbg.SetAsync(value) + self.addTearDownHook(lambda: self.dbg.SetAsync(old_async)) + + def cleanupSubprocesses(self): + # Terminate subprocesses in reverse order from how they were created. + for p in reversed(self.subprocesses): + p.terminate() + del p + del self.subprocesses[:] + + def spawnSubprocess(self, executable, args=[], extra_env=None, install_remote=True): + """ Creates a subprocess.Popen object with the specified executable and arguments, + saves it in self.subprocesses, and returns the object. + """ + proc = _RemoteProcess( + install_remote) if lldb.remote_platform else _LocalProcess(self.TraceOn()) + proc.launch(executable, args, extra_env=extra_env) + self.subprocesses.append(proc) + return proc + + def HideStdout(self): + """Hide output to stdout from the user. + + During test execution, there might be cases where we don't want to show the + standard output to the user. For example, + + self.runCmd(r'''sc print("\n\n\tHello!\n")''') + + tests whether command abbreviation for 'script' works or not. There is no + need to show the 'Hello' output to the user as long as the 'script' command + succeeds and we are not in TraceOn() mode (see the '-t' option). + + In this case, the test method calls self.HideStdout(self) to redirect the + sys.stdout to a null device, and restores the sys.stdout upon teardown. + + Note that you should only call this method at most once during a test case + execution. Any subsequent call has no effect at all.""" + if self.sys_stdout_hidden: + return + + self.sys_stdout_hidden = True + old_stdout = sys.stdout + sys.stdout = open(os.devnull, 'w') + + def restore_stdout(): + sys.stdout = old_stdout + self.addTearDownHook(restore_stdout) + + # ======================================================================= + # Methods for customized teardown cleanups as well as execution of hooks. + # ======================================================================= + + def setTearDownCleanup(self, dictionary=None): + """Register a cleanup action at tearDown() time with a dictionary""" + self.dict = dictionary + self.doTearDownCleanup = True + + def addTearDownCleanup(self, dictionary): + """Add a cleanup action at tearDown() time with a dictionary""" + self.dicts.append(dictionary) + self.doTearDownCleanups = True + + def addTearDownHook(self, hook): + """ + Add a function to be run during tearDown() time. + + Hooks are executed in a first come first serve manner. + """ + if six.callable(hook): + with recording(self, traceAlways) as sbuf: + print( + "Adding tearDown hook:", + getsource_if_available(hook), + file=sbuf) + self.hooks.append(hook) + + return self + + def deletePexpectChild(self): + # This is for the case of directly spawning 'lldb' and interacting with it + # using pexpect. + if self.child and self.child.isalive(): + import pexpect + with recording(self, traceAlways) as sbuf: + print("tearing down the child process....", file=sbuf) + try: + if self.child_in_script_interpreter: + self.child.sendline('quit()') + self.child.expect_exact(self.child_prompt) + self.child.sendline( + 'settings set interpreter.prompt-on-quit false') + self.child.sendline('quit') + self.child.expect(pexpect.EOF) + except (ValueError, pexpect.ExceptionPexpect): + # child is already terminated + pass + except OSError as exception: + import errno + if exception.errno != errno.EIO: + # unexpected error + raise + # child is already terminated + finally: + # Give it one final blow to make sure the child is terminated. + self.child.close() + + def tearDown(self): + """Fixture for unittest test case teardown.""" + self.deletePexpectChild() + + # Check and run any hook functions. + for hook in reversed(self.hooks): + with recording(self, traceAlways) as sbuf: + print( + "Executing tearDown hook:", + getsource_if_available(hook), + file=sbuf) + if funcutils.requires_self(hook): + hook(self) + else: + hook() # try the plain call and hope it works + + del self.hooks + + # Perform registered teardown cleanup. + if doCleanup and self.doTearDownCleanup: + self.cleanup(dictionary=self.dict) + + # In rare cases where there are multiple teardown cleanups added. + if doCleanup and self.doTearDownCleanups: + if self.dicts: + for dict in reversed(self.dicts): + self.cleanup(dictionary=dict) + + # Remove subprocesses created by the test. + self.cleanupSubprocesses() + + # This must be the last statement, otherwise teardown hooks or other + # lines might depend on this still being active. + lldb.SBDebugger.Destroy(self.dbg) + del self.dbg + + # All modules should be orphaned now so that they can be cleared from + # the shared module cache. + lldb.SBModule.GarbageCollectAllocatedModules() + + # Assert that the global module cache is empty. + self.assertEqual(lldb.SBModule.GetNumberAllocatedModules(), 0) + + + # ========================================================= + # Various callbacks to allow introspection of test progress + # ========================================================= + + def markError(self): + """Callback invoked when an error (unexpected exception) errored.""" + self.__errored__ = True + with recording(self, False) as sbuf: + # False because there's no need to write "ERROR" to the stderr twice. + # Once by the Python unittest framework, and a second time by us. + print("ERROR", file=sbuf) + + def markCleanupError(self): + """Callback invoked when an error occurs while a test is cleaning up.""" + self.__cleanup_errored__ = True + with recording(self, False) as sbuf: + # False because there's no need to write "CLEANUP_ERROR" to the stderr twice. + # Once by the Python unittest framework, and a second time by us. + print("CLEANUP_ERROR", file=sbuf) + + def markFailure(self): + """Callback invoked when a failure (test assertion failure) occurred.""" + self.__failed__ = True + with recording(self, False) as sbuf: + # False because there's no need to write "FAIL" to the stderr twice. + # Once by the Python unittest framework, and a second time by us. + print("FAIL", file=sbuf) + + def markExpectedFailure(self, err, bugnumber): + """Callback invoked when an expected failure/error occurred.""" + self.__expected__ = True + with recording(self, False) as sbuf: + # False because there's no need to write "expected failure" to the + # stderr twice. + # Once by the Python unittest framework, and a second time by us. + if bugnumber is None: + print("expected failure", file=sbuf) + else: + print( + "expected failure (problem id:" + str(bugnumber) + ")", + file=sbuf) + + def markSkippedTest(self): + """Callback invoked when a test is skipped.""" + self.__skipped__ = True + with recording(self, False) as sbuf: + # False because there's no need to write "skipped test" to the + # stderr twice. + # Once by the Python unittest framework, and a second time by us. + print("skipped test", file=sbuf) + + def markUnexpectedSuccess(self, bugnumber): + """Callback invoked when an unexpected success occurred.""" + self.__unexpected__ = True + with recording(self, False) as sbuf: + # False because there's no need to write "unexpected success" to the + # stderr twice. + # Once by the Python unittest framework, and a second time by us. + if bugnumber is None: + print("unexpected success", file=sbuf) + else: + print( + "unexpected success (problem id:" + str(bugnumber) + ")", + file=sbuf) + + def getRerunArgs(self): + return " -f %s.%s" % (self.__class__.__name__, self._testMethodName) + + def getLogBasenameForCurrentTest(self, prefix="Incomplete"): + """ + returns a partial path that can be used as the beginning of the name of multiple + log files pertaining to this test + """ + return os.path.join(self.getBuildDir(), prefix) + + def dumpSessionInfo(self): + """ + Dump the debugger interactions leading to a test error/failure. This + allows for more convenient postmortem analysis. + + See also LLDBTestResult (dotest.py) which is a singlton class derived + from TextTestResult and overwrites addError, addFailure, and + addExpectedFailure methods to allow us to to mark the test instance as + such. + """ + + # We are here because self.tearDown() detected that this test instance + # either errored or failed. The lldb.test_result singleton contains + # two lists (errors and failures) which get populated by the unittest + # framework. Look over there for stack trace information. + # + # The lists contain 2-tuples of TestCase instances and strings holding + # formatted tracebacks. + # + # See http://docs.python.org/library/unittest.html#unittest.TestResult. + + # output tracebacks into session + pairs = [] + if self.__errored__: + pairs = configuration.test_result.errors + prefix = 'Error' + elif self.__cleanup_errored__: + pairs = configuration.test_result.cleanup_errors + prefix = 'CleanupError' + elif self.__failed__: + pairs = configuration.test_result.failures + prefix = 'Failure' + elif self.__expected__: + pairs = configuration.test_result.expectedFailures + prefix = 'ExpectedFailure' + elif self.__skipped__: + prefix = 'SkippedTest' + elif self.__unexpected__: + prefix = 'UnexpectedSuccess' + else: + prefix = 'Success' + + if not self.__unexpected__ and not self.__skipped__: + for test, traceback in pairs: + if test is self: + print(traceback, file=self.session) + + import datetime + print( + "Session info generated @", + datetime.datetime.now().ctime(), + file=self.session) + self.session.close() + del self.session + + # process the log files + if prefix != 'Success' or lldbtest_config.log_success: + # keep all log files, rename them to include prefix + src_log_basename = self.getLogBasenameForCurrentTest() + dst_log_basename = self.getLogBasenameForCurrentTest(prefix) + for src in self.log_files: + if os.path.isfile(src): + dst = src.replace(src_log_basename, dst_log_basename) + if os.name == "nt" and os.path.isfile(dst): + # On Windows, renaming a -> b will throw an exception if + # b exists. On non-Windows platforms it silently + # replaces the destination. Ultimately this means that + # atomic renames are not guaranteed to be possible on + # Windows, but we need this to work anyway, so just + # remove the destination first if it already exists. + remove_file(dst) + + lldbutil.mkdir_p(os.path.dirname(dst)) + os.rename(src, dst) + else: + # success! (and we don't want log files) delete log files + for log_file in self.log_files: + if os.path.isfile(log_file): + remove_file(log_file) + + # ==================================================== + # Config. methods supported through a plugin interface + # (enables reading of the current test configuration) + # ==================================================== + + def isMIPS(self): + """Returns true if the architecture is MIPS.""" + arch = self.getArchitecture() + if re.match("mips", arch): + return True + return False + + def isPPC64le(self): + """Returns true if the architecture is PPC64LE.""" + arch = self.getArchitecture() + if re.match("powerpc64le", arch): + return True + return False + + def getCPUInfo(self): + triple = self.dbg.GetSelectedPlatform().GetTriple() + + # TODO other platforms, please implement this function + if not re.match(".*-.*-linux", triple): + return "" + + # Need to do something different for non-Linux/Android targets + cpuinfo_path = self.getBuildArtifact("cpuinfo") + if configuration.lldb_platform_name: + self.runCmd('platform get-file "/proc/cpuinfo" ' + cpuinfo_path) + else: + cpuinfo_path = "/proc/cpuinfo" + + try: + with open(cpuinfo_path, 'r') as f: + cpuinfo = f.read() + except: + return "" + + return cpuinfo + + def isAArch64(self): + """Returns true if the architecture is AArch64.""" + arch = self.getArchitecture().lower() + return arch in ["aarch64", "arm64", "arm64e"] + + def isAArch64SVE(self): + return self.isAArch64() and "sve" in self.getCPUInfo() + + def isAArch64MTE(self): + return self.isAArch64() and "mte" in self.getCPUInfo() + + def isAArch64PAuth(self): + return self.isAArch64() and "paca" in self.getCPUInfo() + + def getArchitecture(self): + """Returns the architecture in effect the test suite is running with.""" + module = builder_module() + arch = module.getArchitecture() + if arch == 'amd64': + arch = 'x86_64' + if arch in ['armv7l', 'armv8l'] : + arch = 'arm' + return arch + + def getLldbArchitecture(self): + """Returns the architecture of the lldb binary.""" + if not hasattr(self, 'lldbArchitecture'): + + # spawn local process + command = [ + lldbtest_config.lldbExec, + "-o", + "file " + lldbtest_config.lldbExec, + "-o", + "quit" + ] + + output = check_output(command) + str = output.decode("utf-8") + + for line in str.splitlines(): + m = re.search( + "Current executable set to '.*' \\((.*)\\)\\.", line) + if m: + self.lldbArchitecture = m.group(1) + break + + return self.lldbArchitecture + + def getCompiler(self): + """Returns the compiler in effect the test suite is running with.""" + module = builder_module() + return module.getCompiler() + + def getCompilerBinary(self): + """Returns the compiler binary the test suite is running with.""" + return self.getCompiler().split()[0] + + def getCompilerVersion(self): + """ Returns a string that represents the compiler version. + Supports: llvm, clang. + """ + compiler = self.getCompilerBinary() + version_output = check_output([compiler, "--version"], errors="replace") + m = re.search('version ([0-9.]+)', version_output) + if m: + return m.group(1) + return 'unknown' + + def getDwarfVersion(self): + """ Returns the dwarf version generated by clang or '0'. """ + if configuration.dwarf_version: + return str(configuration.dwarf_version) + if 'clang' in self.getCompiler(): + try: + triple = builder_module().getTriple(self.getArchitecture()) + target = ['-target', triple] if triple else [] + driver_output = check_output( + [self.getCompiler()] + target + '-g -c -x c - -o - -###'.split(), + stderr=STDOUT) + driver_output = driver_output.decode("utf-8") + for line in driver_output.split(os.linesep): + m = re.search('dwarf-version=([0-9])', line) + if m: + return m.group(1) + except CalledProcessError: + pass + return '0' + + def platformIsDarwin(self): + """Returns true if the OS triple for the selected platform is any valid apple OS""" + return lldbplatformutil.platformIsDarwin() + + def hasDarwinFramework(self): + return self.darwinWithFramework + + def getPlatform(self): + """Returns the target platform the test suite is running on.""" + return lldbplatformutil.getPlatform() + + def isIntelCompiler(self): + """ Returns true if using an Intel (ICC) compiler, false otherwise. """ + return any([x in self.getCompiler() for x in ["icc", "icpc", "icl"]]) + + def expectedCompilerVersion(self, compiler_version): + """Returns True iff compiler_version[1] matches the current compiler version. + Use compiler_version[0] to specify the operator used to determine if a match has occurred. + Any operator other than the following defaults to an equality test: + '>', '>=', "=>", '<', '<=', '=<', '!=', "!" or 'not' + + If the current compiler version cannot be determined, we assume it is close to the top + of trunk, so any less-than or equal-to comparisons will return False, and any + greater-than or not-equal-to comparisons will return True. + """ + if compiler_version is None: + return True + operator = str(compiler_version[0]) + version = compiler_version[1] + + if version is None: + return True + + test_compiler_version = self.getCompilerVersion() + if test_compiler_version == 'unknown': + # Assume the compiler version is at or near the top of trunk. + return operator in ['>', '>=', '!', '!=', 'not'] + + if operator == '>': + return LooseVersion(test_compiler_version) > LooseVersion(version) + if operator == '>=' or operator == '=>': + return LooseVersion(test_compiler_version) >= LooseVersion(version) + if operator == '<': + return LooseVersion(test_compiler_version) < LooseVersion(version) + if operator == '<=' or operator == '=<': + return LooseVersion(test_compiler_version) <= LooseVersion(version) + if operator == '!=' or operator == '!' or operator == 'not': + return str(version) not in str(test_compiler_version) + return str(version) in str(test_compiler_version) + + def expectedCompiler(self, compilers): + """Returns True iff any element of compilers is a sub-string of the current compiler.""" + if (compilers is None): + return True + + for compiler in compilers: + if compiler in self.getCompiler(): + return True + + return False + + def expectedArch(self, archs): + """Returns True iff any element of archs is a sub-string of the current architecture.""" + if (archs is None): + return True + + for arch in archs: + if arch in self.getArchitecture(): + return True + + return False + + def getRunOptions(self): + """Command line option for -A and -C to run this test again, called from + self.dumpSessionInfo().""" + arch = self.getArchitecture() + comp = self.getCompiler() + option_str = "" + if arch: + option_str = "-A " + arch + if comp: + option_str += " -C " + comp + return option_str + + def getDebugInfo(self): + method = getattr(self, self.testMethodName) + return getattr(method, "debug_info", None) + + def build( + self, + debug_info=None, + architecture=None, + compiler=None, + dictionary=None): + """Platform specific way to build binaries.""" + if not architecture and configuration.arch: + architecture = configuration.arch + + if debug_info is None: + debug_info = self.getDebugInfo() + + dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) + + testdir = self.mydir + testname = self.getBuildDirBasename() + + module = builder_module() + command = builder_module().getBuildCommand(debug_info, architecture, + compiler, dictionary, testdir, testname) + if command is None: + raise Exception("Don't know how to build binary") + + self.runBuildCommand(command) + + def runBuildCommand(self, command): + self.trace(seven.join_for_shell(command)) + try: + output = check_output(command, stderr=STDOUT, errors="replace") + except CalledProcessError as cpe: + raise build_exception.BuildError(cpe) + self.trace(output) + + + # ================================================== + # Build methods supported through a plugin interface + # ================================================== + + def getstdlibFlag(self): + """ Returns the proper -stdlib flag, or empty if not required.""" + if self.platformIsDarwin() or self.getPlatform() == "freebsd" or self.getPlatform() == "openbsd": + stdlibflag = "-stdlib=libc++" + else: # this includes NetBSD + stdlibflag = "" + return stdlibflag + + def getstdFlag(self): + """ Returns the proper stdflag. """ + if "gcc" in self.getCompiler() and "4.6" in self.getCompilerVersion(): + stdflag = "-std=c++0x" + else: + stdflag = "-std=c++11" + return stdflag + + def buildDriver(self, sources, exe_name): + """ Platform-specific way to build a program that links with LLDB (via the liblldb.so + or LLDB.framework). + """ + stdflag = self.getstdFlag() + stdlibflag = self.getstdlibFlag() + + lib_dir = configuration.lldb_libs_dir + if self.hasDarwinFramework(): + d = {'CXX_SOURCES': sources, + 'EXE': exe_name, + 'CFLAGS_EXTRAS': "%s %s" % (stdflag, stdlibflag), + 'FRAMEWORK_INCLUDES': "-F%s" % self.framework_dir, + 'LD_EXTRAS': "%s -Wl,-rpath,%s" % (self.lib_lldb, self.framework_dir), + } + elif sys.platform.startswith('win'): + d = { + 'CXX_SOURCES': sources, + 'EXE': exe_name, + 'CFLAGS_EXTRAS': "%s %s -I%s" % (stdflag, + stdlibflag, + os.path.join( + os.environ["LLDB_SRC"], + "include")), + 'LD_EXTRAS': "-L%s -lliblldb" % lib_dir} + else: + d = { + 'CXX_SOURCES': sources, + 'EXE': exe_name, + 'CFLAGS_EXTRAS': "%s %s -I%s" % (stdflag, + stdlibflag, + os.path.join( + os.environ["LLDB_SRC"], + "include")), + 'LD_EXTRAS': "-L%s -llldb -Wl,-rpath,%s" % (lib_dir, lib_dir)} + if self.TraceOn(): + print( + "Building LLDB Driver (%s) from sources %s" % + (exe_name, sources)) + + self.build(dictionary=d) + + def buildLibrary(self, sources, lib_name): + """Platform specific way to build a default library. """ + + stdflag = self.getstdFlag() + + lib_dir = configuration.lldb_libs_dir + if self.hasDarwinFramework(): + d = {'DYLIB_CXX_SOURCES': sources, + 'DYLIB_NAME': lib_name, + 'CFLAGS_EXTRAS': "%s -stdlib=libc++" % stdflag, + 'FRAMEWORK_INCLUDES': "-F%s" % self.framework_dir, + 'LD_EXTRAS': "%s -Wl,-rpath,%s -dynamiclib" % (self.lib_lldb, self.framework_dir), + } + elif self.getPlatform() == 'windows': + d = { + 'DYLIB_CXX_SOURCES': sources, + 'DYLIB_NAME': lib_name, + 'CFLAGS_EXTRAS': "%s -I%s " % (stdflag, + os.path.join( + os.environ["LLDB_SRC"], + "include")), + 'LD_EXTRAS': "-shared -l%s\liblldb.lib" % lib_dir} + else: + d = { + 'DYLIB_CXX_SOURCES': sources, + 'DYLIB_NAME': lib_name, + 'CFLAGS_EXTRAS': "%s -I%s -fPIC" % (stdflag, + os.path.join( + os.environ["LLDB_SRC"], + "include")), + 'LD_EXTRAS': "-shared -L%s -llldb -Wl,-rpath,%s" % (lib_dir, lib_dir)} + if self.TraceOn(): + print( + "Building LLDB Library (%s) from sources %s" % + (lib_name, sources)) + + self.build(dictionary=d) + + def buildProgram(self, sources, exe_name): + """ Platform specific way to build an executable from C/C++ sources. """ + d = {'CXX_SOURCES': sources, + 'EXE': exe_name} + self.build(dictionary=d) + + def signBinary(self, binary_path): + if sys.platform.startswith("darwin"): + codesign_cmd = "codesign --force --sign \"%s\" %s" % ( + lldbtest_config.codesign_identity, binary_path) + call(codesign_cmd, shell=True) + + def findBuiltClang(self): + """Tries to find and use Clang from the build directory as the compiler (instead of the system compiler).""" + paths_to_try = [ + "llvm-build/Release+Asserts/x86_64/bin/clang", + "llvm-build/Debug+Asserts/x86_64/bin/clang", + "llvm-build/Release/x86_64/bin/clang", + "llvm-build/Debug/x86_64/bin/clang", + ] + lldb_root_path = os.path.join( + os.path.dirname(__file__), "..", "..", "..", "..") + for p in paths_to_try: + path = os.path.join(lldb_root_path, p) + if os.path.exists(path): + return path + + # Tries to find clang at the same folder as the lldb + lldb_dir = os.path.dirname(lldbtest_config.lldbExec) + path = distutils.spawn.find_executable("clang", lldb_dir) + if path is not None: + return path + + return os.environ["CC"] + + + def yaml2obj(self, yaml_path, obj_path, max_size=None): + """ + Create an object file at the given path from a yaml file. + + Throws subprocess.CalledProcessError if the object could not be created. + """ + yaml2obj_bin = configuration.get_yaml2obj_path() + if not yaml2obj_bin: + self.assertTrue(False, "No valid yaml2obj executable specified") + command = [yaml2obj_bin, "-o=%s" % obj_path, yaml_path] + if max_size is not None: + command += ["--max-size=%d" % max_size] + self.runBuildCommand(command) + + def cleanup(self, dictionary=None): + """Platform specific way to do cleanup after build.""" + module = builder_module() + if not module.cleanup(dictionary): + raise Exception( + "Don't know how to do cleanup with dictionary: " + + dictionary) + + def invoke(self, obj, name, trace=False): + """Use reflection to call a method dynamically with no argument.""" + trace = (True if traceAlways else trace) + + method = getattr(obj, name) + import inspect + self.assertTrue(inspect.ismethod(method), + name + "is a method name of object: " + str(obj)) + result = method() + with recording(self, trace) as sbuf: + print(str(method) + ":", result, file=sbuf) + return result + + def getLLDBLibraryEnvVal(self): + """ Returns the path that the OS-specific library search environment variable + (self.dylibPath) should be set to in order for a program to find the LLDB + library. If an environment variable named self.dylibPath is already set, + the new path is appended to it and returned. + """ + existing_library_path = os.environ[ + self.dylibPath] if self.dylibPath in os.environ else None + if existing_library_path: + return "%s:%s" % (existing_library_path, configuration.lldb_libs_dir) + if sys.platform.startswith("darwin") and configuration.lldb_framework_path: + return configuration.lldb_framework_path + return configuration.lldb_libs_dir + + def getLibcPlusPlusLibs(self): + if self.getPlatform() in ('freebsd', 'linux', 'netbsd', 'openbsd'): + return ['libc++.so.1'] + else: + return ['libc++.1.dylib', 'libc++abi.'] + + def run_platform_command(self, cmd): + platform = self.dbg.GetSelectedPlatform() + shell_command = lldb.SBPlatformShellCommand(cmd) + err = platform.Run(shell_command) + return (err, shell_command.GetStatus(), shell_command.GetOutput()) + + # Metaclass for TestBase to change the list of test metods when a new TestCase is loaded. + # We change the test methods to create a new test method for each test for each debug info we are + # testing. The name of the new test method will be '_' and with adding + # the new test method we remove the old method at the same time. This functionality can be + # supressed by at test case level setting the class attribute NO_DEBUG_INFO_TESTCASE or at test + # level by using the decorator @no_debug_info_test. + + + class LLDBTestCaseFactory(type): + + def __new__(cls, name, bases, attrs): + original_testcase = super( + LLDBTestCaseFactory, cls).__new__( + cls, name, bases, attrs) + if original_testcase.NO_DEBUG_INFO_TESTCASE: + return original_testcase + + newattrs = {} + for attrname, attrvalue in attrs.items(): + if attrname.startswith("test") and not getattr( + attrvalue, "__no_debug_info_test__", False): + + # If any debug info categories were explicitly tagged, assume that list to be + # authoritative. If none were specified, try with all debug + # info formats. + all_dbginfo_categories = set(test_categories.debug_info_categories) + categories = set( + getattr( + attrvalue, + "categories", + [])) & all_dbginfo_categories + if not categories: + categories = all_dbginfo_categories + + for cat in categories: + @decorators.add_test_categories([cat]) + @wraps(attrvalue) + def test_method(self, attrvalue=attrvalue): + return attrvalue(self) + + method_name = attrname + "_" + cat + test_method.__name__ = method_name + test_method.debug_info = cat + newattrs[method_name] = test_method + + else: + newattrs[attrname] = attrvalue + return super( + LLDBTestCaseFactory, + cls).__new__( + cls, + name, + bases, + newattrs) + + # Setup the metaclass for this class to change the list of the test + # methods when a new class is loaded + + + @add_metaclass(LLDBTestCaseFactory) + class TestBase(Base): + """ + This abstract base class is meant to be subclassed. It provides default + implementations for setUpClass(), tearDownClass(), setUp(), and tearDown(), + among other things. + + Important things for test class writers: + + - Overwrite the mydir class attribute, otherwise your test class won't + run. It specifies the relative directory to the top level 'test' so + the test harness can change to the correct working directory before + running your test. + + - The setUp method sets up things to facilitate subsequent interactions + with the debugger as part of the test. These include: + - populate the test method name + - create/get a debugger set with synchronous mode (self.dbg) + - get the command interpreter from with the debugger (self.ci) + - create a result object for use with the command interpreter + (self.res) + - plus other stuffs + + - The tearDown method tries to perform some necessary cleanup on behalf + of the test to return the debugger to a good state for the next test. + These include: + - execute any tearDown hooks registered by the test method with + TestBase.addTearDownHook(); examples can be found in + settings/TestSettings.py + - kill the inferior process associated with each target, if any, + and, then delete the target from the debugger's target list + - perform build cleanup before running the next test method in the + same test class; examples of registering for this service can be + found in types/TestIntegerTypes.py with the call: + - self.setTearDownCleanup(dictionary=d) + + - Similarly setUpClass and tearDownClass perform classwise setup and + teardown fixtures. The tearDownClass method invokes a default build + cleanup for the entire test class; also, subclasses can implement the + classmethod classCleanup(cls) to perform special class cleanup action. + + - The instance methods runCmd and expect are used heavily by existing + test cases to send a command to the command interpreter and to perform + string/pattern matching on the output of such command execution. The + expect method also provides a mode to peform string/pattern matching + without running a command. + + - The build method is used to build the binaries used during a + particular test scenario. A plugin should be provided for the + sys.platform running the test suite. The Mac OS X implementation is + located in builders/darwin.py. + """ + + # Subclasses can set this to true (if they don't depend on debug info) to avoid running the + # test multiple times with various debug info types. + NO_DEBUG_INFO_TESTCASE = False + + # Maximum allowed attempts when launching the inferior process. + # Can be overridden by the LLDB_MAX_LAUNCH_COUNT environment variable. + maxLaunchCount = 1 + + # Time to wait before the next launching attempt in second(s). + # Can be overridden by the LLDB_TIME_WAIT_NEXT_LAUNCH environment variable. + timeWaitNextLaunch = 1.0 + + def generateSource(self, source): + template = source + '.template' + temp = os.path.join(self.getSourceDir(), template) + with open(temp, 'r') as f: + content = f.read() + + public_api_dir = os.path.join( + os.environ["LLDB_SRC"], "include", "lldb", "API") + + # Look under the include/lldb/API directory and add #include statements + # for all the SB API headers. + public_headers = os.listdir(public_api_dir) + # For different platforms, the include statement can vary. + if self.hasDarwinFramework(): + include_stmt = "'#include <%s>' % os.path.join('LLDB', header)" + else: + include_stmt = "'#include <%s>' % os.path.join('" + public_api_dir + "', header)" + list = [eval(include_stmt) for header in public_headers if ( + header.startswith("SB") and header.endswith(".h"))] + includes = '\n'.join(list) + new_content = content.replace('%include_SB_APIs%', includes) + new_content = new_content.replace('%SOURCE_DIR%', self.getSourceDir()) + src = os.path.join(self.getBuildDir(), source) + with open(src, 'w') as f: + f.write(new_content) + + self.addTearDownHook(lambda: os.remove(src)) + + def setUp(self): + # Works with the test driver to conditionally skip tests via + # decorators. + Base.setUp(self) + + for s in self.setUpCommands(): + self.runCmd(s) + + if "LLDB_MAX_LAUNCH_COUNT" in os.environ: + self.maxLaunchCount = int(os.environ["LLDB_MAX_LAUNCH_COUNT"]) + + if "LLDB_TIME_WAIT_NEXT_LAUNCH" in os.environ: + self.timeWaitNextLaunch = float( + os.environ["LLDB_TIME_WAIT_NEXT_LAUNCH"]) + + # We want our debugger to be synchronous. + self.dbg.SetAsync(False) + + # Retrieve the associated command interpreter instance. + self.ci = self.dbg.GetCommandInterpreter() + if not self.ci: + raise Exception('Could not get the command interpreter') + + # And the result object. + self.res = lldb.SBCommandReturnObject() + + def registerSharedLibrariesWithTarget(self, target, shlibs): + '''If we are remotely running the test suite, register the shared libraries with the target so they get uploaded, otherwise do nothing + + Any modules in the target that have their remote install file specification set will + get uploaded to the remote host. This function registers the local copies of the + shared libraries with the target and sets their remote install locations so they will + be uploaded when the target is run. + ''' + if not shlibs or not self.platformContext: + return None + + shlib_environment_var = self.platformContext.shlib_environment_var + shlib_prefix = self.platformContext.shlib_prefix + shlib_extension = '.' + self.platformContext.shlib_extension + + dirs = [] + # Add any shared libraries to our target if remote so they get + # uploaded into the working directory on the remote side + for name in shlibs: + # The path can be a full path to a shared library, or a make file name like "Foo" for + # "libFoo.dylib" or "libFoo.so", or "Foo.so" for "Foo.so" or "libFoo.so", or just a + # basename like "libFoo.so". So figure out which one it is and resolve the local copy + # of the shared library accordingly + if os.path.isfile(name): + local_shlib_path = name # name is the full path to the local shared library + else: + # Check relative names + local_shlib_path = os.path.join( + self.getBuildDir(), shlib_prefix + name + shlib_extension) + if not os.path.exists(local_shlib_path): + local_shlib_path = os.path.join( + self.getBuildDir(), name + shlib_extension) + if not os.path.exists(local_shlib_path): + local_shlib_path = os.path.join(self.getBuildDir(), name) + + # Make sure we found the local shared library in the above code + self.assertTrue(os.path.exists(local_shlib_path)) + + + # Add the shared library to our target + shlib_module = target.AddModule(local_shlib_path, None, None, None) + if lldb.remote_platform: + # We must set the remote install location if we want the shared library + # to get uploaded to the remote target + remote_shlib_path = lldbutil.append_to_process_working_directory(self, + os.path.basename(local_shlib_path)) + shlib_module.SetRemoteInstallFileSpec( + lldb.SBFileSpec(remote_shlib_path, False)) + dir_to_add = self.get_process_working_directory() + else: + dir_to_add = os.path.dirname(local_shlib_path) + + if dir_to_add not in dirs: + dirs.append(dir_to_add) + + env_value = self.platformContext.shlib_path_separator.join(dirs) + return ['%s=%s' % (shlib_environment_var, env_value)] + + def registerSanitizerLibrariesWithTarget(self, target): + runtimes = [] + for m in target.module_iter(): + libspec = m.GetFileSpec() + if "clang_rt" in libspec.GetFilename(): + runtimes.append(os.path.join(libspec.GetDirectory(), + libspec.GetFilename())) + return self.registerSharedLibrariesWithTarget(target, runtimes) + + # utility methods that tests can use to access the current objects + def target(self): + if not self.dbg: + raise Exception('Invalid debugger instance') + return self.dbg.GetSelectedTarget() + + def process(self): + if not self.dbg: + raise Exception('Invalid debugger instance') + return self.dbg.GetSelectedTarget().GetProcess() + + def thread(self): + if not self.dbg: + raise Exception('Invalid debugger instance') + return self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread() + + def frame(self): + if not self.dbg: + raise Exception('Invalid debugger instance') + return self.dbg.GetSelectedTarget().GetProcess( + ).GetSelectedThread().GetSelectedFrame() + + def get_process_working_directory(self): + '''Get the working directory that should be used when launching processes for local or remote processes.''' + if lldb.remote_platform: + # Remote tests set the platform working directory up in + # TestBase.setUp() + return lldb.remote_platform.GetWorkingDirectory() + else: + # local tests change directory into each test subdirectory + return self.getBuildDir() + + def tearDown(self): + # Ensure all the references to SB objects have gone away so that we can + # be sure that all test-specific resources have been freed before we + # attempt to delete the targets. + gc.collect() + + # Delete the target(s) from the debugger as a general cleanup step. + # This includes terminating the process for each target, if any. + # We'd like to reuse the debugger for our next test without incurring + # the initialization overhead. + targets = [] + for target in self.dbg: + if target: + targets.append(target) + process = target.GetProcess() + if process: + rc = self.invoke(process, "Kill") + assert rc.Success() + for target in targets: + self.dbg.DeleteTarget(target) + + # Assert that all targets are deleted. + self.assertEqual(self.dbg.GetNumTargets(), 0) + + # Do this last, to make sure it's in reverse order from how we setup. + Base.tearDown(self) + + def switch_to_thread_with_stop_reason(self, stop_reason): + """ + Run the 'thread list' command, and select the thread with stop reason as + 'stop_reason'. If no such thread exists, no select action is done. + """ + from .lldbutil import stop_reason_to_str + self.runCmd('thread list') + output = self.res.GetOutput() + thread_line_pattern = re.compile( + "^[ *] thread #([0-9]+):.*stop reason = %s" % + stop_reason_to_str(stop_reason)) + for line in output.splitlines(): + matched = thread_line_pattern.match(line) + if matched: + self.runCmd('thread select %s' % matched.group(1)) + + def runCmd(self, cmd, msg=None, check=True, trace=False, inHistory=False): + """ + Ask the command interpreter to handle the command and then check its + return status. + """ + # Fail fast if 'cmd' is not meaningful. + if cmd is None: + raise Exception("Bad 'cmd' parameter encountered") + + trace = (True if traceAlways else trace) + + if cmd.startswith("target create "): + cmd = cmd.replace("target create ", "file ") + + running = (cmd.startswith("run") or cmd.startswith("process launch")) + + for i in range(self.maxLaunchCount if running else 1): + self.ci.HandleCommand(cmd, self.res, inHistory) + + with recording(self, trace) as sbuf: + print("runCmd:", cmd, file=sbuf) + if not check: + print("check of return status not required", file=sbuf) + if self.res.Succeeded(): + print("output:", self.res.GetOutput(), file=sbuf) + else: + print("runCmd failed!", file=sbuf) + print(self.res.GetError(), file=sbuf) + + if self.res.Succeeded(): + break + elif running: + # For process launch, wait some time before possible next try. + time.sleep(self.timeWaitNextLaunch) + with recording(self, trace) as sbuf: + print("Command '" + cmd + "' failed!", file=sbuf) + + if check: + output = "" + if self.res.GetOutput(): + output += "\nCommand output:\n" + self.res.GetOutput() + if self.res.GetError(): + output += "\nError output:\n" + self.res.GetError() + if msg: + msg += output + if cmd: + cmd += output + self.assertTrue(self.res.Succeeded(), + msg if (msg) else CMD_MSG(cmd)) + + def match( + self, + str, + patterns, + msg=None, + trace=False, + error=False, + matching=True, + exe=True): + """run command in str, and match the result against regexp in patterns returning the match object for the first matching pattern + + Otherwise, all the arguments have the same meanings as for the expect function""" + + trace = (True if traceAlways else trace) + + if exe: + # First run the command. If we are expecting error, set check=False. + # Pass the assert message along since it provides more semantic + # info. + self.runCmd( + str, + msg=msg, + trace=( + True if trace else False), + check=not error) + + # Then compare the output against expected strings. + output = self.res.GetError() if error else self.res.GetOutput() + + # If error is True, the API client expects the command to fail! + if error: + self.assertFalse(self.res.Succeeded(), + "Command '" + str + "' is expected to fail!") + else: + # No execution required, just compare str against the golden input. + output = str + with recording(self, trace) as sbuf: + print("looking at:", output, file=sbuf) + + # The heading says either "Expecting" or "Not expecting". + heading = "Expecting" if matching else "Not expecting" + + for pattern in patterns: + # Match Objects always have a boolean value of True. + match_object = re.search(pattern, output) + matched = bool(match_object) + with recording(self, trace) as sbuf: + print("%s pattern: %s" % (heading, pattern), file=sbuf) + print("Matched" if matched else "Not matched", file=sbuf) + if matched: + break + + self.assertTrue(matched if matching else not matched, + msg if msg else EXP_MSG(str, output, exe)) + + return match_object + + def check_completion_with_desc(self, str_input, match_desc_pairs, enforce_order=False): + """ + Checks that when the given input is completed at the given list of + completions and descriptions is returned. + :param str_input: The input that should be completed. The completion happens at the end of the string. + :param match_desc_pairs: A list of pairs that indicate what completions have to be in the list of + completions returned by LLDB. The first element of the pair is the completion + string that LLDB should generate and the second element the description. + :param enforce_order: True iff the order in which the completions are returned by LLDB + should match the order of the match_desc_pairs pairs. + """ + interp = self.dbg.GetCommandInterpreter() + match_strings = lldb.SBStringList() + description_strings = lldb.SBStringList() + num_matches = interp.HandleCompletionWithDescriptions(str_input, len(str_input), 0, -1, match_strings, description_strings) + self.assertEqual(len(description_strings), len(match_strings)) + + # The index of the last matched description in description_strings or + # -1 if no description has been matched yet. + last_found_index = -1 + out_of_order_errors = "" + missing_pairs = [] + for pair in match_desc_pairs: + found_pair = False + for i in range(num_matches + 1): + match_candidate = match_strings.GetStringAtIndex(i) + description_candidate = description_strings.GetStringAtIndex(i) + if match_candidate == pair[0] and description_candidate == pair[1]: + found_pair = True + if enforce_order and last_found_index > i: + new_err = ("Found completion " + pair[0] + " at index " + + str(i) + " in returned completion list but " + + "should have been after completion " + + match_strings.GetStringAtIndex(last_found_index) + + " (index:" + str(last_found_index) + ")\n") + out_of_order_errors += new_err + last_found_index = i + break + if not found_pair: + missing_pairs.append(pair) + + error_msg = "" + got_failure = False + if len(missing_pairs): + got_failure = True + error_msg += "Missing pairs:\n" + for pair in missing_pairs: + error_msg += " [" + pair[0] + ":" + pair[1] + "]\n" + if len(out_of_order_errors): + got_failure = True + error_msg += out_of_order_errors + if got_failure: + error_msg += "Got the following " + str(num_matches) + " completions back:\n" + for i in range(num_matches + 1): + match_candidate = match_strings.GetStringAtIndex(i) + description_candidate = description_strings.GetStringAtIndex(i) + error_msg += "[" + match_candidate + ":" + description_candidate + "] index " + str(i) + "\n" + self.assertFalse(got_failure, error_msg) + + def complete_exactly(self, str_input, patterns): + self.complete_from_to(str_input, patterns, True) + + def complete_from_to(self, str_input, patterns, turn_off_re_match=False): + """Test that the completion mechanism completes str_input to patterns, + where patterns could be a pattern-string or a list of pattern-strings""" + # Patterns should not be None in order to proceed. + self.assertFalse(patterns is None) + # And should be either a string or list of strings. Check for list type + # below, if not, make a list out of the singleton string. If patterns + # is not a string or not a list of strings, there'll be runtime errors + # later on. + if not isinstance(patterns, list): + patterns = [patterns] + + interp = self.dbg.GetCommandInterpreter() + match_strings = lldb.SBStringList() + num_matches = interp.HandleCompletion(str_input, len(str_input), 0, -1, match_strings) + common_match = match_strings.GetStringAtIndex(0) + if num_matches == 0: + compare_string = str_input + else: + if common_match != None and len(common_match) > 0: + compare_string = str_input + common_match + else: + compare_string = "" + for idx in range(1, num_matches+1): + compare_string += match_strings.GetStringAtIndex(idx) + "\n" + + for p in patterns: + if turn_off_re_match: + self.expect( + compare_string, msg=COMPLETION_MSG( + str_input, p, match_strings), exe=False, substrs=[p]) + else: + self.expect( + compare_string, msg=COMPLETION_MSG( + str_input, p, match_strings), exe=False, patterns=[p]) + + def completions_match(self, command, completions): + """Checks that the completions for the given command are equal to the + given list of completions""" + interp = self.dbg.GetCommandInterpreter() + match_strings = lldb.SBStringList() + interp.HandleCompletion(command, len(command), 0, -1, match_strings) + # match_strings is a 1-indexed list, so we have to slice... + self.assertItemsEqual(completions, list(match_strings)[1:], + "List of returned completion is wrong") + + def completions_contain(self, command, completions): + """Checks that the completions for the given command contain the given + list of completions.""" + interp = self.dbg.GetCommandInterpreter() + match_strings = lldb.SBStringList() + interp.HandleCompletion(command, len(command), 0, -1, match_strings) + for completion in completions: + # match_strings is a 1-indexed list, so we have to slice... + self.assertIn(completion, list(match_strings)[1:], + "Couldn't find expected completion") + + def filecheck( + self, + command, + check_file, + filecheck_options = '', + expect_cmd_failure = False): + # Run the command. + self.runCmd( + command, + check=(not expect_cmd_failure), + msg="FileCheck'ing result of `{0}`".format(command)) + + self.assertTrue((not expect_cmd_failure) == self.res.Succeeded()) + + # Get the error text if there was an error, and the regular text if not. + output = self.res.GetOutput() if self.res.Succeeded() \ + else self.res.GetError() + + # Assemble the absolute path to the check file. As a convenience for + # LLDB inline tests, assume that the check file is a relative path to + # a file within the inline test directory. + if check_file.endswith('.pyc'): + check_file = check_file[:-1] + check_file_abs = os.path.abspath(check_file) + + # Run FileCheck. + filecheck_bin = configuration.get_filecheck_path() + if not filecheck_bin: + self.assertTrue(False, "No valid FileCheck executable specified") + filecheck_args = [filecheck_bin, check_file_abs] + if filecheck_options: + filecheck_args.append(filecheck_options) + subproc = Popen(filecheck_args, stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines = True) + cmd_stdout, cmd_stderr = subproc.communicate(input=output) + cmd_status = subproc.returncode + + filecheck_cmd = " ".join(filecheck_args) + filecheck_trace = """ + --- FileCheck trace (code={0}) --- + {1} + + FileCheck input: + {2} + + FileCheck output: + {3} + {4} + """.format(cmd_status, filecheck_cmd, output, cmd_stdout, cmd_stderr) + + trace = cmd_status != 0 or traceAlways + with recording(self, trace) as sbuf: + print(filecheck_trace, file=sbuf) + + self.assertTrue(cmd_status == 0) + + def expect( + self, + str, + msg=None, + patterns=None, + startstr=None, + endstr=None, + substrs=None, + trace=False, + error=False, + ordered=True, + matching=True, + exe=True, + inHistory=False): + """ + Similar to runCmd; with additional expect style output matching ability. + + Ask the command interpreter to handle the command and then check its + return status. The 'msg' parameter specifies an informational assert + message. We expect the output from running the command to start with + 'startstr', matches the substrings contained in 'substrs', and regexp + matches the patterns contained in 'patterns'. + + When matching is true and ordered is true, which are both the default, + the strings in the substrs array have to appear in the command output + in the order in which they appear in the array. + + If the keyword argument error is set to True, it signifies that the API + client is expecting the command to fail. In this case, the error stream + from running the command is retrieved and compared against the golden + input, instead. + + If the keyword argument matching is set to False, it signifies that the API + client is expecting the output of the command not to match the golden + input. + + Finally, the required argument 'str' represents the lldb command to be + sent to the command interpreter. In case the keyword argument 'exe' is + set to False, the 'str' is treated as a string to be matched/not-matched + against the golden input. + """ + # Catch cases where `expect` has been miscalled. Specifically, prevent + # this easy to make mistake: + # self.expect("lldb command", "some substr") + # The `msg` parameter is used only when a failed match occurs. A failed + # match can only occur when one of `patterns`, `startstr`, `endstr`, or + # `substrs` has been given. Thus, if a `msg` is given, it's an error to + # not also provide one of the matcher parameters. + if msg and not (patterns or startstr or endstr or substrs or error): + assert False, "expect() missing a matcher argument" + + # Check `patterns` and `substrs` are not accidentally given as strings. + assert not isinstance(patterns, six.string_types), \ + "patterns must be a collection of strings" + assert not isinstance(substrs, six.string_types), \ + "substrs must be a collection of strings" + + trace = (True if traceAlways else trace) + + if exe: + # First run the command. If we are expecting error, set check=False. + # Pass the assert message along since it provides more semantic + # info. + self.runCmd( + str, + msg=msg, + trace=( + True if trace else False), + check=not error, + inHistory=inHistory) + + # Then compare the output against expected strings. + output = self.res.GetError() if error else self.res.GetOutput() + + # If error is True, the API client expects the command to fail! + if error: + self.assertFalse(self.res.Succeeded(), + "Command '" + str + "' is expected to fail!") + else: + # No execution required, just compare str against the golden input. + if isinstance(str, lldb.SBCommandReturnObject): + output = str.GetOutput() + else: + output = str + with recording(self, trace) as sbuf: + print("looking at:", output, file=sbuf) + + expecting_str = "Expecting" if matching else "Not expecting" + def found_str(matched): + return "was found" if matched else "was not found" + + # To be used as assert fail message and/or trace content + log_lines = [ + "{}:".format("Ran command" if exe else "Checking string"), + "\"{}\"".format(str), + # Space out command and output + "", + ] + if exe: + # Newline before output to make large strings more readable + log_lines.append("Got output:\n{}".format(output)) + + # Assume that we start matched if we want a match + # Meaning if you have no conditions, matching or + # not matching will always pass + matched = matching + + # We will stop checking on first failure + if startstr: + matched = output.startswith(startstr) + log_lines.append("{} start string: \"{}\" ({})".format( + expecting_str, startstr, found_str(matched))) + + if endstr and matched == matching: + matched = output.endswith(endstr) + log_lines.append("{} end string: \"{}\" ({})".format( + expecting_str, endstr, found_str(matched))) + + if substrs and matched == matching: + start = 0 + for substr in substrs: + index = output[start:].find(substr) + start = start + index if ordered and matching else 0 + matched = index != -1 + log_lines.append("{} sub string: \"{}\" ({})".format( + expecting_str, substr, found_str(matched))) + + if matched != matching: + break + + if patterns and matched == matching: + for pattern in patterns: + matched = re.search(pattern, output) + + pattern_line = "{} regex pattern: \"{}\" ({}".format( + expecting_str, pattern, found_str(matched)) + if matched: + pattern_line += ", matched \"{}\"".format( + matched.group(0)) + pattern_line += ")" + log_lines.append(pattern_line) + + # Convert to bool because match objects + # are True-ish but != True itself + matched = bool(matched) + if matched != matching: + break + + # If a check failed, add any extra assert message + if msg is not None and matched != matching: + log_lines.append(msg) + + log_msg = "\n".join(log_lines) + with recording(self, trace) as sbuf: + print(log_msg, file=sbuf) + if matched != matching: + self.fail(log_msg) + + def expect_expr( + self, + expr, + result_summary=None, + result_value=None, + result_type=None, + result_children=None + ): + """ + Evaluates the given expression and verifies the result. + :param expr: The expression as a string. + :param result_summary: The summary that the expression should have. None if the summary should not be checked. + :param result_value: The value that the expression should have. None if the value should not be checked. + :param result_type: The type that the expression result should have. None if the type should not be checked. + :param result_children: The expected children of the expression result + as a list of ValueChecks. None if the children shouldn't be checked. + """ + self.assertTrue(expr.strip() == expr, "Expression contains trailing/leading whitespace: '" + expr + "'") + + frame = self.frame() + options = lldb.SBExpressionOptions() + + # Disable fix-its that tests don't pass by accident. + options.SetAutoApplyFixIts(False) + + # Set the usual default options for normal expressions. + options.SetIgnoreBreakpoints(True) + + if self.frame().IsValid(): + options.SetLanguage(frame.GuessLanguage()) + eval_result = self.frame().EvaluateExpression(expr, options) + else: + target = self.target() + # If there is no selected target, run the expression in the dummy + # target. + if not target.IsValid(): + target = self.dbg.GetDummyTarget() + eval_result = target.EvaluateExpression(expr, options) + + value_check = ValueCheck(type=result_type, value=result_value, + summary=result_summary, children=result_children) + value_check.check_value(self, eval_result, str(eval_result)) + return eval_result + + def expect_var_path( + self, + var_path, + summary=None, + value=None, + type=None, + children=None + ): + """ + Evaluates the given variable path and verifies the result. + See also 'frame variable' and SBFrame.GetValueForVariablePath. + :param var_path: The variable path as a string. + :param summary: The summary that the variable should have. None if the summary should not be checked. + :param value: The value that the variable should have. None if the value should not be checked. + :param type: The type that the variable result should have. None if the type should not be checked. + :param children: The expected children of the variable as a list of ValueChecks. + None if the children shouldn't be checked. + """ + self.assertTrue(var_path.strip() == var_path, + "Expression contains trailing/leading whitespace: '" + var_path + "'") + + frame = self.frame() + eval_result = frame.GetValueForVariablePath(var_path) + + value_check = ValueCheck(type=type, value=value, + summary=summary, children=children) + value_check.check_value(self, eval_result, str(eval_result)) + return eval_result + + """Assert that an lldb.SBError is in the "success" state.""" + def assertSuccess(self, obj, msg=None): + if not obj.Success(): + error = obj.GetCString() + self.fail(self._formatMessage(msg, + "'{}' is not success".format(error))) + + def createTestTarget(self, file_path=None, msg=None, + load_dependent_modules=True): + """ + Creates a target from the file found at the given file path. + Asserts that the resulting target is valid. + :param file_path: The file path that should be used to create the target. + The default argument opens the current default test + executable in the current test directory. + :param msg: A custom error message. + """ + if file_path is None: + file_path = self.getBuildArtifact("a.out") + error = lldb.SBError() + triple = "" + platform = "" + target = self.dbg.CreateTarget(file_path, triple, platform, + load_dependent_modules, error) + if error.Fail(): + err = "Couldn't create target for path '{}': {}".format(file_path, + str(error)) + self.fail(self._formatMessage(msg, err)) + + self.assertTrue(target.IsValid(), "Got invalid target without error") + return target + + # ================================================= + # Misc. helper methods for debugging test execution + # ================================================= + + def DebugSBValue(self, val): + """Debug print a SBValue object, if traceAlways is True.""" + from .lldbutil import value_type_to_str + + if not traceAlways: + return + + err = sys.stderr + err.write(val.GetName() + ":\n") + err.write('\t' + "TypeName -> " + val.GetTypeName() + '\n') + err.write('\t' + "ByteSize -> " + + str(val.GetByteSize()) + '\n') + err.write('\t' + "NumChildren -> " + + str(val.GetNumChildren()) + '\n') + err.write('\t' + "Value -> " + str(val.GetValue()) + '\n') + err.write('\t' + "ValueAsUnsigned -> " + + str(val.GetValueAsUnsigned()) + '\n') + err.write( + '\t' + + "ValueType -> " + + value_type_to_str( + val.GetValueType()) + + '\n') + err.write('\t' + "Summary -> " + str(val.GetSummary()) + '\n') + err.write('\t' + "IsPointerType -> " + + str(val.TypeIsPointerType()) + '\n') + err.write('\t' + "Location -> " + val.GetLocation() + '\n') + + def DebugSBType(self, type): + """Debug print a SBType object, if traceAlways is True.""" + if not traceAlways: + return + + err = sys.stderr + err.write(type.GetName() + ":\n") + err.write('\t' + "ByteSize -> " + + str(type.GetByteSize()) + '\n') + err.write('\t' + "IsPointerType -> " + + str(type.IsPointerType()) + '\n') + err.write('\t' + "IsReferenceType -> " + + str(type.IsReferenceType()) + '\n') + + def DebugPExpect(self, child): + """Debug the spwaned pexpect object.""" + if not traceAlways: + return + + print(child) + + @classmethod + def RemoveTempFile(cls, file): + if os.path.exists(file): + remove_file(file) + + # On Windows, the first attempt to delete a recently-touched file can fail + # because of a race with antimalware scanners. This function will detect a + # failure and retry. + + + def remove_file(file, num_retries=1, sleep_duration=0.5): + for i in range(num_retries + 1): + try: + os.remove(file) + return True + except: + time.sleep(sleep_duration) + continue + return False +diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py +index 255a4805a973..8f12c9b31077 100644 +--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py ++++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py +@@ -1,359 +1,358 @@ + + from lldbsuite.test.lldbtest import * + import os + import vscode + import time + + + class VSCodeTestCaseBase(TestBase): + + NO_DEBUG_INFO_TESTCASE = True + + def create_debug_adaptor(self, lldbVSCodeEnv=None): + '''Create the Visual Studio Code debug adaptor''' +- self.assertTrue(os.path.exists(self.lldbVSCodeExec), +- 'lldb-vscode must exist') ++ self.assertTrue(is_exe(self.lldbVSCodeExec), 'lldb-vscode must exist') + log_file_path = self.getBuildArtifact('vscode.txt') + self.vscode = vscode.DebugAdaptor( + executable=self.lldbVSCodeExec, init_commands=self.setUpCommands(), + log_file=log_file_path, env=lldbVSCodeEnv) + + def build_and_create_debug_adaptor(self, lldbVSCodeEnv=None): + self.build() + self.create_debug_adaptor(lldbVSCodeEnv) + + def set_source_breakpoints(self, source_path, lines, condition=None, + hitCondition=None): + '''Sets source breakpoints and returns an array of strings containing + the breakpoint IDs ("1", "2") for each breakpoint that was set. + ''' + response = self.vscode.request_setBreakpoints( + source_path, lines, condition=condition, hitCondition=hitCondition) + if response is None: + return [] + breakpoints = response['body']['breakpoints'] + breakpoint_ids = [] + for breakpoint in breakpoints: + breakpoint_ids.append('%i' % (breakpoint['id'])) + return breakpoint_ids + + def set_function_breakpoints(self, functions, condition=None, + hitCondition=None): + '''Sets breakpoints by function name given an array of function names + and returns an array of strings containing the breakpoint IDs + ("1", "2") for each breakpoint that was set. + ''' + response = self.vscode.request_setFunctionBreakpoints( + functions, condition=condition, hitCondition=hitCondition) + if response is None: + return [] + breakpoints = response['body']['breakpoints'] + breakpoint_ids = [] + for breakpoint in breakpoints: + breakpoint_ids.append('%i' % (breakpoint['id'])) + return breakpoint_ids + + def waitUntil(self, condition_callback): + for _ in range(20): + if condition_callback(): + return True + time.sleep(0.5) + return False + + def verify_breakpoint_hit(self, breakpoint_ids): + '''Wait for the process we are debugging to stop, and verify we hit + any breakpoint location in the "breakpoint_ids" array. + "breakpoint_ids" should be a list of breakpoint ID strings + (["1", "2"]). The return value from self.set_source_breakpoints() + or self.set_function_breakpoints() can be passed to this function''' + stopped_events = self.vscode.wait_for_stopped() + for stopped_event in stopped_events: + if 'body' in stopped_event: + body = stopped_event['body'] + if 'reason' not in body: + continue + if body['reason'] != 'breakpoint': + continue + if 'description' not in body: + continue + # Descriptions for breakpoints will be in the form + # "breakpoint 1.1", so look for any description that matches + # ("breakpoint 1.") in the description field as verification + # that one of the breakpoint locations was hit. VSCode doesn't + # allow breakpoints to have multiple locations, but LLDB does. + # So when looking at the description we just want to make sure + # the right breakpoint matches and not worry about the actual + # location. + description = body['description'] + for breakpoint_id in breakpoint_ids: + match_desc = 'breakpoint %s.' % (breakpoint_id) + if match_desc in description: + return + self.assertTrue(False, "breakpoint not hit") + + def verify_exception_breakpoint_hit(self, filter_label): + '''Wait for the process we are debugging to stop, and verify the stop + reason is 'exception' and that the description matches + 'filter_label' + ''' + stopped_events = self.vscode.wait_for_stopped() + for stopped_event in stopped_events: + if 'body' in stopped_event: + body = stopped_event['body'] + if 'reason' not in body: + continue + if body['reason'] != 'exception': + continue + if 'description' not in body: + continue + description = body['description'] + if filter_label == description: + return True + return False + + def verify_commands(self, flavor, output, commands): + self.assertTrue(output and len(output) > 0, "expect console output") + lines = output.splitlines() + prefix = '(lldb) ' + for cmd in commands: + found = False + for line in lines: + if line.startswith(prefix) and cmd in line: + found = True + break + self.assertTrue(found, + "verify '%s' found in console output for '%s'" % ( + cmd, flavor)) + + def get_dict_value(self, d, key_path): + '''Verify each key in the key_path array is in contained in each + dictionary within "d". Assert if any key isn't in the + corresponding dictionary. This is handy for grabbing values from VS + Code response dictionary like getting + response['body']['stackFrames'] + ''' + value = d + for key in key_path: + if key in value: + value = value[key] + else: + self.assertTrue(key in value, + 'key "%s" from key_path "%s" not in "%s"' % ( + key, key_path, d)) + return value + + def get_stackFrames_and_totalFramesCount(self, threadId=None, startFrame=None, + levels=None, dump=False): + response = self.vscode.request_stackTrace(threadId=threadId, + startFrame=startFrame, + levels=levels, + dump=dump) + if response: + stackFrames = self.get_dict_value(response, ['body', 'stackFrames']) + totalFrames = self.get_dict_value(response, ['body', 'totalFrames']) + self.assertTrue(totalFrames > 0, + 'verify totalFrames count is provided by extension that supports ' + 'async frames loading') + return (stackFrames, totalFrames) + return (None, 0) + + def get_stackFrames(self, threadId=None, startFrame=None, levels=None, + dump=False): + (stackFrames, totalFrames) = self.get_stackFrames_and_totalFramesCount( + threadId=threadId, + startFrame=startFrame, + levels=levels, + dump=dump) + return stackFrames + + def get_source_and_line(self, threadId=None, frameIndex=0): + stackFrames = self.get_stackFrames(threadId=threadId, + startFrame=frameIndex, + levels=1) + if stackFrames is not None: + stackFrame = stackFrames[0] + ['source', 'path'] + if 'source' in stackFrame: + source = stackFrame['source'] + if 'path' in source: + if 'line' in stackFrame: + return (source['path'], stackFrame['line']) + return ('', 0) + + def get_stdout(self, timeout=0.0): + return self.vscode.get_output('stdout', timeout=timeout) + + def get_console(self, timeout=0.0): + return self.vscode.get_output('console', timeout=timeout) + + def collect_console(self, duration): + return self.vscode.collect_output('console', duration=duration) + + def get_local_as_int(self, name, threadId=None): + value = self.vscode.get_local_variable_value(name, threadId=threadId) + if value.startswith('0x'): + return int(value, 16) + elif value.startswith('0'): + return int(value, 8) + else: + return int(value) + + def set_local(self, name, value, id=None): + '''Set a top level local variable only.''' + return self.vscode.request_setVariable(1, name, str(value), id=id) + + def set_global(self, name, value, id=None): + '''Set a top level global variable only.''' + return self.vscode.request_setVariable(2, name, str(value), id=id) + + def stepIn(self, threadId=None, waitForStop=True): + self.vscode.request_stepIn(threadId=threadId) + if waitForStop: + return self.vscode.wait_for_stopped() + return None + + def stepOver(self, threadId=None, waitForStop=True): + self.vscode.request_next(threadId=threadId) + if waitForStop: + return self.vscode.wait_for_stopped() + return None + + def stepOut(self, threadId=None, waitForStop=True): + self.vscode.request_stepOut(threadId=threadId) + if waitForStop: + return self.vscode.wait_for_stopped() + return None + + def continue_to_next_stop(self): + self.vscode.request_continue() + return self.vscode.wait_for_stopped() + + def continue_to_breakpoints(self, breakpoint_ids): + self.vscode.request_continue() + self.verify_breakpoint_hit(breakpoint_ids) + + def continue_to_exception_breakpoint(self, filter_label): + self.vscode.request_continue() + self.assertTrue(self.verify_exception_breakpoint_hit(filter_label), + 'verify we got "%s"' % (filter_label)) + + def continue_to_exit(self, exitCode=0): + self.vscode.request_continue() + stopped_events = self.vscode.wait_for_stopped() + self.assertEquals(len(stopped_events), 1, + "stopped_events = {}".format(stopped_events)) + self.assertEquals(stopped_events[0]['event'], 'exited', + 'make sure program ran to completion') + self.assertEquals(stopped_events[0]['body']['exitCode'], exitCode, + 'exitCode == %i' % (exitCode)) + + def attach(self, program=None, pid=None, waitFor=None, trace=None, + initCommands=None, preRunCommands=None, stopCommands=None, + exitCommands=None, attachCommands=None, coreFile=None, + disconnectAutomatically=True, terminateCommands=None, + postRunCommands=None, sourceMap=None): + '''Build the default Makefile target, create the VSCode debug adaptor, + and attach to the process. + ''' + # Make sure we disconnect and terminate the VSCode debug adaptor even + # if we throw an exception during the test case. + def cleanup(): + if disconnectAutomatically: + self.vscode.request_disconnect(terminateDebuggee=True) + self.vscode.terminate() + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + # Initialize and launch the program + self.vscode.request_initialize() + response = self.vscode.request_attach( + program=program, pid=pid, waitFor=waitFor, trace=trace, + initCommands=initCommands, preRunCommands=preRunCommands, + stopCommands=stopCommands, exitCommands=exitCommands, + attachCommands=attachCommands, terminateCommands=terminateCommands, + coreFile=coreFile, postRunCommands=postRunCommands, + sourceMap=sourceMap) + if not (response and response['success']): + self.assertTrue(response['success'], + 'attach failed (%s)' % (response['message'])) + + def launch(self, program=None, args=None, cwd=None, env=None, + stopOnEntry=False, disableASLR=True, + disableSTDIO=False, shellExpandArguments=False, + trace=False, initCommands=None, preRunCommands=None, + stopCommands=None, exitCommands=None, terminateCommands=None, + sourcePath=None, debuggerRoot=None, launchCommands=None, + sourceMap=None, disconnectAutomatically=True, runInTerminal=False, + expectFailure=False, postRunCommands=None): + '''Sending launch request to vscode + ''' + + # Make sure we disconnect and terminate the VSCode debug adapter, + # if we throw an exception during the test case + def cleanup(): + if disconnectAutomatically: + self.vscode.request_disconnect(terminateDebuggee=True) + self.vscode.terminate() + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # Initialize and launch the program + self.vscode.request_initialize() + response = self.vscode.request_launch( + program, + args=args, + cwd=cwd, + env=env, + stopOnEntry=stopOnEntry, + disableASLR=disableASLR, + disableSTDIO=disableSTDIO, + shellExpandArguments=shellExpandArguments, + trace=trace, + initCommands=initCommands, + preRunCommands=preRunCommands, + stopCommands=stopCommands, + exitCommands=exitCommands, + terminateCommands=terminateCommands, + sourcePath=sourcePath, + debuggerRoot=debuggerRoot, + launchCommands=launchCommands, + sourceMap=sourceMap, + runInTerminal=runInTerminal, + expectFailure=expectFailure, + postRunCommands=postRunCommands) + + if expectFailure: + return response + + if not (response and response['success']): + self.assertTrue(response['success'], + 'launch failed (%s)' % (response['message'])) + # We need to trigger a request_configurationDone after we've successfully + # attached a runInTerminal process to finish initialization. + if runInTerminal: + self.vscode.request_configurationDone() + return response + + + def build_and_launch(self, program, args=None, cwd=None, env=None, + stopOnEntry=False, disableASLR=True, + disableSTDIO=False, shellExpandArguments=False, + trace=False, initCommands=None, preRunCommands=None, + stopCommands=None, exitCommands=None, + terminateCommands=None, sourcePath=None, + debuggerRoot=None, runInTerminal=False, + disconnectAutomatically=True, postRunCommands=None, + lldbVSCodeEnv=None): + '''Build the default Makefile target, create the VSCode debug adaptor, + and launch the process. + ''' + self.build_and_create_debug_adaptor(lldbVSCodeEnv) + self.assertTrue(os.path.exists(program), 'executable must exist') + + return self.launch(program, args, cwd, env, stopOnEntry, disableASLR, + disableSTDIO, shellExpandArguments, trace, + initCommands, preRunCommands, stopCommands, exitCommands, + terminateCommands, sourcePath, debuggerRoot, runInTerminal=runInTerminal, + disconnectAutomatically=disconnectAutomatically, + postRunCommands=postRunCommands) +diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp +index 8e1857ce4bfa..1d35a09b1010 100644 +--- a/lldb/source/Target/Process.cpp ++++ b/lldb/source/Target/Process.cpp +@@ -1,6130 +1,6132 @@ + //===-- Process.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 + #include + #include + + #include "llvm/ADT/ScopeExit.h" + #include "llvm/Support/ScopedPrinter.h" + #include "llvm/Support/Threading.h" + + #include "lldb/Breakpoint/BreakpointLocation.h" + #include "lldb/Breakpoint/StoppointCallbackContext.h" + #include "lldb/Core/Debugger.h" + #include "lldb/Core/Module.h" + #include "lldb/Core/ModuleSpec.h" + #include "lldb/Core/PluginManager.h" + #include "lldb/Core/StreamFile.h" + #include "lldb/Expression/DiagnosticManager.h" + #include "lldb/Expression/DynamicCheckerFunctions.h" + #include "lldb/Expression/UserExpression.h" + #include "lldb/Expression/UtilityFunction.h" + #include "lldb/Host/ConnectionFileDescriptor.h" + #include "lldb/Host/FileSystem.h" + #include "lldb/Host/Host.h" + #include "lldb/Host/HostInfo.h" + #include "lldb/Host/OptionParser.h" + #include "lldb/Host/Pipe.h" + #include "lldb/Host/Terminal.h" + #include "lldb/Host/ThreadLauncher.h" + #include "lldb/Interpreter/CommandInterpreter.h" + #include "lldb/Interpreter/OptionArgParser.h" + #include "lldb/Interpreter/OptionValueProperties.h" + #include "lldb/Symbol/Function.h" + #include "lldb/Symbol/Symbol.h" + #include "lldb/Target/ABI.h" + #include "lldb/Target/AssertFrameRecognizer.h" + #include "lldb/Target/DynamicLoader.h" + #include "lldb/Target/InstrumentationRuntime.h" + #include "lldb/Target/JITLoader.h" + #include "lldb/Target/JITLoaderList.h" + #include "lldb/Target/Language.h" + #include "lldb/Target/LanguageRuntime.h" + #include "lldb/Target/MemoryHistory.h" + #include "lldb/Target/MemoryRegionInfo.h" + #include "lldb/Target/OperatingSystem.h" + #include "lldb/Target/Platform.h" + #include "lldb/Target/Process.h" + #include "lldb/Target/RegisterContext.h" + #include "lldb/Target/StopInfo.h" + #include "lldb/Target/StructuredDataPlugin.h" + #include "lldb/Target/SystemRuntime.h" + #include "lldb/Target/Target.h" + #include "lldb/Target/TargetList.h" + #include "lldb/Target/Thread.h" + #include "lldb/Target/ThreadPlan.h" + #include "lldb/Target/ThreadPlanBase.h" + #include "lldb/Target/ThreadPlanCallFunction.h" + #include "lldb/Target/ThreadPlanStack.h" + #include "lldb/Target/UnixSignals.h" + #include "lldb/Utility/Event.h" + #include "lldb/Utility/LLDBLog.h" + #include "lldb/Utility/Log.h" + #include "lldb/Utility/NameMatches.h" + #include "lldb/Utility/ProcessInfo.h" + #include "lldb/Utility/SelectHelper.h" + #include "lldb/Utility/State.h" + #include "lldb/Utility/Timer.h" + + using namespace lldb; + using namespace lldb_private; + using namespace std::chrono; + + // Comment out line below to disable memory caching, overriding the process + // setting target.process.disable-memory-cache + #define ENABLE_MEMORY_CACHING + + #ifdef ENABLE_MEMORY_CACHING + #define DISABLE_MEM_CACHE_DEFAULT false + #else + #define DISABLE_MEM_CACHE_DEFAULT true + #endif + + class ProcessOptionValueProperties + : public Cloneable { + public: + ProcessOptionValueProperties(ConstString name) : Cloneable(name) {} + + const Property *GetPropertyAtIndex(const ExecutionContext *exe_ctx, + bool will_modify, + uint32_t idx) const override { + // When getting the value for a key from the process options, we will + // always try and grab the setting from the current process if there is + // one. Else we just use the one from this instance. + if (exe_ctx) { + Process *process = exe_ctx->GetProcessPtr(); + if (process) { + ProcessOptionValueProperties *instance_properties = + static_cast( + process->GetValueProperties().get()); + if (this != instance_properties) + return instance_properties->ProtectedGetPropertyAtIndex(idx); + } + } + return ProtectedGetPropertyAtIndex(idx); + } + }; + + static constexpr OptionEnumValueElement g_follow_fork_mode_values[] = { + { + eFollowParent, + "parent", + "Continue tracing the parent process and detach the child.", + }, + { + eFollowChild, + "child", + "Trace the child process and detach the parent.", + }, + }; + + #define LLDB_PROPERTIES_process + #include "TargetProperties.inc" + + enum { + #define LLDB_PROPERTIES_process + #include "TargetPropertiesEnum.inc" + ePropertyExperimental, + }; + + #define LLDB_PROPERTIES_process_experimental + #include "TargetProperties.inc" + + enum { + #define LLDB_PROPERTIES_process_experimental + #include "TargetPropertiesEnum.inc" + }; + + class ProcessExperimentalOptionValueProperties + : public Cloneable { + public: + ProcessExperimentalOptionValueProperties() + : Cloneable( + ConstString(Properties::GetExperimentalSettingsName())) {} + }; + + ProcessExperimentalProperties::ProcessExperimentalProperties() + : Properties(OptionValuePropertiesSP( + new ProcessExperimentalOptionValueProperties())) { + m_collection_sp->Initialize(g_process_experimental_properties); + } + + ProcessProperties::ProcessProperties(lldb_private::Process *process) + : Properties(), + m_process(process) // Can be nullptr for global ProcessProperties + { + if (process == nullptr) { + // Global process properties, set them up one time + m_collection_sp = + std::make_shared(ConstString("process")); + m_collection_sp->Initialize(g_process_properties); + m_collection_sp->AppendProperty( + ConstString("thread"), ConstString("Settings specific to threads."), + true, Thread::GetGlobalProperties().GetValueProperties()); + } else { + m_collection_sp = + OptionValueProperties::CreateLocalCopy(Process::GetGlobalProperties()); + m_collection_sp->SetValueChangedCallback( + ePropertyPythonOSPluginPath, + [this] { m_process->LoadOperatingSystemPlugin(true); }); + } + + m_experimental_properties_up = + std::make_unique(); + m_collection_sp->AppendProperty( + ConstString(Properties::GetExperimentalSettingsName()), + ConstString("Experimental settings - setting these won't produce " + "errors if the setting is not present."), + true, m_experimental_properties_up->GetValueProperties()); + } + + ProcessProperties::~ProcessProperties() = default; + + bool ProcessProperties::GetDisableMemoryCache() const { + const uint32_t idx = ePropertyDisableMemCache; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); + } + + uint64_t ProcessProperties::GetMemoryCacheLineSize() const { + const uint32_t idx = ePropertyMemCacheLineSize; + return m_collection_sp->GetPropertyAtIndexAsUInt64( + nullptr, idx, g_process_properties[idx].default_uint_value); + } + + Args ProcessProperties::GetExtraStartupCommands() const { + Args args; + const uint32_t idx = ePropertyExtraStartCommand; + m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, args); + return args; + } + + void ProcessProperties::SetExtraStartupCommands(const Args &args) { + const uint32_t idx = ePropertyExtraStartCommand; + m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, args); + } + + FileSpec ProcessProperties::GetPythonOSPluginPath() const { + const uint32_t idx = ePropertyPythonOSPluginPath; + return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); + } + + uint32_t ProcessProperties::GetVirtualAddressableBits() const { + const uint32_t idx = ePropertyVirtualAddressableBits; + return m_collection_sp->GetPropertyAtIndexAsUInt64( + nullptr, idx, g_process_properties[idx].default_uint_value); + } + + void ProcessProperties::SetVirtualAddressableBits(uint32_t bits) { + const uint32_t idx = ePropertyVirtualAddressableBits; + m_collection_sp->SetPropertyAtIndexAsUInt64(nullptr, idx, bits); + } + void ProcessProperties::SetPythonOSPluginPath(const FileSpec &file) { + const uint32_t idx = ePropertyPythonOSPluginPath; + m_collection_sp->SetPropertyAtIndexAsFileSpec(nullptr, idx, file); + } + + bool ProcessProperties::GetIgnoreBreakpointsInExpressions() const { + const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); + } + + void ProcessProperties::SetIgnoreBreakpointsInExpressions(bool ignore) { + const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, ignore); + } + + bool ProcessProperties::GetUnwindOnErrorInExpressions() const { + const uint32_t idx = ePropertyUnwindOnErrorInExpressions; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); + } + + void ProcessProperties::SetUnwindOnErrorInExpressions(bool ignore) { + const uint32_t idx = ePropertyUnwindOnErrorInExpressions; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, ignore); + } + + bool ProcessProperties::GetStopOnSharedLibraryEvents() const { + const uint32_t idx = ePropertyStopOnSharedLibraryEvents; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); + } + + void ProcessProperties::SetStopOnSharedLibraryEvents(bool stop) { + const uint32_t idx = ePropertyStopOnSharedLibraryEvents; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, stop); + } + + bool ProcessProperties::GetDisableLangRuntimeUnwindPlans() const { + const uint32_t idx = ePropertyDisableLangRuntimeUnwindPlans; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); + } + + void ProcessProperties::SetDisableLangRuntimeUnwindPlans(bool disable) { + const uint32_t idx = ePropertyDisableLangRuntimeUnwindPlans; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, disable); + m_process->Flush(); + } + + bool ProcessProperties::GetDetachKeepsStopped() const { + const uint32_t idx = ePropertyDetachKeepsStopped; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); + } + + void ProcessProperties::SetDetachKeepsStopped(bool stop) { + const uint32_t idx = ePropertyDetachKeepsStopped; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, stop); + } + + bool ProcessProperties::GetWarningsOptimization() const { + const uint32_t idx = ePropertyWarningOptimization; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); + } + + bool ProcessProperties::GetWarningsUnsupportedLanguage() const { + const uint32_t idx = ePropertyWarningUnsupportedLanguage; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); + } + + bool ProcessProperties::GetStopOnExec() const { + const uint32_t idx = ePropertyStopOnExec; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); + } + + std::chrono::seconds ProcessProperties::GetUtilityExpressionTimeout() const { + const uint32_t idx = ePropertyUtilityExpressionTimeout; + uint64_t value = m_collection_sp->GetPropertyAtIndexAsUInt64( + nullptr, idx, g_process_properties[idx].default_uint_value); + return std::chrono::seconds(value); + } + + std::chrono::seconds ProcessProperties::GetInterruptTimeout() const { + const uint32_t idx = ePropertyInterruptTimeout; + uint64_t value = m_collection_sp->GetPropertyAtIndexAsUInt64( + nullptr, idx, g_process_properties[idx].default_uint_value); + return std::chrono::seconds(value); + } + + bool ProcessProperties::GetSteppingRunsAllThreads() const { + const uint32_t idx = ePropertySteppingRunsAllThreads; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_process_properties[idx].default_uint_value != 0); + } + + bool ProcessProperties::GetOSPluginReportsAllThreads() const { + const bool fail_value = true; + const Property *exp_property = + m_collection_sp->GetPropertyAtIndex(nullptr, true, ePropertyExperimental); + OptionValueProperties *exp_values = + exp_property->GetValue()->GetAsProperties(); + if (!exp_values) + return fail_value; + + return exp_values->GetPropertyAtIndexAsBoolean( + nullptr, ePropertyOSPluginReportsAllThreads, fail_value); + } + + void ProcessProperties::SetOSPluginReportsAllThreads(bool does_report) { + const Property *exp_property = + m_collection_sp->GetPropertyAtIndex(nullptr, true, ePropertyExperimental); + OptionValueProperties *exp_values = + exp_property->GetValue()->GetAsProperties(); + if (exp_values) + exp_values->SetPropertyAtIndexAsBoolean( + nullptr, ePropertyOSPluginReportsAllThreads, does_report); + } + + FollowForkMode ProcessProperties::GetFollowForkMode() const { + const uint32_t idx = ePropertyFollowForkMode; + return (FollowForkMode)m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_process_properties[idx].default_uint_value); + } + + ProcessSP Process::FindPlugin(lldb::TargetSP target_sp, + llvm::StringRef plugin_name, + ListenerSP listener_sp, + const FileSpec *crash_file_path, + bool can_connect) { + static uint32_t g_process_unique_id = 0; + + ProcessSP process_sp; + ProcessCreateInstance create_callback = nullptr; + if (!plugin_name.empty()) { + create_callback = + PluginManager::GetProcessCreateCallbackForPluginName(plugin_name); + if (create_callback) { + process_sp = create_callback(target_sp, listener_sp, crash_file_path, + can_connect); + if (process_sp) { + if (process_sp->CanDebug(target_sp, true)) { + process_sp->m_process_unique_id = ++g_process_unique_id; + } else + process_sp.reset(); + } + } + } else { + for (uint32_t idx = 0; + (create_callback = + PluginManager::GetProcessCreateCallbackAtIndex(idx)) != nullptr; + ++idx) { + process_sp = create_callback(target_sp, listener_sp, crash_file_path, + can_connect); + if (process_sp) { + if (process_sp->CanDebug(target_sp, false)) { + process_sp->m_process_unique_id = ++g_process_unique_id; + break; + } else + process_sp.reset(); + } + } + } + return process_sp; + } + + ConstString &Process::GetStaticBroadcasterClass() { + static ConstString class_name("lldb.process"); + return class_name; + } + + Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp) + : Process(target_sp, listener_sp, + UnixSignals::Create(HostInfo::GetArchitecture())) { + // This constructor just delegates to the full Process constructor, + // defaulting to using the Host's UnixSignals. + } + + Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp, + const UnixSignalsSP &unix_signals_sp) + : ProcessProperties(this), + Broadcaster((target_sp->GetDebugger().GetBroadcasterManager()), + Process::GetStaticBroadcasterClass().AsCString()), + m_target_wp(target_sp), m_public_state(eStateUnloaded), + m_private_state(eStateUnloaded), + m_private_state_broadcaster(nullptr, + "lldb.process.internal_state_broadcaster"), + m_private_state_control_broadcaster( + nullptr, "lldb.process.internal_state_control_broadcaster"), + m_private_state_listener_sp( + Listener::MakeListener("lldb.process.internal_state_listener")), + m_mod_id(), m_process_unique_id(0), m_thread_index_id(0), + m_thread_id_to_index_id_map(), m_exit_status(-1), m_exit_string(), + m_exit_status_mutex(), m_thread_mutex(), m_thread_list_real(this), + m_thread_list(this), m_thread_plans(*this), m_extended_thread_list(this), + m_extended_thread_stop_id(0), m_queue_list(this), m_queue_list_stop_id(0), + m_notifications(), m_image_tokens(), m_listener_sp(listener_sp), + m_breakpoint_site_list(), m_dynamic_checkers_up(), + m_unix_signals_sp(unix_signals_sp), m_abi_sp(), m_process_input_reader(), + m_stdio_communication("process.stdio"), m_stdio_communication_mutex(), + m_stdin_forward(false), m_stdout_data(), m_stderr_data(), + m_profile_data_comm_mutex(), m_profile_data(), m_iohandler_sync(0), + m_memory_cache(*this), m_allocated_memory_cache(*this), + m_should_detach(false), m_next_event_action_up(), m_public_run_lock(), + m_private_run_lock(), m_finalizing(false), + m_clear_thread_plans_on_stop(false), m_force_next_event_delivery(false), + m_last_broadcast_state(eStateInvalid), m_destroy_in_process(false), + m_can_interpret_function_calls(false), m_warnings_issued(), + m_run_thread_plan_lock(), m_can_jit(eCanJITDontKnow) { + CheckInWithManager(); + + Log *log = GetLog(LLDBLog::Object); + LLDB_LOGF(log, "%p Process::Process()", static_cast(this)); + + if (!m_unix_signals_sp) + m_unix_signals_sp = std::make_shared(); + + SetEventName(eBroadcastBitStateChanged, "state-changed"); + SetEventName(eBroadcastBitInterrupt, "interrupt"); + SetEventName(eBroadcastBitSTDOUT, "stdout-available"); + SetEventName(eBroadcastBitSTDERR, "stderr-available"); + SetEventName(eBroadcastBitProfileData, "profile-data-available"); + SetEventName(eBroadcastBitStructuredData, "structured-data-available"); + + m_private_state_control_broadcaster.SetEventName( + eBroadcastInternalStateControlStop, "control-stop"); + m_private_state_control_broadcaster.SetEventName( + eBroadcastInternalStateControlPause, "control-pause"); + m_private_state_control_broadcaster.SetEventName( + eBroadcastInternalStateControlResume, "control-resume"); + + m_listener_sp->StartListeningForEvents( + this, eBroadcastBitStateChanged | eBroadcastBitInterrupt | + eBroadcastBitSTDOUT | eBroadcastBitSTDERR | + eBroadcastBitProfileData | eBroadcastBitStructuredData); + + m_private_state_listener_sp->StartListeningForEvents( + &m_private_state_broadcaster, + eBroadcastBitStateChanged | eBroadcastBitInterrupt); + + m_private_state_listener_sp->StartListeningForEvents( + &m_private_state_control_broadcaster, + eBroadcastInternalStateControlStop | eBroadcastInternalStateControlPause | + eBroadcastInternalStateControlResume); + // We need something valid here, even if just the default UnixSignalsSP. + assert(m_unix_signals_sp && "null m_unix_signals_sp after initialization"); + + // Allow the platform to override the default cache line size + OptionValueSP value_sp = + m_collection_sp + ->GetPropertyAtIndex(nullptr, true, ePropertyMemCacheLineSize) + ->GetValue(); + uint32_t platform_cache_line_size = + target_sp->GetPlatform()->GetDefaultMemoryCacheLineSize(); + if (!value_sp->OptionWasSet() && platform_cache_line_size != 0) + value_sp->SetUInt64Value(platform_cache_line_size); + + RegisterAssertFrameRecognizer(this); + } + + Process::~Process() { + Log *log = GetLog(LLDBLog::Object); + LLDB_LOGF(log, "%p Process::~Process()", static_cast(this)); + StopPrivateStateThread(); + + // ThreadList::Clear() will try to acquire this process's mutex, so + // explicitly clear the thread list here to ensure that the mutex is not + // destroyed before the thread list. + m_thread_list.Clear(); + } + + ProcessProperties &Process::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 ProcessProperties *g_settings_ptr = + new ProcessProperties(nullptr); + return *g_settings_ptr; + } + + void Process::Finalize() { + if (m_finalizing.exchange(true)) + return; + + // Destroy the process. This will call the virtual function DoDestroy under + // the hood, giving our derived class a chance to do the ncessary tear down. + DestroyImpl(false); + + // Clear our broadcaster before we proceed with destroying + Broadcaster::Clear(); + + // Do any cleanup needed prior to being destructed... Subclasses that + // override this method should call this superclass method as well. + + // We need to destroy the loader before the derived Process class gets + // destroyed since it is very likely that undoing the loader will require + // access to the real process. + m_dynamic_checkers_up.reset(); + m_abi_sp.reset(); + m_os_up.reset(); + m_system_runtime_up.reset(); + m_dyld_up.reset(); + m_jit_loaders_up.reset(); + m_thread_plans.Clear(); + m_thread_list_real.Destroy(); + m_thread_list.Destroy(); + m_extended_thread_list.Destroy(); + m_queue_list.Clear(); + m_queue_list_stop_id = 0; + std::vector empty_notifications; + m_notifications.swap(empty_notifications); + m_image_tokens.clear(); + m_memory_cache.Clear(); + m_allocated_memory_cache.Clear(); + { + std::lock_guard guard(m_language_runtimes_mutex); + m_language_runtimes.clear(); + } + m_instrumentation_runtimes.clear(); + m_next_event_action_up.reset(); + // Clear the last natural stop ID since it has a strong reference to this + // process + m_mod_id.SetStopEventForLastNaturalStopID(EventSP()); + //#ifdef LLDB_CONFIGURATION_DEBUG + // StreamFile s(stdout, false); + // EventSP event_sp; + // while (m_private_state_listener_sp->GetNextEvent(event_sp)) + // { + // event_sp->Dump (&s); + // s.EOL(); + // } + //#endif + // We have to be very careful here as the m_private_state_listener might + // contain events that have ProcessSP values in them which can keep this + // process around forever. These events need to be cleared out. + m_private_state_listener_sp->Clear(); + m_public_run_lock.TrySetRunning(); // This will do nothing if already locked + m_public_run_lock.SetStopped(); + m_private_run_lock.TrySetRunning(); // This will do nothing if already locked + m_private_run_lock.SetStopped(); + m_structured_data_plugin_map.clear(); + } + + void Process::RegisterNotificationCallbacks(const Notifications &callbacks) { + m_notifications.push_back(callbacks); + if (callbacks.initialize != nullptr) + callbacks.initialize(callbacks.baton, this); + } + + bool Process::UnregisterNotificationCallbacks(const Notifications &callbacks) { + std::vector::iterator pos, end = m_notifications.end(); + for (pos = m_notifications.begin(); pos != end; ++pos) { + if (pos->baton == callbacks.baton && + pos->initialize == callbacks.initialize && + pos->process_state_changed == callbacks.process_state_changed) { + m_notifications.erase(pos); + return true; + } + } + return false; + } + + void Process::SynchronouslyNotifyStateChanged(StateType state) { + std::vector::iterator notification_pos, + notification_end = m_notifications.end(); + for (notification_pos = m_notifications.begin(); + notification_pos != notification_end; ++notification_pos) { + if (notification_pos->process_state_changed) + notification_pos->process_state_changed(notification_pos->baton, this, + state); + } + } + + // FIXME: We need to do some work on events before the general Listener sees + // them. + // For instance if we are continuing from a breakpoint, we need to ensure that + // we do the little "insert real insn, step & stop" trick. But we can't do + // that when the event is delivered by the broadcaster - since that is done on + // the thread that is waiting for new events, so if we needed more than one + // event for our handling, we would stall. So instead we do it when we fetch + // the event off of the queue. + // + + StateType Process::GetNextEvent(EventSP &event_sp) { + StateType state = eStateInvalid; + + if (m_listener_sp->GetEventForBroadcaster(this, event_sp, + std::chrono::seconds(0)) && + event_sp) + state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + + return state; + } + + void Process::SyncIOHandler(uint32_t iohandler_id, + const Timeout &timeout) { + // don't sync (potentially context switch) in case where there is no process + // IO + if (!m_process_input_reader) + return; + + auto Result = m_iohandler_sync.WaitForValueNotEqualTo(iohandler_id, timeout); + + Log *log = GetLog(LLDBLog::Process); + if (Result) { + LLDB_LOG( + log, + "waited from m_iohandler_sync to change from {0}. New value is {1}.", + iohandler_id, *Result); + } else { + LLDB_LOG(log, "timed out waiting for m_iohandler_sync to change from {0}.", + iohandler_id); + } + } + + StateType Process::WaitForProcessToStop(const Timeout &timeout, + EventSP *event_sp_ptr, bool wait_always, + ListenerSP hijack_listener_sp, + Stream *stream, bool use_run_lock) { + // We can't just wait for a "stopped" event, because the stopped event may + // have restarted the target. We have to actually check each event, and in + // the case of a stopped event check the restarted flag on the event. + if (event_sp_ptr) + event_sp_ptr->reset(); + StateType state = GetState(); + // If we are exited or detached, we won't ever get back to any other valid + // state... + if (state == eStateDetached || state == eStateExited) + return state; + + Log *log = GetLog(LLDBLog::Process); + LLDB_LOG(log, "timeout = {0}", timeout); + + if (!wait_always && StateIsStoppedState(state, true) && + StateIsStoppedState(GetPrivateState(), true)) { + LLDB_LOGF(log, + "Process::%s returning without waiting for events; process " + "private and public states are already 'stopped'.", + __FUNCTION__); + // We need to toggle the run lock as this won't get done in + // SetPublicState() if the process is hijacked. + if (hijack_listener_sp && use_run_lock) + m_public_run_lock.SetStopped(); + return state; + } + + while (state != eStateInvalid) { + EventSP event_sp; + state = GetStateChangedEvents(event_sp, timeout, hijack_listener_sp); + if (event_sp_ptr && event_sp) + *event_sp_ptr = event_sp; + + bool pop_process_io_handler = (hijack_listener_sp.get() != nullptr); + Process::HandleProcessStateChangedEvent(event_sp, stream, + pop_process_io_handler); + + switch (state) { + case eStateCrashed: + case eStateDetached: + case eStateExited: + case eStateUnloaded: + // We need to toggle the run lock as this won't get done in + // SetPublicState() if the process is hijacked. + if (hijack_listener_sp && use_run_lock) + m_public_run_lock.SetStopped(); + return state; + case eStateStopped: + if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) + continue; + else { + // We need to toggle the run lock as this won't get done in + // SetPublicState() if the process is hijacked. + if (hijack_listener_sp && use_run_lock) + m_public_run_lock.SetStopped(); + return state; + } + default: + continue; + } + } + return state; + } + + bool Process::HandleProcessStateChangedEvent(const EventSP &event_sp, + Stream *stream, + bool &pop_process_io_handler) { + const bool handle_pop = pop_process_io_handler; + + pop_process_io_handler = false; + ProcessSP process_sp = + Process::ProcessEventData::GetProcessFromEvent(event_sp.get()); + + if (!process_sp) + return false; + + StateType event_state = + Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + if (event_state == eStateInvalid) + return false; + + switch (event_state) { + case eStateInvalid: + case eStateUnloaded: + case eStateAttaching: + case eStateLaunching: + case eStateStepping: + case eStateDetached: + if (stream) + stream->Printf("Process %" PRIu64 " %s\n", process_sp->GetID(), + StateAsCString(event_state)); + if (event_state == eStateDetached) + pop_process_io_handler = true; + break; + + case eStateConnected: + case eStateRunning: + // Don't be chatty when we run... + break; + + case eStateExited: + if (stream) + process_sp->GetStatus(*stream); + pop_process_io_handler = true; + break; + + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + // Make sure the program hasn't been auto-restarted: + if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) { + if (stream) { + size_t num_reasons = + Process::ProcessEventData::GetNumRestartedReasons(event_sp.get()); + if (num_reasons > 0) { + // FIXME: Do we want to report this, or would that just be annoyingly + // chatty? + if (num_reasons == 1) { + const char *reason = + Process::ProcessEventData::GetRestartedReasonAtIndex( + event_sp.get(), 0); + stream->Printf("Process %" PRIu64 " stopped and restarted: %s\n", + process_sp->GetID(), + reason ? reason : ""); + } else { + stream->Printf("Process %" PRIu64 + " stopped and restarted, reasons:\n", + process_sp->GetID()); + + for (size_t i = 0; i < num_reasons; i++) { + const char *reason = + Process::ProcessEventData::GetRestartedReasonAtIndex( + event_sp.get(), i); + stream->Printf("\t%s\n", reason ? reason : ""); + } + } + } + } + } else { + StopInfoSP curr_thread_stop_info_sp; + // Lock the thread list so it doesn't change on us, this is the scope for + // the locker: + { + ThreadList &thread_list = process_sp->GetThreadList(); + std::lock_guard guard(thread_list.GetMutex()); + + ThreadSP curr_thread(thread_list.GetSelectedThread()); + ThreadSP thread; + StopReason curr_thread_stop_reason = eStopReasonInvalid; + bool prefer_curr_thread = false; + if (curr_thread && curr_thread->IsValid()) { + curr_thread_stop_reason = curr_thread->GetStopReason(); + switch (curr_thread_stop_reason) { + case eStopReasonNone: + case eStopReasonInvalid: + // Don't prefer the current thread if it didn't stop for a reason. + break; + case eStopReasonSignal: { + // We need to do the same computation we do for other threads + // below in case the current thread happens to be the one that + // stopped for the no-stop signal. + uint64_t signo = curr_thread->GetStopInfo()->GetValue(); + if (process_sp->GetUnixSignals()->GetShouldStop(signo)) + prefer_curr_thread = true; + } break; + default: + prefer_curr_thread = true; + break; + } + curr_thread_stop_info_sp = curr_thread->GetStopInfo(); + } + + if (!prefer_curr_thread) { + // Prefer a thread that has just completed its plan over another + // thread as current thread. + ThreadSP plan_thread; + ThreadSP other_thread; + + const size_t num_threads = thread_list.GetSize(); + size_t i; + for (i = 0; i < num_threads; ++i) { + thread = thread_list.GetThreadAtIndex(i); + StopReason thread_stop_reason = thread->GetStopReason(); + switch (thread_stop_reason) { + case eStopReasonInvalid: + case eStopReasonNone: + break; + + case eStopReasonSignal: { + // Don't select a signal thread if we weren't going to stop at + // that signal. We have to have had another reason for stopping + // here, and the user doesn't want to see this thread. + uint64_t signo = thread->GetStopInfo()->GetValue(); + if (process_sp->GetUnixSignals()->GetShouldStop(signo)) { + if (!other_thread) + other_thread = thread; + } + break; + } + case eStopReasonTrace: + case eStopReasonBreakpoint: + case eStopReasonWatchpoint: + case eStopReasonException: + case eStopReasonExec: + case eStopReasonFork: + case eStopReasonVFork: + case eStopReasonVForkDone: + case eStopReasonThreadExiting: + case eStopReasonInstrumentation: + case eStopReasonProcessorTrace: + if (!other_thread) + other_thread = thread; + break; + case eStopReasonPlanComplete: + if (!plan_thread) + plan_thread = thread; + break; + } + } + if (plan_thread) + thread_list.SetSelectedThreadByID(plan_thread->GetID()); + else if (other_thread) + thread_list.SetSelectedThreadByID(other_thread->GetID()); + else { + if (curr_thread && curr_thread->IsValid()) + thread = curr_thread; + else + thread = thread_list.GetThreadAtIndex(0); + + if (thread) + thread_list.SetSelectedThreadByID(thread->GetID()); + } + } + } + // Drop the ThreadList mutex by here, since GetThreadStatus below might + // have to run code, e.g. for Data formatters, and if we hold the + // ThreadList mutex, then the process is going to have a hard time + // restarting the process. + if (stream) { + Debugger &debugger = process_sp->GetTarget().GetDebugger(); + if (debugger.GetTargetList().GetSelectedTarget().get() == + &process_sp->GetTarget()) { + ThreadSP thread_sp = process_sp->GetThreadList().GetSelectedThread(); + + if (!thread_sp || !thread_sp->IsValid()) + return false; + + const bool only_threads_with_stop_reason = true; + const uint32_t start_frame = thread_sp->GetSelectedFrameIndex(); + const uint32_t num_frames = 1; + const uint32_t num_frames_with_source = 1; + const bool stop_format = true; + + process_sp->GetStatus(*stream); + process_sp->GetThreadStatus(*stream, only_threads_with_stop_reason, + start_frame, num_frames, + num_frames_with_source, + stop_format); + if (curr_thread_stop_info_sp) { + lldb::addr_t crashing_address; + ValueObjectSP valobj_sp = StopInfo::GetCrashingDereference( + curr_thread_stop_info_sp, &crashing_address); + if (valobj_sp) { + const ValueObject::GetExpressionPathFormat format = + ValueObject::GetExpressionPathFormat:: + eGetExpressionPathFormatHonorPointers; + stream->PutCString("Likely cause: "); + valobj_sp->GetExpressionPath(*stream, format); + stream->Printf(" accessed 0x%" PRIx64 "\n", crashing_address); + } + } + } else { + uint32_t target_idx = debugger.GetTargetList().GetIndexOfTarget( + process_sp->GetTarget().shared_from_this()); + if (target_idx != UINT32_MAX) + stream->Printf("Target %d: (", target_idx); + else + stream->Printf("Target : ("); + process_sp->GetTarget().Dump(stream, eDescriptionLevelBrief); + stream->Printf(") stopped.\n"); + } + } + + // Pop the process IO handler + pop_process_io_handler = true; + } + break; + } + + if (handle_pop && pop_process_io_handler) + process_sp->PopProcessIOHandler(); + + return true; + } + + bool Process::HijackProcessEvents(ListenerSP listener_sp) { + if (listener_sp) { + return HijackBroadcaster(listener_sp, eBroadcastBitStateChanged | + eBroadcastBitInterrupt); + } else + return false; + } + + void Process::RestoreProcessEvents() { RestoreBroadcaster(); } + + StateType Process::GetStateChangedEvents(EventSP &event_sp, + const Timeout &timeout, + ListenerSP hijack_listener_sp) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOG(log, "timeout = {0}, event_sp)...", timeout); + + ListenerSP listener_sp = hijack_listener_sp; + if (!listener_sp) + listener_sp = m_listener_sp; + + StateType state = eStateInvalid; + if (listener_sp->GetEventForBroadcasterWithType( + this, eBroadcastBitStateChanged | eBroadcastBitInterrupt, event_sp, + timeout)) { + if (event_sp && event_sp->GetType() == eBroadcastBitStateChanged) + state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + else + LLDB_LOG(log, "got no event or was interrupted."); + } + + LLDB_LOG(log, "timeout = {0}, event_sp) => {1}", timeout, state); + return state; + } + + Event *Process::PeekAtStateChangedEvents() { + Log *log = GetLog(LLDBLog::Process); + + LLDB_LOGF(log, "Process::%s...", __FUNCTION__); + + Event *event_ptr; + event_ptr = m_listener_sp->PeekAtNextEventForBroadcasterWithType( + this, eBroadcastBitStateChanged); + if (log) { + if (event_ptr) { + LLDB_LOGF(log, "Process::%s (event_ptr) => %s", __FUNCTION__, + StateAsCString(ProcessEventData::GetStateFromEvent(event_ptr))); + } else { + LLDB_LOGF(log, "Process::%s no events found", __FUNCTION__); + } + } + return event_ptr; + } + + StateType + Process::GetStateChangedEventsPrivate(EventSP &event_sp, + const Timeout &timeout) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOG(log, "timeout = {0}, event_sp)...", timeout); + + StateType state = eStateInvalid; + if (m_private_state_listener_sp->GetEventForBroadcasterWithType( + &m_private_state_broadcaster, + eBroadcastBitStateChanged | eBroadcastBitInterrupt, event_sp, + timeout)) + if (event_sp && event_sp->GetType() == eBroadcastBitStateChanged) + state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + + LLDB_LOG(log, "timeout = {0}, event_sp) => {1}", timeout, + state == eStateInvalid ? "TIMEOUT" : StateAsCString(state)); + return state; + } + + bool Process::GetEventsPrivate(EventSP &event_sp, + const Timeout &timeout, + bool control_only) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOG(log, "timeout = {0}, event_sp)...", timeout); + + if (control_only) + return m_private_state_listener_sp->GetEventForBroadcaster( + &m_private_state_control_broadcaster, event_sp, timeout); + else + return m_private_state_listener_sp->GetEvent(event_sp, timeout); + } + + bool Process::IsRunning() const { + return StateIsRunningState(m_public_state.GetValue()); + } + + int Process::GetExitStatus() { + std::lock_guard guard(m_exit_status_mutex); + + if (m_public_state.GetValue() == eStateExited) + return m_exit_status; + return -1; + } + + const char *Process::GetExitDescription() { + std::lock_guard guard(m_exit_status_mutex); + + if (m_public_state.GetValue() == eStateExited && !m_exit_string.empty()) + return m_exit_string.c_str(); + return nullptr; + } + + bool Process::SetExitStatus(int status, const char *cstr) { + // Use a mutex to protect setting the exit status. + std::lock_guard guard(m_exit_status_mutex); + + Log *log(GetLog(LLDBLog::State | LLDBLog::Process)); + LLDB_LOGF( + log, "Process::SetExitStatus (status=%i (0x%8.8x), description=%s%s%s)", + status, status, cstr ? "\"" : "", cstr ? cstr : "NULL", cstr ? "\"" : ""); + + // We were already in the exited state + if (m_private_state.GetValue() == eStateExited) { + LLDB_LOGF(log, "Process::SetExitStatus () ignoring exit status because " + "state was already set to eStateExited"); + return false; + } + + m_exit_status = status; + if (cstr) + m_exit_string = cstr; + else + m_exit_string.clear(); + + // Clear the last natural stop ID since it has a strong reference to this + // process + m_mod_id.SetStopEventForLastNaturalStopID(EventSP()); + + SetPrivateState(eStateExited); + + // Allow subclasses to do some cleanup + DidExit(); + + return true; + } + + bool Process::IsAlive() { + switch (m_private_state.GetValue()) { + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateStopped: + case eStateRunning: + case eStateStepping: + case eStateCrashed: + case eStateSuspended: + return true; + default: + return false; + } + } + + // This static callback can be used to watch for local child processes on the + // current host. The child process exits, the process will be found in the + // global target list (we want to be completely sure that the + // lldb_private::Process doesn't go away before we can deliver the signal. + bool Process::SetProcessExitStatus( + lldb::pid_t pid, bool exited, + int signo, // Zero for no signal + int exit_status // Exit value of process if signal is zero + ) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, + "Process::SetProcessExitStatus (pid=%" PRIu64 + ", exited=%i, signal=%i, exit_status=%i)\n", + pid, exited, signo, exit_status); + + if (exited) { + TargetSP target_sp(Debugger::FindTargetWithProcessID(pid)); + if (target_sp) { + ProcessSP process_sp(target_sp->GetProcessSP()); + if (process_sp) { + const char *signal_cstr = nullptr; + if (signo) + signal_cstr = process_sp->GetUnixSignals()->GetSignalAsCString(signo); + + process_sp->SetExitStatus(exit_status, signal_cstr); + } + } + return true; + } + return false; + } + + bool Process::UpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) { + m_thread_plans.ClearThreadCache(); + return DoUpdateThreadList(old_thread_list, new_thread_list); + } + + void Process::UpdateThreadListIfNeeded() { + const uint32_t stop_id = GetStopID(); + if (m_thread_list.GetSize(false) == 0 || + stop_id != m_thread_list.GetStopID()) { + bool clear_unused_threads = true; + const StateType state = GetPrivateState(); + if (StateIsStoppedState(state, true)) { + std::lock_guard guard(m_thread_list.GetMutex()); + m_thread_list.SetStopID(stop_id); + + // m_thread_list does have its own mutex, but we need to hold onto the + // mutex between the call to UpdateThreadList(...) and the + // os->UpdateThreadList(...) so it doesn't change on us + ThreadList &old_thread_list = m_thread_list; + ThreadList real_thread_list(this); + ThreadList new_thread_list(this); + // Always update the thread list with the protocol specific thread list, + // but only update if "true" is returned + if (UpdateThreadList(m_thread_list_real, real_thread_list)) { + // Don't call into the OperatingSystem to update the thread list if we + // are shutting down, since that may call back into the SBAPI's, + // requiring the API lock which is already held by whoever is shutting + // us down, causing a deadlock. + OperatingSystem *os = GetOperatingSystem(); + if (os && !m_destroy_in_process) { + // Clear any old backing threads where memory threads might have been + // backed by actual threads from the lldb_private::Process subclass + size_t num_old_threads = old_thread_list.GetSize(false); + for (size_t i = 0; i < num_old_threads; ++i) + old_thread_list.GetThreadAtIndex(i, false)->ClearBackingThread(); + // See if the OS plugin reports all threads. If it does, then + // it is safe to clear unseen thread's plans here. Otherwise we + // should preserve them in case they show up again: + clear_unused_threads = GetOSPluginReportsAllThreads(); + + // Turn off dynamic types to ensure we don't run any expressions. + // Objective-C can run an expression to determine if a SBValue is a + // dynamic type or not and we need to avoid this. OperatingSystem + // plug-ins can't run expressions that require running code... + + Target &target = GetTarget(); + const lldb::DynamicValueType saved_prefer_dynamic = + target.GetPreferDynamicValue(); + if (saved_prefer_dynamic != lldb::eNoDynamicValues) + target.SetPreferDynamicValue(lldb::eNoDynamicValues); + + // Now let the OperatingSystem plug-in update the thread list + + os->UpdateThreadList( + old_thread_list, // Old list full of threads created by OS plug-in + real_thread_list, // The actual thread list full of threads + // created by each lldb_private::Process + // subclass + new_thread_list); // The new thread list that we will show to the + // user that gets filled in + + if (saved_prefer_dynamic != lldb::eNoDynamicValues) + target.SetPreferDynamicValue(saved_prefer_dynamic); + } else { + // No OS plug-in, the new thread list is the same as the real thread + // list. + new_thread_list = real_thread_list; + } + + m_thread_list_real.Update(real_thread_list); + m_thread_list.Update(new_thread_list); + m_thread_list.SetStopID(stop_id); + + if (GetLastNaturalStopID() != m_extended_thread_stop_id) { + // Clear any extended threads that we may have accumulated previously + m_extended_thread_list.Clear(); + m_extended_thread_stop_id = GetLastNaturalStopID(); + + m_queue_list.Clear(); + m_queue_list_stop_id = GetLastNaturalStopID(); + } + } + // Now update the plan stack map. + // If we do have an OS plugin, any absent real threads in the + // m_thread_list have already been removed from the ThreadPlanStackMap. + // So any remaining threads are OS Plugin threads, and those we want to + // preserve in case they show up again. + m_thread_plans.Update(m_thread_list, clear_unused_threads); + } + } + } + + ThreadPlanStack *Process::FindThreadPlans(lldb::tid_t tid) { + return m_thread_plans.Find(tid); + } + + bool Process::PruneThreadPlansForTID(lldb::tid_t tid) { + return m_thread_plans.PrunePlansForTID(tid); + } + + void Process::PruneThreadPlans() { + m_thread_plans.Update(GetThreadList(), true, false); + } + + bool Process::DumpThreadPlansForTID(Stream &strm, lldb::tid_t tid, + lldb::DescriptionLevel desc_level, + bool internal, bool condense_trivial, + bool skip_unreported_plans) { + return m_thread_plans.DumpPlansForTID( + strm, tid, desc_level, internal, condense_trivial, skip_unreported_plans); + } + void Process::DumpThreadPlans(Stream &strm, lldb::DescriptionLevel desc_level, + bool internal, bool condense_trivial, + bool skip_unreported_plans) { + m_thread_plans.DumpPlans(strm, desc_level, internal, condense_trivial, + skip_unreported_plans); + } + + void Process::UpdateQueueListIfNeeded() { + if (m_system_runtime_up) { + if (m_queue_list.GetSize() == 0 || + m_queue_list_stop_id != GetLastNaturalStopID()) { + const StateType state = GetPrivateState(); + if (StateIsStoppedState(state, true)) { + m_system_runtime_up->PopulateQueueList(m_queue_list); + m_queue_list_stop_id = GetLastNaturalStopID(); + } + } + } + } + + ThreadSP Process::CreateOSPluginThread(lldb::tid_t tid, lldb::addr_t context) { + OperatingSystem *os = GetOperatingSystem(); + if (os) + return os->CreateThread(tid, context); + return ThreadSP(); + } + + uint32_t Process::GetNextThreadIndexID(uint64_t thread_id) { + return AssignIndexIDToThread(thread_id); + } + + bool Process::HasAssignedIndexIDToThread(uint64_t thread_id) { + return (m_thread_id_to_index_id_map.find(thread_id) != + m_thread_id_to_index_id_map.end()); + } + + uint32_t Process::AssignIndexIDToThread(uint64_t thread_id) { + uint32_t result = 0; + std::map::iterator iterator = + m_thread_id_to_index_id_map.find(thread_id); + if (iterator == m_thread_id_to_index_id_map.end()) { + result = ++m_thread_index_id; + m_thread_id_to_index_id_map[thread_id] = result; + } else { + result = iterator->second; + } + + return result; + } + + StateType Process::GetState() { + return m_public_state.GetValue(); + } + + void Process::SetPublicState(StateType new_state, bool restarted) { + const bool new_state_is_stopped = StateIsStoppedState(new_state, false); + if (new_state_is_stopped) { + // This will only set the time if the public stop time has no value, so + // it is ok to call this multiple times. With a public stop we can't look + // at the stop ID because many private stops might have happened, so we + // can't check for a stop ID of zero. This allows the "statistics" command + // to dump the time it takes to reach somewhere in your code, like a + // breakpoint you set. + GetTarget().GetStatistics().SetFirstPublicStopTime(); + } + + Log *log(GetLog(LLDBLog::State | LLDBLog::Process)); + LLDB_LOGF(log, "Process::SetPublicState (state = %s, restarted = %i)", + StateAsCString(new_state), restarted); + const StateType old_state = m_public_state.GetValue(); + m_public_state.SetValue(new_state); + + // On the transition from Run to Stopped, we unlock the writer end of the run + // lock. The lock gets locked in Resume, which is the public API to tell the + // program to run. + if (!StateChangedIsExternallyHijacked()) { + if (new_state == eStateDetached) { + LLDB_LOGF(log, + "Process::SetPublicState (%s) -- unlocking run lock for detach", + StateAsCString(new_state)); + m_public_run_lock.SetStopped(); + } else { + const bool old_state_is_stopped = StateIsStoppedState(old_state, false); + if ((old_state_is_stopped != new_state_is_stopped)) { + if (new_state_is_stopped && !restarted) { + LLDB_LOGF(log, "Process::SetPublicState (%s) -- unlocking run lock", + StateAsCString(new_state)); + m_public_run_lock.SetStopped(); + } + } + } + } + } + + Status Process::Resume() { + Log *log(GetLog(LLDBLog::State | LLDBLog::Process)); + LLDB_LOGF(log, "Process::Resume -- locking run lock"); + if (!m_public_run_lock.TrySetRunning()) { + Status error("Resume request failed - process still running."); + LLDB_LOGF(log, "Process::Resume: -- TrySetRunning failed, not resuming."); + return error; + } + Status error = PrivateResume(); + if (!error.Success()) { + // Undo running state change + m_public_run_lock.SetStopped(); + } + return error; + } + + static const char *g_resume_sync_name = "lldb.Process.ResumeSynchronous.hijack"; + + Status Process::ResumeSynchronous(Stream *stream) { + Log *log(GetLog(LLDBLog::State | LLDBLog::Process)); + LLDB_LOGF(log, "Process::ResumeSynchronous -- locking run lock"); + if (!m_public_run_lock.TrySetRunning()) { + Status error("Resume request failed - process still running."); + LLDB_LOGF(log, "Process::Resume: -- TrySetRunning failed, not resuming."); + return error; + } + + ListenerSP listener_sp( + Listener::MakeListener(g_resume_sync_name)); + HijackProcessEvents(listener_sp); + + Status error = PrivateResume(); + if (error.Success()) { + StateType state = WaitForProcessToStop(llvm::None, nullptr, true, + listener_sp, stream); + const bool must_be_alive = + false; // eStateExited is ok, so this must be false + if (!StateIsStoppedState(state, must_be_alive)) + error.SetErrorStringWithFormat( + "process not in stopped state after synchronous resume: %s", + StateAsCString(state)); + } else { + // Undo running state change + m_public_run_lock.SetStopped(); + } + + // Undo the hijacking of process events... + RestoreProcessEvents(); + + return error; + } + + bool Process::StateChangedIsExternallyHijacked() { + if (IsHijackedForEvent(eBroadcastBitStateChanged)) { + const char *hijacking_name = GetHijackingListenerName(); + if (hijacking_name && + strcmp(hijacking_name, g_resume_sync_name)) + return true; + } + return false; + } + + bool Process::StateChangedIsHijackedForSynchronousResume() { + if (IsHijackedForEvent(eBroadcastBitStateChanged)) { + const char *hijacking_name = GetHijackingListenerName(); + if (hijacking_name && + strcmp(hijacking_name, g_resume_sync_name) == 0) + return true; + } + return false; + } + + StateType Process::GetPrivateState() { return m_private_state.GetValue(); } + + void Process::SetPrivateState(StateType new_state) { + if (m_finalizing) + return; + + Log *log(GetLog(LLDBLog::State | LLDBLog::Process | LLDBLog::Unwind)); + bool state_changed = false; + + LLDB_LOGF(log, "Process::SetPrivateState (%s)", StateAsCString(new_state)); + + std::lock_guard thread_guard(m_thread_list.GetMutex()); + std::lock_guard guard(m_private_state.GetMutex()); + + const StateType old_state = m_private_state.GetValueNoLock(); + state_changed = old_state != new_state; + + const bool old_state_is_stopped = StateIsStoppedState(old_state, false); + const bool new_state_is_stopped = StateIsStoppedState(new_state, false); + if (old_state_is_stopped != new_state_is_stopped) { + if (new_state_is_stopped) + m_private_run_lock.SetStopped(); + else + m_private_run_lock.SetRunning(); + } + + if (state_changed) { + m_private_state.SetValueNoLock(new_state); + EventSP event_sp( + new Event(eBroadcastBitStateChanged, + new ProcessEventData(shared_from_this(), new_state))); + if (StateIsStoppedState(new_state, false)) { + // Note, this currently assumes that all threads in the list stop when + // the process stops. In the future we will want to support a debugging + // model where some threads continue to run while others are stopped. + // When that happens we will either need a way for the thread list to + // identify which threads are stopping or create a special thread list + // containing only threads which actually stopped. + // + // The process plugin is responsible for managing the actual behavior of + // the threads and should have stopped any threads that are going to stop + // before we get here. + m_thread_list.DidStop(); + + if (m_mod_id.BumpStopID() == 0) + GetTarget().GetStatistics().SetFirstPrivateStopTime(); + + if (!m_mod_id.IsLastResumeForUserExpression()) + m_mod_id.SetStopEventForLastNaturalStopID(event_sp); + m_memory_cache.Clear(); + LLDB_LOGF(log, "Process::SetPrivateState (%s) stop_id = %u", + StateAsCString(new_state), m_mod_id.GetStopID()); + } + + m_private_state_broadcaster.BroadcastEvent(event_sp); + } else { + LLDB_LOGF(log, + "Process::SetPrivateState (%s) state didn't change. Ignoring...", + StateAsCString(new_state)); + } + } + + void Process::SetRunningUserExpression(bool on) { + m_mod_id.SetRunningUserExpression(on); + } + + void Process::SetRunningUtilityFunction(bool on) { + m_mod_id.SetRunningUtilityFunction(on); + } + + addr_t Process::GetImageInfoAddress() { return LLDB_INVALID_ADDRESS; } + + const lldb::ABISP &Process::GetABI() { + if (!m_abi_sp) + m_abi_sp = ABI::FindPlugin(shared_from_this(), GetTarget().GetArchitecture()); + return m_abi_sp; + } + + std::vector Process::GetLanguageRuntimes() { + std::vector language_runtimes; + + if (m_finalizing) + return language_runtimes; + + std::lock_guard guard(m_language_runtimes_mutex); + // Before we pass off a copy of the language runtimes, we must make sure that + // our collection is properly populated. It's possible that some of the + // language runtimes were not loaded yet, either because nobody requested it + // yet or the proper condition for loading wasn't yet met (e.g. libc++.so + // hadn't been loaded). + for (const lldb::LanguageType lang_type : Language::GetSupportedLanguages()) { + if (LanguageRuntime *runtime = GetLanguageRuntime(lang_type)) + language_runtimes.emplace_back(runtime); + } + + return language_runtimes; + } + + LanguageRuntime *Process::GetLanguageRuntime(lldb::LanguageType language) { + if (m_finalizing) + return nullptr; + + LanguageRuntime *runtime = nullptr; + + std::lock_guard guard(m_language_runtimes_mutex); + LanguageRuntimeCollection::iterator pos; + pos = m_language_runtimes.find(language); + if (pos == m_language_runtimes.end() || !pos->second) { + lldb::LanguageRuntimeSP runtime_sp( + LanguageRuntime::FindPlugin(this, language)); + + m_language_runtimes[language] = runtime_sp; + runtime = runtime_sp.get(); + } else + runtime = pos->second.get(); + + if (runtime) + // It's possible that a language runtime can support multiple LanguageTypes, + // for example, CPPLanguageRuntime will support eLanguageTypeC_plus_plus, + // eLanguageTypeC_plus_plus_03, etc. Because of this, we should get the + // primary language type and make sure that our runtime supports it. + assert(runtime->GetLanguageType() == Language::GetPrimaryLanguage(language)); + + return runtime; + } + + bool Process::IsPossibleDynamicValue(ValueObject &in_value) { + if (m_finalizing) + return false; + + if (in_value.IsDynamic()) + return false; + LanguageType known_type = in_value.GetObjectRuntimeLanguage(); + + if (known_type != eLanguageTypeUnknown && known_type != eLanguageTypeC) { + LanguageRuntime *runtime = GetLanguageRuntime(known_type); + return runtime ? runtime->CouldHaveDynamicValue(in_value) : false; + } + + for (LanguageRuntime *runtime : GetLanguageRuntimes()) { + if (runtime->CouldHaveDynamicValue(in_value)) + return true; + } + + return false; + } + + void Process::SetDynamicCheckers(DynamicCheckerFunctions *dynamic_checkers) { + m_dynamic_checkers_up.reset(dynamic_checkers); + } + + BreakpointSiteList &Process::GetBreakpointSiteList() { + return m_breakpoint_site_list; + } + + const BreakpointSiteList &Process::GetBreakpointSiteList() const { + return m_breakpoint_site_list; + } + + void Process::DisableAllBreakpointSites() { + m_breakpoint_site_list.ForEach([this](BreakpointSite *bp_site) -> void { + // bp_site->SetEnabled(true); + DisableBreakpointSite(bp_site); + }); + } + + Status Process::ClearBreakpointSiteByID(lldb::user_id_t break_id) { + Status error(DisableBreakpointSiteByID(break_id)); + + if (error.Success()) + m_breakpoint_site_list.Remove(break_id); + + return error; + } + + Status Process::DisableBreakpointSiteByID(lldb::user_id_t break_id) { + Status error; + BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID(break_id); + if (bp_site_sp) { + if (bp_site_sp->IsEnabled()) + error = DisableBreakpointSite(bp_site_sp.get()); + } else { + error.SetErrorStringWithFormat("invalid breakpoint site ID: %" PRIu64, + break_id); + } + + return error; + } + + Status Process::EnableBreakpointSiteByID(lldb::user_id_t break_id) { + Status error; + BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID(break_id); + if (bp_site_sp) { + if (!bp_site_sp->IsEnabled()) + error = EnableBreakpointSite(bp_site_sp.get()); + } else { + error.SetErrorStringWithFormat("invalid breakpoint site ID: %" PRIu64, + break_id); + } + return error; + } + + lldb::break_id_t + Process::CreateBreakpointSite(const BreakpointLocationSP &owner, + bool use_hardware) { + addr_t load_addr = LLDB_INVALID_ADDRESS; + + bool show_error = true; + switch (GetState()) { + case eStateInvalid: + case eStateUnloaded: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateDetached: + case eStateExited: + show_error = false; + break; + + case eStateStopped: + case eStateRunning: + case eStateStepping: + case eStateCrashed: + case eStateSuspended: + show_error = IsAlive(); + break; + } + + // Reset the IsIndirect flag here, in case the location changes from pointing + // to a indirect symbol to a regular symbol. + owner->SetIsIndirect(false); + + if (owner->ShouldResolveIndirectFunctions()) { + Symbol *symbol = owner->GetAddress().CalculateSymbolContextSymbol(); + if (symbol && symbol->IsIndirect()) { + Status error; + Address symbol_address = symbol->GetAddress(); + load_addr = ResolveIndirectFunction(&symbol_address, error); + if (!error.Success() && show_error) { + GetTarget().GetDebugger().GetErrorStream().Printf( + "warning: failed to resolve indirect function at 0x%" PRIx64 + " for breakpoint %i.%i: %s\n", + symbol->GetLoadAddress(&GetTarget()), + owner->GetBreakpoint().GetID(), owner->GetID(), + error.AsCString() ? error.AsCString() : "unknown error"); + return LLDB_INVALID_BREAK_ID; + } + Address resolved_address(load_addr); + load_addr = resolved_address.GetOpcodeLoadAddress(&GetTarget()); + owner->SetIsIndirect(true); + } else + load_addr = owner->GetAddress().GetOpcodeLoadAddress(&GetTarget()); + } else + load_addr = owner->GetAddress().GetOpcodeLoadAddress(&GetTarget()); + + if (load_addr != LLDB_INVALID_ADDRESS) { + BreakpointSiteSP bp_site_sp; + + // Look up this breakpoint site. If it exists, then add this new owner, + // otherwise create a new breakpoint site and add it. + + bp_site_sp = m_breakpoint_site_list.FindByAddress(load_addr); + + if (bp_site_sp) { + bp_site_sp->AddOwner(owner); + owner->SetBreakpointSite(bp_site_sp); + return bp_site_sp->GetID(); + } else { + bp_site_sp.reset(new BreakpointSite(&m_breakpoint_site_list, owner, + load_addr, use_hardware)); + if (bp_site_sp) { + Status error = EnableBreakpointSite(bp_site_sp.get()); + if (error.Success()) { + owner->SetBreakpointSite(bp_site_sp); + return m_breakpoint_site_list.Add(bp_site_sp); + } else { + if (show_error || use_hardware) { + // Report error for setting breakpoint... + GetTarget().GetDebugger().GetErrorStream().Printf( + "warning: failed to set breakpoint site at 0x%" PRIx64 + " for breakpoint %i.%i: %s\n", + load_addr, owner->GetBreakpoint().GetID(), owner->GetID(), + error.AsCString() ? error.AsCString() : "unknown error"); + } + } + } + } + } + // We failed to enable the breakpoint + return LLDB_INVALID_BREAK_ID; + } + + void Process::RemoveOwnerFromBreakpointSite(lldb::user_id_t owner_id, + lldb::user_id_t owner_loc_id, + BreakpointSiteSP &bp_site_sp) { + uint32_t num_owners = bp_site_sp->RemoveOwner(owner_id, owner_loc_id); + if (num_owners == 0) { + // Don't try to disable the site if we don't have a live process anymore. + if (IsAlive()) + DisableBreakpointSite(bp_site_sp.get()); + m_breakpoint_site_list.RemoveByAddress(bp_site_sp->GetLoadAddress()); + } + } + + size_t Process::RemoveBreakpointOpcodesFromBuffer(addr_t bp_addr, size_t size, + uint8_t *buf) const { + size_t bytes_removed = 0; + BreakpointSiteList bp_sites_in_range; + + if (m_breakpoint_site_list.FindInRange(bp_addr, bp_addr + size, + bp_sites_in_range)) { + bp_sites_in_range.ForEach([bp_addr, size, + buf](BreakpointSite *bp_site) -> void { + if (bp_site->GetType() == BreakpointSite::eSoftware) { + addr_t intersect_addr; + size_t intersect_size; + size_t opcode_offset; + if (bp_site->IntersectsRange(bp_addr, size, &intersect_addr, + &intersect_size, &opcode_offset)) { + assert(bp_addr <= intersect_addr && intersect_addr < bp_addr + size); + assert(bp_addr < intersect_addr + intersect_size && + intersect_addr + intersect_size <= bp_addr + size); + assert(opcode_offset + intersect_size <= bp_site->GetByteSize()); + size_t buf_offset = intersect_addr - bp_addr; + ::memcpy(buf + buf_offset, + bp_site->GetSavedOpcodeBytes() + opcode_offset, + intersect_size); + } + } + }); + } + return bytes_removed; + } + + size_t Process::GetSoftwareBreakpointTrapOpcode(BreakpointSite *bp_site) { + PlatformSP platform_sp(GetTarget().GetPlatform()); + if (platform_sp) + return platform_sp->GetSoftwareBreakpointTrapOpcode(GetTarget(), bp_site); + return 0; + } + + Status Process::EnableSoftwareBreakpoint(BreakpointSite *bp_site) { + Status error; + assert(bp_site != nullptr); + Log *log = GetLog(LLDBLog::Breakpoints); + const addr_t bp_addr = bp_site->GetLoadAddress(); + LLDB_LOGF( + log, "Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64, + bp_site->GetID(), (uint64_t)bp_addr); + if (bp_site->IsEnabled()) { + LLDB_LOGF( + log, + "Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 + " -- already enabled", + bp_site->GetID(), (uint64_t)bp_addr); + return error; + } + + if (bp_addr == LLDB_INVALID_ADDRESS) { + error.SetErrorString("BreakpointSite contains an invalid load address."); + return error; + } + // Ask the lldb::Process subclass to fill in the correct software breakpoint + // trap for the breakpoint site + const size_t bp_opcode_size = GetSoftwareBreakpointTrapOpcode(bp_site); + + if (bp_opcode_size == 0) { + error.SetErrorStringWithFormat("Process::GetSoftwareBreakpointTrapOpcode() " + "returned zero, unable to get breakpoint " + "trap for address 0x%" PRIx64, + bp_addr); + } else { + const uint8_t *const bp_opcode_bytes = bp_site->GetTrapOpcodeBytes(); + + if (bp_opcode_bytes == nullptr) { + error.SetErrorString( + "BreakpointSite doesn't contain a valid breakpoint trap opcode."); + return error; + } + + // Save the original opcode by reading it + if (DoReadMemory(bp_addr, bp_site->GetSavedOpcodeBytes(), bp_opcode_size, + error) == bp_opcode_size) { + // Write a software breakpoint in place of the original opcode + if (DoWriteMemory(bp_addr, bp_opcode_bytes, bp_opcode_size, error) == + bp_opcode_size) { + uint8_t verify_bp_opcode_bytes[64]; + if (DoReadMemory(bp_addr, verify_bp_opcode_bytes, bp_opcode_size, + error) == bp_opcode_size) { + if (::memcmp(bp_opcode_bytes, verify_bp_opcode_bytes, + bp_opcode_size) == 0) { + bp_site->SetEnabled(true); + bp_site->SetType(BreakpointSite::eSoftware); + LLDB_LOGF(log, + "Process::EnableSoftwareBreakpoint (site_id = %d) " + "addr = 0x%" PRIx64 " -- SUCCESS", + bp_site->GetID(), (uint64_t)bp_addr); + } else + error.SetErrorString( + "failed to verify the breakpoint trap in memory."); + } else + error.SetErrorString( + "Unable to read memory to verify breakpoint trap."); + } else + error.SetErrorString("Unable to write breakpoint trap to memory."); + } else + error.SetErrorString("Unable to read memory at breakpoint address."); + } + if (log && error.Fail()) + LLDB_LOGF( + log, + "Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 + " -- FAILED: %s", + bp_site->GetID(), (uint64_t)bp_addr, error.AsCString()); + return error; + } + + Status Process::DisableSoftwareBreakpoint(BreakpointSite *bp_site) { + Status error; + assert(bp_site != nullptr); + Log *log = GetLog(LLDBLog::Breakpoints); + addr_t bp_addr = bp_site->GetLoadAddress(); + lldb::user_id_t breakID = bp_site->GetID(); + LLDB_LOGF(log, + "Process::DisableSoftwareBreakpoint (breakID = %" PRIu64 + ") addr = 0x%" PRIx64, + breakID, (uint64_t)bp_addr); + + if (bp_site->IsHardware()) { + error.SetErrorString("Breakpoint site is a hardware breakpoint."); + } else if (bp_site->IsEnabled()) { + const size_t break_op_size = bp_site->GetByteSize(); + const uint8_t *const break_op = bp_site->GetTrapOpcodeBytes(); + if (break_op_size > 0) { + // Clear a software breakpoint instruction + uint8_t curr_break_op[8]; + assert(break_op_size <= sizeof(curr_break_op)); + bool break_op_found = false; + + // Read the breakpoint opcode + if (DoReadMemory(bp_addr, curr_break_op, break_op_size, error) == + break_op_size) { + bool verify = false; + // Make sure the breakpoint opcode exists at this address + if (::memcmp(curr_break_op, break_op, break_op_size) == 0) { + break_op_found = true; + // We found a valid breakpoint opcode at this address, now restore + // the saved opcode. + if (DoWriteMemory(bp_addr, bp_site->GetSavedOpcodeBytes(), + break_op_size, error) == break_op_size) { + verify = true; + } else + error.SetErrorString( + "Memory write failed when restoring original opcode."); + } else { + error.SetErrorString( + "Original breakpoint trap is no longer in memory."); + // Set verify to true and so we can check if the original opcode has + // already been restored + verify = true; + } + + if (verify) { + uint8_t verify_opcode[8]; + assert(break_op_size < sizeof(verify_opcode)); + // Verify that our original opcode made it back to the inferior + if (DoReadMemory(bp_addr, verify_opcode, break_op_size, error) == + break_op_size) { + // compare the memory we just read with the original opcode + if (::memcmp(bp_site->GetSavedOpcodeBytes(), verify_opcode, + break_op_size) == 0) { + // SUCCESS + bp_site->SetEnabled(false); + LLDB_LOGF(log, + "Process::DisableSoftwareBreakpoint (site_id = %d) " + "addr = 0x%" PRIx64 " -- SUCCESS", + bp_site->GetID(), (uint64_t)bp_addr); + return error; + } else { + if (break_op_found) + error.SetErrorString("Failed to restore original opcode."); + } + } else + error.SetErrorString("Failed to read memory to verify that " + "breakpoint trap was restored."); + } + } else + error.SetErrorString( + "Unable to read memory that should contain the breakpoint trap."); + } + } else { + LLDB_LOGF( + log, + "Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 + " -- already disabled", + bp_site->GetID(), (uint64_t)bp_addr); + return error; + } + + LLDB_LOGF( + log, + "Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 + " -- FAILED: %s", + bp_site->GetID(), (uint64_t)bp_addr, error.AsCString()); + return error; + } + + // Uncomment to verify memory caching works after making changes to caching + // code + //#define VERIFY_MEMORY_READS + + size_t Process::ReadMemory(addr_t addr, void *buf, size_t size, Status &error) { + error.Clear(); + if (!GetDisableMemoryCache()) { + #if defined(VERIFY_MEMORY_READS) + // Memory caching is enabled, with debug verification + + if (buf && size) { + // Uncomment the line below to make sure memory caching is working. + // I ran this through the test suite and got no assertions, so I am + // pretty confident this is working well. If any changes are made to + // memory caching, uncomment the line below and test your changes! + + // Verify all memory reads by using the cache first, then redundantly + // reading the same memory from the inferior and comparing to make sure + // everything is exactly the same. + std::string verify_buf(size, '\0'); + assert(verify_buf.size() == size); + const size_t cache_bytes_read = + m_memory_cache.Read(this, addr, buf, size, error); + Status verify_error; + const size_t verify_bytes_read = + ReadMemoryFromInferior(addr, const_cast(verify_buf.data()), + verify_buf.size(), verify_error); + assert(cache_bytes_read == verify_bytes_read); + assert(memcmp(buf, verify_buf.data(), verify_buf.size()) == 0); + assert(verify_error.Success() == error.Success()); + return cache_bytes_read; + } + return 0; + #else // !defined(VERIFY_MEMORY_READS) + // Memory caching is enabled, without debug verification + + return m_memory_cache.Read(addr, buf, size, error); + #endif // defined (VERIFY_MEMORY_READS) + } else { + // Memory caching is disabled + + return ReadMemoryFromInferior(addr, buf, size, error); + } + } + + size_t Process::ReadCStringFromMemory(addr_t addr, std::string &out_str, + Status &error) { + char buf[256]; + out_str.clear(); + addr_t curr_addr = addr; + while (true) { + size_t length = ReadCStringFromMemory(curr_addr, buf, sizeof(buf), error); + 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; + } + return out_str.size(); + } + + // Deprecated in favor of ReadStringFromMemory which has wchar support and + // correct code to find null terminators. + size_t Process::ReadCStringFromMemory(addr_t addr, char *dst, + size_t dst_max_len, + Status &result_error) { + 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); + Status error; + addr_t curr_addr = addr; + const size_t cache_line_size = m_memory_cache.GetMemoryCacheLineSize(); + 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(bytes_left, cache_line_bytes_left); + size_t bytes_read = ReadMemory(curr_addr, curr_dst, bytes_to_read, error); + + if (bytes_read == 0) { + result_error = 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; + } + } else { + if (dst == nullptr) + result_error.SetErrorString("invalid arguments"); + else + result_error.Clear(); + } + return total_cstr_len; + } + + size_t Process::ReadMemoryFromInferior(addr_t addr, void *buf, size_t size, + Status &error) { + LLDB_SCOPED_TIMER(); + + if (buf == nullptr || size == 0) + return 0; + + size_t bytes_read = 0; + uint8_t *bytes = (uint8_t *)buf; + + while (bytes_read < size) { + const size_t curr_size = size - bytes_read; + const size_t curr_bytes_read = + DoReadMemory(addr + bytes_read, bytes + bytes_read, curr_size, error); + bytes_read += curr_bytes_read; + if (curr_bytes_read == curr_size || curr_bytes_read == 0) + break; + } + + // Replace any software breakpoint opcodes that fall into this range back + // into "buf" before we return + if (bytes_read > 0) + RemoveBreakpointOpcodesFromBuffer(addr, bytes_read, (uint8_t *)buf); + return bytes_read; + } + + uint64_t Process::ReadUnsignedIntegerFromMemory(lldb::addr_t vm_addr, + size_t integer_byte_size, + uint64_t fail_value, + Status &error) { + Scalar scalar; + if (ReadScalarIntegerFromMemory(vm_addr, integer_byte_size, false, scalar, + error)) + return scalar.ULongLong(fail_value); + return fail_value; + } + + int64_t Process::ReadSignedIntegerFromMemory(lldb::addr_t vm_addr, + size_t integer_byte_size, + int64_t fail_value, + Status &error) { + Scalar scalar; + if (ReadScalarIntegerFromMemory(vm_addr, integer_byte_size, true, scalar, + error)) + return scalar.SLongLong(fail_value); + return fail_value; + } + + addr_t Process::ReadPointerFromMemory(lldb::addr_t vm_addr, Status &error) { + Scalar scalar; + if (ReadScalarIntegerFromMemory(vm_addr, GetAddressByteSize(), false, scalar, + error)) + return scalar.ULongLong(LLDB_INVALID_ADDRESS); + return LLDB_INVALID_ADDRESS; + } + + bool Process::WritePointerToMemory(lldb::addr_t vm_addr, lldb::addr_t ptr_value, + Status &error) { + Scalar scalar; + const uint32_t addr_byte_size = GetAddressByteSize(); + if (addr_byte_size <= 4) + scalar = (uint32_t)ptr_value; + else + scalar = ptr_value; + return WriteScalarToMemory(vm_addr, scalar, addr_byte_size, error) == + addr_byte_size; + } + + size_t Process::WriteMemoryPrivate(addr_t addr, const void *buf, size_t size, + Status &error) { + size_t bytes_written = 0; + const uint8_t *bytes = (const uint8_t *)buf; + + while (bytes_written < size) { + const size_t curr_size = size - bytes_written; + const size_t curr_bytes_written = DoWriteMemory( + addr + bytes_written, bytes + bytes_written, curr_size, error); + bytes_written += curr_bytes_written; + if (curr_bytes_written == curr_size || curr_bytes_written == 0) + break; + } + return bytes_written; + } + + size_t Process::WriteMemory(addr_t addr, const void *buf, size_t size, + Status &error) { + #if defined(ENABLE_MEMORY_CACHING) + m_memory_cache.Flush(addr, size); + #endif + + if (buf == nullptr || size == 0) + return 0; + + m_mod_id.BumpMemoryID(); + + // We need to write any data that would go where any current software traps + // (enabled software breakpoints) any software traps (breakpoints) that we + // may have placed in our tasks memory. + + BreakpointSiteList bp_sites_in_range; + if (!m_breakpoint_site_list.FindInRange(addr, addr + size, bp_sites_in_range)) + return WriteMemoryPrivate(addr, buf, size, error); + + // No breakpoint sites overlap + if (bp_sites_in_range.IsEmpty()) + return WriteMemoryPrivate(addr, buf, size, error); + + const uint8_t *ubuf = (const uint8_t *)buf; + uint64_t bytes_written = 0; + + bp_sites_in_range.ForEach([this, addr, size, &bytes_written, &ubuf, + &error](BreakpointSite *bp) -> void { + if (error.Fail()) + return; + + if (bp->GetType() != BreakpointSite::eSoftware) + return; + + addr_t intersect_addr; + size_t intersect_size; + size_t opcode_offset; + const bool intersects = bp->IntersectsRange( + addr, size, &intersect_addr, &intersect_size, &opcode_offset); + UNUSED_IF_ASSERT_DISABLED(intersects); + assert(intersects); + assert(addr <= intersect_addr && intersect_addr < addr + size); + assert(addr < intersect_addr + intersect_size && + intersect_addr + intersect_size <= addr + size); + assert(opcode_offset + intersect_size <= bp->GetByteSize()); + + // Check for bytes before this breakpoint + const addr_t curr_addr = addr + bytes_written; + if (intersect_addr > curr_addr) { + // There are some bytes before this breakpoint that we need to just + // write to memory + size_t curr_size = intersect_addr - curr_addr; + size_t curr_bytes_written = + WriteMemoryPrivate(curr_addr, ubuf + bytes_written, curr_size, error); + bytes_written += curr_bytes_written; + if (curr_bytes_written != curr_size) { + // We weren't able to write all of the requested bytes, we are + // done looping and will return the number of bytes that we have + // written so far. + if (error.Success()) + error.SetErrorToGenericError(); + } + } + // Now write any bytes that would cover up any software breakpoints + // directly into the breakpoint opcode buffer + ::memcpy(bp->GetSavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, + intersect_size); + bytes_written += intersect_size; + }); + + // Write any remaining bytes after the last breakpoint if we have any left + if (bytes_written < size) + bytes_written += + WriteMemoryPrivate(addr + bytes_written, ubuf + bytes_written, + size - bytes_written, error); + + return bytes_written; + } + + size_t Process::WriteScalarToMemory(addr_t addr, const Scalar &scalar, + size_t byte_size, Status &error) { + if (byte_size == UINT32_MAX) + byte_size = scalar.GetByteSize(); + if (byte_size > 0) { + uint8_t buf[32]; + const size_t mem_size = + scalar.GetAsMemoryData(buf, byte_size, GetByteOrder(), error); + if (mem_size > 0) + return WriteMemory(addr, buf, mem_size, error); + else + error.SetErrorString("failed to get scalar as memory data"); + } else { + error.SetErrorString("invalid scalar value"); + } + return 0; + } + + size_t Process::ReadScalarIntegerFromMemory(addr_t addr, uint32_t byte_size, + bool is_signed, Scalar &scalar, + Status &error) { + uint64_t uval = 0; + if (byte_size == 0) { + error.SetErrorString("byte size is zero"); + } else if (byte_size & (byte_size - 1)) { + error.SetErrorStringWithFormat("byte size %u is not a power of 2", + byte_size); + } else if (byte_size <= sizeof(uval)) { + const size_t bytes_read = ReadMemory(addr, &uval, byte_size, error); + if (bytes_read == byte_size) { + DataExtractor data(&uval, sizeof(uval), GetByteOrder(), + 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.SignExtend(byte_size * 8); + return bytes_read; + } + } else { + error.SetErrorStringWithFormat( + "byte size of %u is too large for integer scalar type", byte_size); + } + return 0; + } + + Status Process::WriteObjectFile(std::vector entries) { + Status error; + for (const auto &Entry : entries) { + WriteMemory(Entry.Dest, Entry.Contents.data(), Entry.Contents.size(), + error); + if (!error.Success()) + break; + } + return error; + } + + #define USE_ALLOCATE_MEMORY_CACHE 1 + addr_t Process::AllocateMemory(size_t size, uint32_t permissions, + Status &error) { + if (GetPrivateState() != eStateStopped) { + error.SetErrorToGenericError(); + return LLDB_INVALID_ADDRESS; + } + + #if defined(USE_ALLOCATE_MEMORY_CACHE) + return m_allocated_memory_cache.AllocateMemory(size, permissions, error); + #else + addr_t allocated_addr = DoAllocateMemory(size, permissions, error); + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, + "Process::AllocateMemory(size=%" PRIu64 + ", permissions=%s) => 0x%16.16" PRIx64 + " (m_stop_id = %u m_memory_id = %u)", + (uint64_t)size, GetPermissionsAsCString(permissions), + (uint64_t)allocated_addr, m_mod_id.GetStopID(), + m_mod_id.GetMemoryID()); + return allocated_addr; + #endif + } + + addr_t Process::CallocateMemory(size_t size, uint32_t permissions, + Status &error) { + addr_t return_addr = AllocateMemory(size, permissions, error); + if (error.Success()) { + std::string buffer(size, 0); + WriteMemory(return_addr, buffer.c_str(), size, error); + } + return return_addr; + } + + bool Process::CanJIT() { + if (m_can_jit == eCanJITDontKnow) { + Log *log = GetLog(LLDBLog::Process); + Status err; + + uint64_t allocated_memory = AllocateMemory( + 8, ePermissionsReadable | ePermissionsWritable | ePermissionsExecutable, + err); + + if (err.Success()) { + m_can_jit = eCanJITYes; + LLDB_LOGF(log, + "Process::%s pid %" PRIu64 + " allocation test passed, CanJIT () is true", + __FUNCTION__, GetID()); + } else { + m_can_jit = eCanJITNo; + LLDB_LOGF(log, + "Process::%s pid %" PRIu64 + " allocation test failed, CanJIT () is false: %s", + __FUNCTION__, GetID(), err.AsCString()); + } + + DeallocateMemory(allocated_memory); + } + + return m_can_jit == eCanJITYes; + } + + void Process::SetCanJIT(bool can_jit) { + m_can_jit = (can_jit ? eCanJITYes : eCanJITNo); + } + + void Process::SetCanRunCode(bool can_run_code) { + SetCanJIT(can_run_code); + m_can_interpret_function_calls = can_run_code; + } + + Status Process::DeallocateMemory(addr_t ptr) { + Status error; + #if defined(USE_ALLOCATE_MEMORY_CACHE) + if (!m_allocated_memory_cache.DeallocateMemory(ptr)) { + error.SetErrorStringWithFormat( + "deallocation of memory at 0x%" PRIx64 " failed.", (uint64_t)ptr); + } + #else + error = DoDeallocateMemory(ptr); + + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, + "Process::DeallocateMemory(addr=0x%16.16" PRIx64 + ") => err = %s (m_stop_id = %u, m_memory_id = %u)", + ptr, error.AsCString("SUCCESS"), m_mod_id.GetStopID(), + m_mod_id.GetMemoryID()); + #endif + return error; + } + + ModuleSP Process::ReadModuleFromMemory(const FileSpec &file_spec, + lldb::addr_t header_addr, + size_t size_to_read) { + Log *log = GetLog(LLDBLog::Host); + if (log) { + LLDB_LOGF(log, + "Process::ReadModuleFromMemory reading %s binary from memory", + file_spec.GetPath().c_str()); + } + ModuleSP module_sp(new Module(file_spec, ArchSpec())); + if (module_sp) { + Status error; + ObjectFile *objfile = module_sp->GetMemoryObjectFile( + shared_from_this(), header_addr, error, size_to_read); + if (objfile) + return module_sp; + } + return ModuleSP(); + } + + bool Process::GetLoadAddressPermissions(lldb::addr_t load_addr, + uint32_t &permissions) { + MemoryRegionInfo range_info; + permissions = 0; + Status error(GetMemoryRegionInfo(load_addr, range_info)); + if (!error.Success()) + return false; + if (range_info.GetReadable() == MemoryRegionInfo::eDontKnow || + range_info.GetWritable() == MemoryRegionInfo::eDontKnow || + range_info.GetExecutable() == MemoryRegionInfo::eDontKnow) { + return false; + } + + if (range_info.GetReadable() == MemoryRegionInfo::eYes) + permissions |= lldb::ePermissionsReadable; + + if (range_info.GetWritable() == MemoryRegionInfo::eYes) + permissions |= lldb::ePermissionsWritable; + + if (range_info.GetExecutable() == MemoryRegionInfo::eYes) + permissions |= lldb::ePermissionsExecutable; + + return true; + } + + Status Process::EnableWatchpoint(Watchpoint *watchpoint, bool notify) { + Status error; + error.SetErrorString("watchpoints are not supported"); + return error; + } + + Status Process::DisableWatchpoint(Watchpoint *watchpoint, bool notify) { + Status error; + error.SetErrorString("watchpoints are not supported"); + return error; + } + + StateType + Process::WaitForProcessStopPrivate(EventSP &event_sp, + const Timeout &timeout) { + StateType state; + + while (true) { + event_sp.reset(); + state = GetStateChangedEventsPrivate(event_sp, timeout); + + if (StateIsStoppedState(state, false)) + break; + + // If state is invalid, then we timed out + if (state == eStateInvalid) + break; + + if (event_sp) + HandlePrivateEvent(event_sp); + } + return state; + } + + void Process::LoadOperatingSystemPlugin(bool flush) { + if (flush) + m_thread_list.Clear(); + m_os_up.reset(OperatingSystem::FindPlugin(this, nullptr)); + if (flush) + Flush(); + } + + Status Process::Launch(ProcessLaunchInfo &launch_info) { + Status error; + m_abi_sp.reset(); + m_dyld_up.reset(); + m_jit_loaders_up.reset(); + m_system_runtime_up.reset(); + m_os_up.reset(); + m_process_input_reader.reset(); + + Module *exe_module = GetTarget().GetExecutableModulePointer(); + + // The "remote executable path" is hooked up to the local Executable + // module. But we should be able to debug a remote process even if the + // executable module only exists on the remote. However, there needs to + // be a way to express this path, without actually having a module. + // The way to do that is to set the ExecutableFile in the LaunchInfo. + // Figure that out here: + + FileSpec exe_spec_to_use; + if (!exe_module) { + if (!launch_info.GetExecutableFile()) { + error.SetErrorString("executable module does not exist"); + return error; + } + exe_spec_to_use = launch_info.GetExecutableFile(); + } else + exe_spec_to_use = exe_module->GetFileSpec(); + + if (exe_module && FileSystem::Instance().Exists(exe_module->GetFileSpec())) { + // Install anything that might need to be installed prior to launching. + // For host systems, this will do nothing, but if we are connected to a + // remote platform it will install any needed binaries + error = GetTarget().Install(&launch_info); + if (error.Fail()) + return error; + } + // Listen and queue events that are broadcasted during the process launch. + ListenerSP listener_sp(Listener::MakeListener("LaunchEventHijack")); + HijackProcessEvents(listener_sp); + auto on_exit = llvm::make_scope_exit([this]() { RestoreProcessEvents(); }); + + if (PrivateStateThreadIsValid()) + PausePrivateStateThread(); + + error = WillLaunch(exe_module); + if (error.Success()) { + const bool restarted = false; + SetPublicState(eStateLaunching, restarted); + m_should_detach = false; + + if (m_public_run_lock.TrySetRunning()) { + // Now launch using these arguments. + error = DoLaunch(exe_module, launch_info); + } else { + // This shouldn't happen + error.SetErrorString("failed to acquire process run lock"); + } + + if (error.Fail()) { + if (GetID() != LLDB_INVALID_PROCESS_ID) { + SetID(LLDB_INVALID_PROCESS_ID); + const char *error_string = error.AsCString(); + if (error_string == nullptr) + error_string = "launch failed"; + SetExitStatus(-1, error_string); + } + } else { + EventSP event_sp; + + // Now wait for the process to launch and return control to us, and then + // call DidLaunch: + StateType state = WaitForProcessStopPrivate(event_sp, seconds(10)); + + if (state == eStateInvalid || !event_sp) { + // We were able to launch the process, but we failed to catch the + // initial stop. + error.SetErrorString("failed to catch stop after launch"); + SetExitStatus(0, "failed to catch stop after launch"); + Destroy(false); + } else if (state == eStateStopped || state == eStateCrashed) { + DidLaunch(); + + DynamicLoader *dyld = GetDynamicLoader(); + if (dyld) + dyld->DidLaunch(); + + GetJITLoaders().DidLaunch(); + + SystemRuntime *system_runtime = GetSystemRuntime(); + if (system_runtime) + system_runtime->DidLaunch(); + + if (!m_os_up) + LoadOperatingSystemPlugin(false); + + // We successfully launched the process and stopped, now it the + // right time to set up signal filters before resuming. + UpdateAutomaticSignalFiltering(); + + // Note, the stop event was consumed above, but not handled. This + // was done to give DidLaunch a chance to run. The target is either + // stopped or crashed. Directly set the state. This is done to + // prevent a stop message with a bunch of spurious output on thread + // status, as well as not pop a ProcessIOHandler. + // We are done with the launch hijack listener, and this stop should + // go to the public state listener: + RestoreProcessEvents(); ++ // Releasing the scope guard to avoid double restoring. ++ on_exit.release(); + SetPublicState(state, false); + + if (PrivateStateThreadIsValid()) + ResumePrivateStateThread(); + else + StartPrivateStateThread(); + + // Target was stopped at entry as was intended. Need to notify the + // listeners about it. + if (state == eStateStopped && + launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) + HandlePrivateEvent(event_sp); + } else if (state == eStateExited) { + // We exited while trying to launch somehow. Don't call DidLaunch + // as that's not likely to work, and return an invalid pid. + HandlePrivateEvent(event_sp); + } + } + } else { + std::string local_exec_file_path = exe_spec_to_use.GetPath(); + error.SetErrorStringWithFormat("file doesn't exist: '%s'", + local_exec_file_path.c_str()); + } + + return error; + } + + Status Process::LoadCore() { + Status error = DoLoadCore(); + if (error.Success()) { + ListenerSP listener_sp( + Listener::MakeListener("lldb.process.load_core_listener")); + HijackProcessEvents(listener_sp); + + if (PrivateStateThreadIsValid()) + ResumePrivateStateThread(); + else + StartPrivateStateThread(); + + DynamicLoader *dyld = GetDynamicLoader(); + if (dyld) + dyld->DidAttach(); + + GetJITLoaders().DidAttach(); + + SystemRuntime *system_runtime = GetSystemRuntime(); + if (system_runtime) + system_runtime->DidAttach(); + + if (!m_os_up) + LoadOperatingSystemPlugin(false); + + // We successfully loaded a core file, now pretend we stopped so we can + // show all of the threads in the core file and explore the crashed state. + SetPrivateState(eStateStopped); + + // Wait for a stopped event since we just posted one above... + lldb::EventSP event_sp; + StateType state = + WaitForProcessToStop(llvm::None, &event_sp, true, listener_sp); + + if (!StateIsStoppedState(state, false)) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, "Process::Halt() failed to stop, state is: %s", + StateAsCString(state)); + error.SetErrorString( + "Did not get stopped event after loading the core file."); + } + RestoreProcessEvents(); + } + return error; + } + + DynamicLoader *Process::GetDynamicLoader() { + if (!m_dyld_up) + m_dyld_up.reset(DynamicLoader::FindPlugin(this, "")); + return m_dyld_up.get(); + } + + DataExtractor Process::GetAuxvData() { return DataExtractor(); } + + llvm::Expected Process::SaveCore(llvm::StringRef outfile) { + return false; + } + + JITLoaderList &Process::GetJITLoaders() { + if (!m_jit_loaders_up) { + m_jit_loaders_up = std::make_unique(); + JITLoader::LoadPlugins(this, *m_jit_loaders_up); + } + return *m_jit_loaders_up; + } + + SystemRuntime *Process::GetSystemRuntime() { + if (!m_system_runtime_up) + m_system_runtime_up.reset(SystemRuntime::FindPlugin(this)); + return m_system_runtime_up.get(); + } + + Process::AttachCompletionHandler::AttachCompletionHandler(Process *process, + uint32_t exec_count) + : NextEventAction(process), m_exec_count(exec_count) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF( + log, + "Process::AttachCompletionHandler::%s process=%p, exec_count=%" PRIu32, + __FUNCTION__, static_cast(process), exec_count); + } + + Process::NextEventAction::EventActionResult + Process::AttachCompletionHandler::PerformAction(lldb::EventSP &event_sp) { + Log *log = GetLog(LLDBLog::Process); + + StateType state = ProcessEventData::GetStateFromEvent(event_sp.get()); + LLDB_LOGF(log, + "Process::AttachCompletionHandler::%s called with state %s (%d)", + __FUNCTION__, StateAsCString(state), static_cast(state)); + + switch (state) { + case eStateAttaching: + return eEventActionSuccess; + + case eStateRunning: + case eStateConnected: + return eEventActionRetry; + + case eStateStopped: + case eStateCrashed: + // During attach, prior to sending the eStateStopped event, + // lldb_private::Process subclasses must set the new process ID. + assert(m_process->GetID() != LLDB_INVALID_PROCESS_ID); + // We don't want these events to be reported, so go set the + // ShouldReportStop here: + m_process->GetThreadList().SetShouldReportStop(eVoteNo); + + if (m_exec_count > 0) { + --m_exec_count; + + LLDB_LOGF(log, + "Process::AttachCompletionHandler::%s state %s: reduced " + "remaining exec count to %" PRIu32 ", requesting resume", + __FUNCTION__, StateAsCString(state), m_exec_count); + + RequestResume(); + return eEventActionRetry; + } else { + LLDB_LOGF(log, + "Process::AttachCompletionHandler::%s state %s: no more " + "execs expected to start, continuing with attach", + __FUNCTION__, StateAsCString(state)); + + m_process->CompleteAttach(); + return eEventActionSuccess; + } + break; + + default: + case eStateExited: + case eStateInvalid: + break; + } + + m_exit_string.assign("No valid Process"); + return eEventActionExit; + } + + Process::NextEventAction::EventActionResult + Process::AttachCompletionHandler::HandleBeingInterrupted() { + return eEventActionSuccess; + } + + const char *Process::AttachCompletionHandler::GetExitString() { + return m_exit_string.c_str(); + } + + ListenerSP ProcessAttachInfo::GetListenerForProcess(Debugger &debugger) { + if (m_listener_sp) + return m_listener_sp; + else + return debugger.GetListener(); + } + + Status Process::Attach(ProcessAttachInfo &attach_info) { + m_abi_sp.reset(); + m_process_input_reader.reset(); + m_dyld_up.reset(); + m_jit_loaders_up.reset(); + m_system_runtime_up.reset(); + m_os_up.reset(); + + lldb::pid_t attach_pid = attach_info.GetProcessID(); + Status error; + if (attach_pid == LLDB_INVALID_PROCESS_ID) { + char process_name[PATH_MAX]; + + if (attach_info.GetExecutableFile().GetPath(process_name, + sizeof(process_name))) { + const bool wait_for_launch = attach_info.GetWaitForLaunch(); + + if (wait_for_launch) { + error = WillAttachToProcessWithName(process_name, wait_for_launch); + if (error.Success()) { + if (m_public_run_lock.TrySetRunning()) { + m_should_detach = true; + const bool restarted = false; + SetPublicState(eStateAttaching, restarted); + // Now attach using these arguments. + error = DoAttachToProcessWithName(process_name, attach_info); + } else { + // This shouldn't happen + error.SetErrorString("failed to acquire process run lock"); + } + + if (error.Fail()) { + if (GetID() != LLDB_INVALID_PROCESS_ID) { + SetID(LLDB_INVALID_PROCESS_ID); + if (error.AsCString() == nullptr) + error.SetErrorString("attach failed"); + + SetExitStatus(-1, error.AsCString()); + } + } else { + SetNextEventAction(new Process::AttachCompletionHandler( + this, attach_info.GetResumeCount())); + StartPrivateStateThread(); + } + return error; + } + } else { + ProcessInstanceInfoList process_infos; + PlatformSP platform_sp(GetTarget().GetPlatform()); + + if (platform_sp) { + ProcessInstanceInfoMatch match_info; + match_info.GetProcessInfo() = attach_info; + match_info.SetNameMatchType(NameMatch::Equals); + platform_sp->FindProcesses(match_info, process_infos); + const uint32_t num_matches = process_infos.size(); + if (num_matches == 1) { + attach_pid = process_infos[0].GetProcessID(); + // Fall through and attach using the above process ID + } else { + match_info.GetProcessInfo().GetExecutableFile().GetPath( + process_name, sizeof(process_name)); + if (num_matches > 1) { + StreamString s; + ProcessInstanceInfo::DumpTableHeader(s, true, false); + for (size_t i = 0; i < num_matches; i++) { + process_infos[i].DumpAsTableRow( + s, platform_sp->GetUserIDResolver(), true, false); + } + error.SetErrorStringWithFormat( + "more than one process named %s:\n%s", process_name, + s.GetData()); + } else + error.SetErrorStringWithFormat( + "could not find a process named %s", process_name); + } + } else { + error.SetErrorString( + "invalid platform, can't find processes by name"); + return error; + } + } + } else { + error.SetErrorString("invalid process name"); + } + } + + if (attach_pid != LLDB_INVALID_PROCESS_ID) { + error = WillAttachToProcessWithID(attach_pid); + if (error.Success()) { + + if (m_public_run_lock.TrySetRunning()) { + // Now attach using these arguments. + m_should_detach = true; + const bool restarted = false; + SetPublicState(eStateAttaching, restarted); + error = DoAttachToProcessWithID(attach_pid, attach_info); + } else { + // This shouldn't happen + error.SetErrorString("failed to acquire process run lock"); + } + + if (error.Success()) { + SetNextEventAction(new Process::AttachCompletionHandler( + this, attach_info.GetResumeCount())); + StartPrivateStateThread(); + } else { + if (GetID() != LLDB_INVALID_PROCESS_ID) + SetID(LLDB_INVALID_PROCESS_ID); + + const char *error_string = error.AsCString(); + if (error_string == nullptr) + error_string = "attach failed"; + + SetExitStatus(-1, error_string); + } + } + } + return error; + } + + void Process::CompleteAttach() { + Log *log(GetLog(LLDBLog::Process | LLDBLog::Target)); + LLDB_LOGF(log, "Process::%s()", __FUNCTION__); + + // Let the process subclass figure out at much as it can about the process + // before we go looking for a dynamic loader plug-in. + ArchSpec process_arch; + DidAttach(process_arch); + + if (process_arch.IsValid()) { + GetTarget().SetArchitecture(process_arch); + if (log) { + const char *triple_str = process_arch.GetTriple().getTriple().c_str(); + LLDB_LOGF(log, + "Process::%s replacing process architecture with DidAttach() " + "architecture: %s", + __FUNCTION__, triple_str ? triple_str : ""); + } + } + + // We just attached. If we have a platform, ask it for the process + // architecture, and if it isn't the same as the one we've already set, + // switch architectures. + PlatformSP platform_sp(GetTarget().GetPlatform()); + assert(platform_sp); + if (platform_sp) { + const ArchSpec &target_arch = GetTarget().GetArchitecture(); + if (target_arch.IsValid() && + !platform_sp->IsCompatibleArchitecture(target_arch, false, nullptr)) { + ArchSpec platform_arch; + platform_sp = + platform_sp->GetPlatformForArchitecture(target_arch, &platform_arch); + if (platform_sp) { + GetTarget().SetPlatform(platform_sp); + GetTarget().SetArchitecture(platform_arch); + LLDB_LOGF(log, + "Process::%s switching platform to %s and architecture " + "to %s based on info from attach", + __FUNCTION__, platform_sp->GetName().AsCString(""), + platform_arch.GetTriple().getTriple().c_str()); + } + } else if (!process_arch.IsValid()) { + ProcessInstanceInfo process_info; + GetProcessInfo(process_info); + const ArchSpec &process_arch = process_info.GetArchitecture(); + const ArchSpec &target_arch = GetTarget().GetArchitecture(); + if (process_arch.IsValid() && + target_arch.IsCompatibleMatch(process_arch) && + !target_arch.IsExactMatch(process_arch)) { + GetTarget().SetArchitecture(process_arch); + LLDB_LOGF(log, + "Process::%s switching architecture to %s based on info " + "the platform retrieved for pid %" PRIu64, + __FUNCTION__, process_arch.GetTriple().getTriple().c_str(), + GetID()); + } + } + } + + // We have completed the attach, now it is time to find the dynamic loader + // plug-in + DynamicLoader *dyld = GetDynamicLoader(); + if (dyld) { + dyld->DidAttach(); + if (log) { + ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); + LLDB_LOG(log, + "after DynamicLoader::DidAttach(), target " + "executable is {0} (using {1} plugin)", + exe_module_sp ? exe_module_sp->GetFileSpec() : FileSpec(), + dyld->GetPluginName()); + } + } + + GetJITLoaders().DidAttach(); + + SystemRuntime *system_runtime = GetSystemRuntime(); + if (system_runtime) { + system_runtime->DidAttach(); + if (log) { + ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); + LLDB_LOG(log, + "after SystemRuntime::DidAttach(), target " + "executable is {0} (using {1} plugin)", + exe_module_sp ? exe_module_sp->GetFileSpec() : FileSpec(), + system_runtime->GetPluginName()); + } + } + + if (!m_os_up) { + LoadOperatingSystemPlugin(false); + if (m_os_up) { + // Somebody might have gotten threads before now, but we need to force the + // update after we've loaded the OperatingSystem plugin or it won't get a + // chance to process the threads. + m_thread_list.Clear(); + UpdateThreadListIfNeeded(); + } + } + // Figure out which one is the executable, and set that in our target: + ModuleSP new_executable_module_sp; + for (ModuleSP module_sp : GetTarget().GetImages().Modules()) { + if (module_sp && module_sp->IsExecutable()) { + if (GetTarget().GetExecutableModulePointer() != module_sp.get()) + new_executable_module_sp = module_sp; + break; + } + } + if (new_executable_module_sp) { + GetTarget().SetExecutableModule(new_executable_module_sp, + eLoadDependentsNo); + if (log) { + ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); + LLDB_LOGF( + log, + "Process::%s after looping through modules, target executable is %s", + __FUNCTION__, + exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str() + : ""); + } + } + } + + Status Process::ConnectRemote(llvm::StringRef remote_url) { + m_abi_sp.reset(); + m_process_input_reader.reset(); + + // Find the process and its architecture. Make sure it matches the + // architecture of the current Target, and if not adjust it. + + Status error(DoConnectRemote(remote_url)); + if (error.Success()) { + if (GetID() != LLDB_INVALID_PROCESS_ID) { + EventSP event_sp; + StateType state = WaitForProcessStopPrivate(event_sp, llvm::None); + + if (state == eStateStopped || state == eStateCrashed) { + // If we attached and actually have a process on the other end, then + // this ended up being the equivalent of an attach. + CompleteAttach(); + + // This delays passing the stopped event to listeners till + // CompleteAttach gets a chance to complete... + HandlePrivateEvent(event_sp); + } + } + + if (PrivateStateThreadIsValid()) + ResumePrivateStateThread(); + else + StartPrivateStateThread(); + } + return error; + } + + Status Process::PrivateResume() { + Log *log(GetLog(LLDBLog::Process | LLDBLog::Step)); + LLDB_LOGF(log, + "Process::PrivateResume() m_stop_id = %u, public state: %s " + "private state: %s", + m_mod_id.GetStopID(), StateAsCString(m_public_state.GetValue()), + StateAsCString(m_private_state.GetValue())); + + // If signals handing status changed we might want to update our signal + // filters before resuming. + UpdateAutomaticSignalFiltering(); + + Status error(WillResume()); + // Tell the process it is about to resume before the thread list + if (error.Success()) { + // Now let the thread list know we are about to resume so it can let all of + // our threads know that they are about to be resumed. Threads will each be + // called with Thread::WillResume(StateType) where StateType contains the + // state that they are supposed to have when the process is resumed + // (suspended/running/stepping). Threads should also check their resume + // signal in lldb::Thread::GetResumeSignal() to see if they are supposed to + // start back up with a signal. + if (m_thread_list.WillResume()) { + // Last thing, do the PreResumeActions. + if (!RunPreResumeActions()) { + error.SetErrorString( + "Process::PrivateResume PreResumeActions failed, not resuming."); + } else { + m_mod_id.BumpResumeID(); + error = DoResume(); + if (error.Success()) { + DidResume(); + m_thread_list.DidResume(); + LLDB_LOGF(log, "Process thinks the process has resumed."); + } else { + LLDB_LOGF(log, "Process::PrivateResume() DoResume failed."); + return error; + } + } + } else { + // Somebody wanted to run without running (e.g. we were faking a step + // from one frame of a set of inlined frames that share the same PC to + // another.) So generate a continue & a stopped event, and let the world + // handle them. + LLDB_LOGF(log, + "Process::PrivateResume() asked to simulate a start & stop."); + + SetPrivateState(eStateRunning); + SetPrivateState(eStateStopped); + } + } else + LLDB_LOGF(log, "Process::PrivateResume() got an error \"%s\".", + error.AsCString("")); + return error; + } + + Status Process::Halt(bool clear_thread_plans, bool use_run_lock) { + if (!StateIsRunningState(m_public_state.GetValue())) + return Status("Process is not running."); + + // Don't clear the m_clear_thread_plans_on_stop, only set it to true if in + // case it was already set and some thread plan logic calls halt on its own. + m_clear_thread_plans_on_stop |= clear_thread_plans; + + ListenerSP halt_listener_sp( + Listener::MakeListener("lldb.process.halt_listener")); + HijackProcessEvents(halt_listener_sp); + + EventSP event_sp; + + SendAsyncInterrupt(); + + if (m_public_state.GetValue() == eStateAttaching) { + // Don't hijack and eat the eStateExited as the code that was doing the + // attach will be waiting for this event... + RestoreProcessEvents(); + SetExitStatus(SIGKILL, "Cancelled async attach."); + Destroy(false); + return Status(); + } + + // Wait for the process halt timeout seconds for the process to stop. + StateType state = + WaitForProcessToStop(GetInterruptTimeout(), &event_sp, true, + halt_listener_sp, nullptr, use_run_lock); + RestoreProcessEvents(); + + if (state == eStateInvalid || !event_sp) { + // We timed out and didn't get a stop event... + return Status("Halt timed out. State = %s", StateAsCString(GetState())); + } + + BroadcastEvent(event_sp); + + return Status(); + } + + Status Process::StopForDestroyOrDetach(lldb::EventSP &exit_event_sp) { + Status error; + + // Check both the public & private states here. If we're hung evaluating an + // expression, for instance, then the public state will be stopped, but we + // still need to interrupt. + if (m_public_state.GetValue() == eStateRunning || + m_private_state.GetValue() == eStateRunning) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, "Process::%s() About to stop.", __FUNCTION__); + + ListenerSP listener_sp( + Listener::MakeListener("lldb.Process.StopForDestroyOrDetach.hijack")); + HijackProcessEvents(listener_sp); + + SendAsyncInterrupt(); + + // Consume the interrupt event. + StateType state = WaitForProcessToStop(GetInterruptTimeout(), + &exit_event_sp, true, listener_sp); + + RestoreProcessEvents(); + + // If the process exited while we were waiting for it to stop, put the + // exited event into the shared pointer passed in and return. Our caller + // doesn't need to do anything else, since they don't have a process + // anymore... + + if (state == eStateExited || m_private_state.GetValue() == eStateExited) { + LLDB_LOGF(log, "Process::%s() Process exited while waiting to stop.", + __FUNCTION__); + return error; + } else + exit_event_sp.reset(); // It is ok to consume any non-exit stop events + + if (state != eStateStopped) { + LLDB_LOGF(log, "Process::%s() failed to stop, state is: %s", __FUNCTION__, + StateAsCString(state)); + // If we really couldn't stop the process then we should just error out + // here, but if the lower levels just bobbled sending the event and we + // really are stopped, then continue on. + StateType private_state = m_private_state.GetValue(); + if (private_state != eStateStopped) { + return Status( + "Attempt to stop the target in order to detach timed out. " + "State = %s", + StateAsCString(GetState())); + } + } + } + return error; + } + + Status Process::Detach(bool keep_stopped) { + EventSP exit_event_sp; + Status error; + m_destroy_in_process = true; + + error = WillDetach(); + + if (error.Success()) { + if (DetachRequiresHalt()) { + error = StopForDestroyOrDetach(exit_event_sp); + if (!error.Success()) { + m_destroy_in_process = false; + return error; + } else if (exit_event_sp) { + // We shouldn't need to do anything else here. There's no process left + // to detach from... + StopPrivateStateThread(); + m_destroy_in_process = false; + return error; + } + } + + m_thread_list.DiscardThreadPlans(); + DisableAllBreakpointSites(); + + error = DoDetach(keep_stopped); + if (error.Success()) { + DidDetach(); + StopPrivateStateThread(); + } else { + return error; + } + } + m_destroy_in_process = false; + + // If we exited when we were waiting for a process to stop, then forward the + // event here so we don't lose the event + if (exit_event_sp) { + // Directly broadcast our exited event because we shut down our private + // state thread above + BroadcastEvent(exit_event_sp); + } + + // If we have been interrupted (to kill us) in the middle of running, we may + // not end up propagating the last events through the event system, in which + // case we might strand the write lock. Unlock it here so when we do to tear + // down the process we don't get an error destroying the lock. + + m_public_run_lock.SetStopped(); + return error; + } + + Status Process::Destroy(bool force_kill) { + // If we've already called Process::Finalize then there's nothing useful to + // be done here. Finalize has actually called Destroy already. + if (m_finalizing) + return {}; + return DestroyImpl(force_kill); + } + + Status Process::DestroyImpl(bool force_kill) { + // Tell ourselves we are in the process of destroying the process, so that we + // don't do any unnecessary work that might hinder the destruction. Remember + // to set this back to false when we are done. That way if the attempt + // failed and the process stays around for some reason it won't be in a + // confused state. + + if (force_kill) + m_should_detach = false; + + if (GetShouldDetach()) { + // FIXME: This will have to be a process setting: + bool keep_stopped = false; + Detach(keep_stopped); + } + + m_destroy_in_process = true; + + Status error(WillDestroy()); + if (error.Success()) { + EventSP exit_event_sp; + if (DestroyRequiresHalt()) { + error = StopForDestroyOrDetach(exit_event_sp); + } + + if (m_public_state.GetValue() == eStateStopped) { + // Ditch all thread plans, and remove all our breakpoints: in case we + // have to restart the target to kill it, we don't want it hitting a + // breakpoint... Only do this if we've stopped, however, since if we + // didn't manage to halt it above, then we're not going to have much luck + // doing this now. + m_thread_list.DiscardThreadPlans(); + DisableAllBreakpointSites(); + } + + error = DoDestroy(); + if (error.Success()) { + DidDestroy(); + StopPrivateStateThread(); + } + m_stdio_communication.StopReadThread(); + m_stdio_communication.Disconnect(); + m_stdin_forward = false; + + if (m_process_input_reader) { + m_process_input_reader->SetIsDone(true); + m_process_input_reader->Cancel(); + m_process_input_reader.reset(); + } + + // If we exited when we were waiting for a process to stop, then forward + // the event here so we don't lose the event + if (exit_event_sp) { + // Directly broadcast our exited event because we shut down our private + // state thread above + BroadcastEvent(exit_event_sp); + } + + // If we have been interrupted (to kill us) in the middle of running, we + // may not end up propagating the last events through the event system, in + // which case we might strand the write lock. Unlock it here so when we do + // to tear down the process we don't get an error destroying the lock. + m_public_run_lock.SetStopped(); + } + + m_destroy_in_process = false; + + return error; + } + + Status Process::Signal(int signal) { + Status error(WillSignal()); + if (error.Success()) { + error = DoSignal(signal); + if (error.Success()) + DidSignal(); + } + return error; + } + + void Process::SetUnixSignals(UnixSignalsSP &&signals_sp) { + assert(signals_sp && "null signals_sp"); + m_unix_signals_sp = signals_sp; + } + + const lldb::UnixSignalsSP &Process::GetUnixSignals() { + assert(m_unix_signals_sp && "null m_unix_signals_sp"); + return m_unix_signals_sp; + } + + lldb::ByteOrder Process::GetByteOrder() const { + return GetTarget().GetArchitecture().GetByteOrder(); + } + + uint32_t Process::GetAddressByteSize() const { + return GetTarget().GetArchitecture().GetAddressByteSize(); + } + + bool Process::ShouldBroadcastEvent(Event *event_ptr) { + const StateType state = + Process::ProcessEventData::GetStateFromEvent(event_ptr); + bool return_value = true; + Log *log(GetLog(LLDBLog::Events | LLDBLog::Process)); + + switch (state) { + case eStateDetached: + case eStateExited: + case eStateUnloaded: + m_stdio_communication.SynchronizeWithReadThread(); + m_stdio_communication.StopReadThread(); + m_stdio_communication.Disconnect(); + m_stdin_forward = false; + + LLVM_FALLTHROUGH; + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + // These events indicate changes in the state of the debugging session, + // always report them. + return_value = true; + break; + case eStateInvalid: + // We stopped for no apparent reason, don't report it. + return_value = false; + break; + case eStateRunning: + case eStateStepping: + // If we've started the target running, we handle the cases where we are + // already running and where there is a transition from stopped to running + // differently. running -> running: Automatically suppress extra running + // events stopped -> running: Report except when there is one or more no + // votes + // and no yes votes. + SynchronouslyNotifyStateChanged(state); + if (m_force_next_event_delivery) + return_value = true; + else { + switch (m_last_broadcast_state) { + case eStateRunning: + case eStateStepping: + // We always suppress multiple runnings with no PUBLIC stop in between. + return_value = false; + break; + default: + // TODO: make this work correctly. For now always report + // run if we aren't running so we don't miss any running events. If I + // run the lldb/test/thread/a.out file and break at main.cpp:58, run + // and hit the breakpoints on multiple threads, then somehow during the + // stepping over of all breakpoints no run gets reported. + + // This is a transition from stop to run. + switch (m_thread_list.ShouldReportRun(event_ptr)) { + case eVoteYes: + case eVoteNoOpinion: + return_value = true; + break; + case eVoteNo: + return_value = false; + break; + } + break; + } + } + break; + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + // We've stopped. First see if we're going to restart the target. If we + // are going to stop, then we always broadcast the event. If we aren't + // going to stop, let the thread plans decide if we're going to report this + // event. If no thread has an opinion, we don't report it. + + m_stdio_communication.SynchronizeWithReadThread(); + RefreshStateAfterStop(); + if (ProcessEventData::GetInterruptedFromEvent(event_ptr)) { + LLDB_LOGF(log, + "Process::ShouldBroadcastEvent (%p) stopped due to an " + "interrupt, state: %s", + static_cast(event_ptr), StateAsCString(state)); + // Even though we know we are going to stop, we should let the threads + // have a look at the stop, so they can properly set their state. + m_thread_list.ShouldStop(event_ptr); + return_value = true; + } else { + bool was_restarted = ProcessEventData::GetRestartedFromEvent(event_ptr); + bool should_resume = false; + + // It makes no sense to ask "ShouldStop" if we've already been + // restarted... Asking the thread list is also not likely to go well, + // since we are running again. So in that case just report the event. + + if (!was_restarted) + should_resume = !m_thread_list.ShouldStop(event_ptr); + + if (was_restarted || should_resume || m_resume_requested) { + Vote report_stop_vote = m_thread_list.ShouldReportStop(event_ptr); + LLDB_LOGF(log, + "Process::ShouldBroadcastEvent: should_resume: %i state: " + "%s was_restarted: %i report_stop_vote: %d.", + should_resume, StateAsCString(state), was_restarted, + report_stop_vote); + + switch (report_stop_vote) { + case eVoteYes: + return_value = true; + break; + case eVoteNoOpinion: + case eVoteNo: + return_value = false; + break; + } + + if (!was_restarted) { + LLDB_LOGF(log, + "Process::ShouldBroadcastEvent (%p) Restarting process " + "from state: %s", + static_cast(event_ptr), StateAsCString(state)); + ProcessEventData::SetRestartedInEvent(event_ptr, true); + PrivateResume(); + } + } else { + return_value = true; + SynchronouslyNotifyStateChanged(state); + } + } + break; + } + + // Forcing the next event delivery is a one shot deal. So reset it here. + m_force_next_event_delivery = false; + + // We do some coalescing of events (for instance two consecutive running + // events get coalesced.) But we only coalesce against events we actually + // broadcast. So we use m_last_broadcast_state to track that. NB - you + // can't use "m_public_state.GetValue()" for that purpose, as was originally + // done, because the PublicState reflects the last event pulled off the + // queue, and there may be several events stacked up on the queue unserviced. + // So the PublicState may not reflect the last broadcasted event yet. + // m_last_broadcast_state gets updated here. + + if (return_value) + m_last_broadcast_state = state; + + LLDB_LOGF(log, + "Process::ShouldBroadcastEvent (%p) => new state: %s, last " + "broadcast state: %s - %s", + static_cast(event_ptr), StateAsCString(state), + StateAsCString(m_last_broadcast_state), + return_value ? "YES" : "NO"); + return return_value; + } + + bool Process::StartPrivateStateThread(bool is_secondary_thread) { + Log *log = GetLog(LLDBLog::Events); + + bool already_running = PrivateStateThreadIsValid(); + LLDB_LOGF(log, "Process::%s()%s ", __FUNCTION__, + already_running ? " already running" + : " starting private state thread"); + + if (!is_secondary_thread && already_running) + return true; + + // Create a thread that watches our internal state and controls which events + // make it to clients (into the DCProcess event queue). + char thread_name[1024]; + uint32_t max_len = llvm::get_max_thread_name_length(); + if (max_len > 0 && max_len <= 30) { + // On platforms with abbreviated thread name lengths, choose thread names + // that fit within the limit. + if (already_running) + snprintf(thread_name, sizeof(thread_name), "intern-state-OV"); + else + snprintf(thread_name, sizeof(thread_name), "intern-state"); + } else { + if (already_running) + snprintf(thread_name, sizeof(thread_name), + "", + GetID()); + else + snprintf(thread_name, sizeof(thread_name), + "", GetID()); + } + + // Create the private state thread, and start it running. + PrivateStateThreadArgs *args_ptr = + new PrivateStateThreadArgs(this, is_secondary_thread); + llvm::Expected private_state_thread = + ThreadLauncher::LaunchThread(thread_name, Process::PrivateStateThread, + (void *)args_ptr, 8 * 1024 * 1024); + if (!private_state_thread) { + LLDB_LOG(GetLog(LLDBLog::Host), "failed to launch host thread: {}", + llvm::toString(private_state_thread.takeError())); + return false; + } + + assert(private_state_thread->IsJoinable()); + m_private_state_thread = *private_state_thread; + ResumePrivateStateThread(); + return true; + } + + void Process::PausePrivateStateThread() { + ControlPrivateStateThread(eBroadcastInternalStateControlPause); + } + + void Process::ResumePrivateStateThread() { + ControlPrivateStateThread(eBroadcastInternalStateControlResume); + } + + void Process::StopPrivateStateThread() { + if (m_private_state_thread.IsJoinable()) + ControlPrivateStateThread(eBroadcastInternalStateControlStop); + else { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF( + log, + "Went to stop the private state thread, but it was already invalid."); + } + } + + void Process::ControlPrivateStateThread(uint32_t signal) { + Log *log = GetLog(LLDBLog::Process); + + assert(signal == eBroadcastInternalStateControlStop || + signal == eBroadcastInternalStateControlPause || + signal == eBroadcastInternalStateControlResume); + + LLDB_LOGF(log, "Process::%s (signal = %d)", __FUNCTION__, signal); + + // Signal the private state thread + if (m_private_state_thread.IsJoinable()) { + // Broadcast the event. + // It is important to do this outside of the if below, because it's + // possible that the thread state is invalid but that the thread is waiting + // on a control event instead of simply being on its way out (this should + // not happen, but it apparently can). + LLDB_LOGF(log, "Sending control event of type: %d.", signal); + std::shared_ptr event_receipt_sp(new EventDataReceipt()); + m_private_state_control_broadcaster.BroadcastEvent(signal, + event_receipt_sp); + + // Wait for the event receipt or for the private state thread to exit + bool receipt_received = false; + if (PrivateStateThreadIsValid()) { + while (!receipt_received) { + // Check for a receipt for n seconds and then check if the private + // state thread is still around. + receipt_received = + event_receipt_sp->WaitForEventReceived(GetUtilityExpressionTimeout()); + if (!receipt_received) { + // Check if the private state thread is still around. If it isn't + // then we are done waiting + if (!PrivateStateThreadIsValid()) + break; // Private state thread exited or is exiting, we are done + } + } + } + + if (signal == eBroadcastInternalStateControlStop) { + thread_result_t result = {}; + m_private_state_thread.Join(&result); + m_private_state_thread.Reset(); + } + } else { + LLDB_LOGF( + log, + "Private state thread already dead, no need to signal it to stop."); + } + } + + void Process::SendAsyncInterrupt() { + if (PrivateStateThreadIsValid()) + m_private_state_broadcaster.BroadcastEvent(Process::eBroadcastBitInterrupt, + nullptr); + else + BroadcastEvent(Process::eBroadcastBitInterrupt, nullptr); + } + + void Process::HandlePrivateEvent(EventSP &event_sp) { + Log *log = GetLog(LLDBLog::Process); + m_resume_requested = false; + + const StateType new_state = + Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + + // First check to see if anybody wants a shot at this event: + if (m_next_event_action_up) { + NextEventAction::EventActionResult action_result = + m_next_event_action_up->PerformAction(event_sp); + LLDB_LOGF(log, "Ran next event action, result was %d.", action_result); + + switch (action_result) { + case NextEventAction::eEventActionSuccess: + SetNextEventAction(nullptr); + break; + + case NextEventAction::eEventActionRetry: + break; + + case NextEventAction::eEventActionExit: + // Handle Exiting Here. If we already got an exited event, we should + // just propagate it. Otherwise, swallow this event, and set our state + // to exit so the next event will kill us. + if (new_state != eStateExited) { + // FIXME: should cons up an exited event, and discard this one. + SetExitStatus(0, m_next_event_action_up->GetExitString()); + SetNextEventAction(nullptr); + return; + } + SetNextEventAction(nullptr); + break; + } + } + + // See if we should broadcast this state to external clients? + const bool should_broadcast = ShouldBroadcastEvent(event_sp.get()); + + if (should_broadcast) { + const bool is_hijacked = IsHijackedForEvent(eBroadcastBitStateChanged); + if (log) { + LLDB_LOGF(log, + "Process::%s (pid = %" PRIu64 + ") broadcasting new state %s (old state %s) to %s", + __FUNCTION__, GetID(), StateAsCString(new_state), + StateAsCString(GetState()), + is_hijacked ? "hijacked" : "public"); + } + Process::ProcessEventData::SetUpdateStateOnRemoval(event_sp.get()); + if (StateIsRunningState(new_state)) { + // Only push the input handler if we aren't fowarding events, as this + // means the curses GUI is in use... Or don't push it if we are launching + // since it will come up stopped. + if (!GetTarget().GetDebugger().IsForwardingEvents() && + new_state != eStateLaunching && new_state != eStateAttaching) { + PushProcessIOHandler(); + m_iohandler_sync.SetValue(m_iohandler_sync.GetValue() + 1, + eBroadcastAlways); + LLDB_LOGF(log, "Process::%s updated m_iohandler_sync to %d", + __FUNCTION__, m_iohandler_sync.GetValue()); + } + } else if (StateIsStoppedState(new_state, false)) { + if (!Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) { + // If the lldb_private::Debugger is handling the events, we don't want + // to pop the process IOHandler here, we want to do it when we receive + // the stopped event so we can carefully control when the process + // IOHandler is popped because when we stop we want to display some + // text stating how and why we stopped, then maybe some + // process/thread/frame info, and then we want the "(lldb) " prompt to + // show up. If we pop the process IOHandler here, then we will cause + // the command interpreter to become the top IOHandler after the + // process pops off and it will update its prompt right away... See the + // Debugger.cpp file where it calls the function as + // "process_sp->PopProcessIOHandler()" to see where I am talking about. + // Otherwise we end up getting overlapping "(lldb) " prompts and + // garbled output. + // + // If we aren't handling the events in the debugger (which is indicated + // by "m_target.GetDebugger().IsHandlingEvents()" returning false) or + // we are hijacked, then we always pop the process IO handler manually. + // Hijacking happens when the internal process state thread is running + // thread plans, or when commands want to run in synchronous mode and + // they call "process->WaitForProcessToStop()". An example of something + // that will hijack the events is a simple expression: + // + // (lldb) expr (int)puts("hello") + // + // This will cause the internal process state thread to resume and halt + // the process (and _it_ will hijack the eBroadcastBitStateChanged + // events) and we do need the IO handler to be pushed and popped + // correctly. + + if (is_hijacked || !GetTarget().GetDebugger().IsHandlingEvents()) + PopProcessIOHandler(); + } + } + + BroadcastEvent(event_sp); + } else { + if (log) { + LLDB_LOGF( + log, + "Process::%s (pid = %" PRIu64 + ") suppressing state %s (old state %s): should_broadcast == false", + __FUNCTION__, GetID(), StateAsCString(new_state), + StateAsCString(GetState())); + } + } + } + + Status Process::HaltPrivate() { + EventSP event_sp; + Status error(WillHalt()); + if (error.Fail()) + return error; + + // Ask the process subclass to actually halt our process + bool caused_stop; + error = DoHalt(caused_stop); + + DidHalt(); + return error; + } + + thread_result_t Process::PrivateStateThread(void *arg) { + std::unique_ptr args_up( + static_cast(arg)); + thread_result_t result = + args_up->process->RunPrivateStateThread(args_up->is_secondary_thread); + return result; + } + + thread_result_t Process::RunPrivateStateThread(bool is_secondary_thread) { + bool control_only = true; + + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, "Process::%s (arg = %p, pid = %" PRIu64 ") thread starting...", + __FUNCTION__, static_cast(this), GetID()); + + bool exit_now = false; + bool interrupt_requested = false; + while (!exit_now) { + EventSP event_sp; + GetEventsPrivate(event_sp, llvm::None, control_only); + if (event_sp->BroadcasterIs(&m_private_state_control_broadcaster)) { + LLDB_LOGF(log, + "Process::%s (arg = %p, pid = %" PRIu64 + ") got a control event: %d", + __FUNCTION__, static_cast(this), GetID(), + event_sp->GetType()); + + switch (event_sp->GetType()) { + case eBroadcastInternalStateControlStop: + exit_now = true; + break; // doing any internal state management below + + case eBroadcastInternalStateControlPause: + control_only = true; + break; + + case eBroadcastInternalStateControlResume: + control_only = false; + break; + } + + continue; + } else if (event_sp->GetType() == eBroadcastBitInterrupt) { + if (m_public_state.GetValue() == eStateAttaching) { + LLDB_LOGF(log, + "Process::%s (arg = %p, pid = %" PRIu64 + ") woke up with an interrupt while attaching - " + "forwarding interrupt.", + __FUNCTION__, static_cast(this), GetID()); + BroadcastEvent(eBroadcastBitInterrupt, nullptr); + } else if (StateIsRunningState(m_last_broadcast_state)) { + LLDB_LOGF(log, + "Process::%s (arg = %p, pid = %" PRIu64 + ") woke up with an interrupt - Halting.", + __FUNCTION__, static_cast(this), GetID()); + Status error = HaltPrivate(); + if (error.Fail() && log) + LLDB_LOGF(log, + "Process::%s (arg = %p, pid = %" PRIu64 + ") failed to halt the process: %s", + __FUNCTION__, static_cast(this), GetID(), + error.AsCString()); + // Halt should generate a stopped event. Make a note of the fact that + // we were doing the interrupt, so we can set the interrupted flag + // after we receive the event. We deliberately set this to true even if + // HaltPrivate failed, so that we can interrupt on the next natural + // stop. + interrupt_requested = true; + } else { + // This can happen when someone (e.g. Process::Halt) sees that we are + // running and sends an interrupt request, but the process actually + // stops before we receive it. In that case, we can just ignore the + // request. We use m_last_broadcast_state, because the Stopped event + // may not have been popped of the event queue yet, which is when the + // public state gets updated. + LLDB_LOGF(log, + "Process::%s ignoring interrupt as we have already stopped.", + __FUNCTION__); + } + continue; + } + + const StateType internal_state = + Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + + if (internal_state != eStateInvalid) { + if (m_clear_thread_plans_on_stop && + StateIsStoppedState(internal_state, true)) { + m_clear_thread_plans_on_stop = false; + m_thread_list.DiscardThreadPlans(); + } + + if (interrupt_requested) { + if (StateIsStoppedState(internal_state, true)) { + // We requested the interrupt, so mark this as such in the stop event + // so clients can tell an interrupted process from a natural stop + ProcessEventData::SetInterruptedInEvent(event_sp.get(), true); + interrupt_requested = false; + } else if (log) { + LLDB_LOGF(log, + "Process::%s interrupt_requested, but a non-stopped " + "state '%s' received.", + __FUNCTION__, StateAsCString(internal_state)); + } + } + + HandlePrivateEvent(event_sp); + } + + if (internal_state == eStateInvalid || internal_state == eStateExited || + internal_state == eStateDetached) { + LLDB_LOGF(log, + "Process::%s (arg = %p, pid = %" PRIu64 + ") about to exit with internal state %s...", + __FUNCTION__, static_cast(this), GetID(), + StateAsCString(internal_state)); + + break; + } + } + + // Verify log is still enabled before attempting to write to it... + LLDB_LOGF(log, "Process::%s (arg = %p, pid = %" PRIu64 ") thread exiting...", + __FUNCTION__, static_cast(this), GetID()); + + // If we are a secondary thread, then the primary thread we are working for + // will have already acquired the public_run_lock, and isn't done with what + // it was doing yet, so don't try to change it on the way out. + if (!is_secondary_thread) + m_public_run_lock.SetStopped(); + return {}; + } + + // Process Event Data + + Process::ProcessEventData::ProcessEventData() : EventData(), m_process_wp() {} + + Process::ProcessEventData::ProcessEventData(const ProcessSP &process_sp, + StateType state) + : EventData(), m_process_wp(), m_state(state), m_restarted(false), + m_update_state(0), m_interrupted(false) { + if (process_sp) + m_process_wp = process_sp; + } + + Process::ProcessEventData::~ProcessEventData() = default; + + ConstString Process::ProcessEventData::GetFlavorString() { + static ConstString g_flavor("Process::ProcessEventData"); + return g_flavor; + } + + ConstString Process::ProcessEventData::GetFlavor() const { + return ProcessEventData::GetFlavorString(); + } + + bool Process::ProcessEventData::ShouldStop(Event *event_ptr, + bool &found_valid_stopinfo) { + found_valid_stopinfo = false; + + ProcessSP process_sp(m_process_wp.lock()); + if (!process_sp) + return false; + + ThreadList &curr_thread_list = process_sp->GetThreadList(); + uint32_t num_threads = curr_thread_list.GetSize(); + uint32_t idx; + + // The actions might change one of the thread's stop_info's opinions about + // whether we should stop the process, so we need to query that as we go. + + // One other complication here, is that we try to catch any case where the + // target has run (except for expressions) and immediately exit, but if we + // get that wrong (which is possible) then the thread list might have + // changed, and that would cause our iteration here to crash. We could + // make a copy of the thread list, but we'd really like to also know if it + // has changed at all, so we make up a vector of the thread ID's and check + // what we get back against this list & bag out if anything differs. + ThreadList not_suspended_thread_list(process_sp.get()); + std::vector thread_index_array(num_threads); + uint32_t not_suspended_idx = 0; + for (idx = 0; idx < num_threads; ++idx) { + lldb::ThreadSP thread_sp = curr_thread_list.GetThreadAtIndex(idx); + + /* + Filter out all suspended threads, they could not be the reason + of stop and no need to perform any actions on them. + */ + if (thread_sp->GetResumeState() != eStateSuspended) { + not_suspended_thread_list.AddThread(thread_sp); + thread_index_array[not_suspended_idx] = thread_sp->GetIndexID(); + not_suspended_idx++; + } + } + + // Use this to track whether we should continue from here. We will only + // continue the target running if no thread says we should stop. Of course + // if some thread's PerformAction actually sets the target running, then it + // doesn't matter what the other threads say... + + bool still_should_stop = false; + + // Sometimes - for instance if we have a bug in the stub we are talking to, + // we stop but no thread has a valid stop reason. In that case we should + // just stop, because we have no way of telling what the right thing to do + // is, and it's better to let the user decide than continue behind their + // backs. + + for (idx = 0; idx < not_suspended_thread_list.GetSize(); ++idx) { + curr_thread_list = process_sp->GetThreadList(); + if (curr_thread_list.GetSize() != num_threads) { + Log *log(GetLog(LLDBLog::Step | LLDBLog::Process)); + LLDB_LOGF( + log, + "Number of threads changed from %u to %u while processing event.", + num_threads, curr_thread_list.GetSize()); + break; + } + + lldb::ThreadSP thread_sp = not_suspended_thread_list.GetThreadAtIndex(idx); + + if (thread_sp->GetIndexID() != thread_index_array[idx]) { + Log *log(GetLog(LLDBLog::Step | LLDBLog::Process)); + LLDB_LOGF(log, + "The thread at position %u changed from %u to %u while " + "processing event.", + idx, thread_index_array[idx], thread_sp->GetIndexID()); + break; + } + + StopInfoSP stop_info_sp = thread_sp->GetStopInfo(); + if (stop_info_sp && stop_info_sp->IsValid()) { + found_valid_stopinfo = true; + bool this_thread_wants_to_stop; + if (stop_info_sp->GetOverrideShouldStop()) { + this_thread_wants_to_stop = + stop_info_sp->GetOverriddenShouldStopValue(); + } else { + stop_info_sp->PerformAction(event_ptr); + // The stop action might restart the target. If it does, then we + // want to mark that in the event so that whoever is receiving it + // will know to wait for the running event and reflect that state + // appropriately. We also need to stop processing actions, since they + // aren't expecting the target to be running. + + // FIXME: we might have run. + if (stop_info_sp->HasTargetRunSinceMe()) { + SetRestarted(true); + break; + } + + this_thread_wants_to_stop = stop_info_sp->ShouldStop(event_ptr); + } + + if (!still_should_stop) + still_should_stop = this_thread_wants_to_stop; + } + } + + return still_should_stop; + } + + void Process::ProcessEventData::DoOnRemoval(Event *event_ptr) { + ProcessSP process_sp(m_process_wp.lock()); + + if (!process_sp) + return; + + // This function gets called twice for each event, once when the event gets + // pulled off of the private process event queue, and then any number of + // times, first when it gets pulled off of the public event queue, then other + // times when we're pretending that this is where we stopped at the end of + // expression evaluation. m_update_state is used to distinguish these three + // cases; it is 0 when we're just pulling it off for private handling, and > + // 1 for expression evaluation, and we don't want to do the breakpoint + // command handling then. + if (m_update_state != 1) + return; + + process_sp->SetPublicState( + m_state, Process::ProcessEventData::GetRestartedFromEvent(event_ptr)); + + if (m_state == eStateStopped && !m_restarted) { + // Let process subclasses know we are about to do a public stop and do + // anything they might need to in order to speed up register and memory + // accesses. + process_sp->WillPublicStop(); + } + + // If this is a halt event, even if the halt stopped with some reason other + // than a plain interrupt (e.g. we had already stopped for a breakpoint when + // the halt request came through) don't do the StopInfo actions, as they may + // end up restarting the process. + if (m_interrupted) + return; + + // If we're not stopped or have restarted, then skip the StopInfo actions: + if (m_state != eStateStopped || m_restarted) { + return; + } + + bool does_anybody_have_an_opinion = false; + bool still_should_stop = ShouldStop(event_ptr, does_anybody_have_an_opinion); + + if (GetRestarted()) { + return; + } + + if (!still_should_stop && does_anybody_have_an_opinion) { + // We've been asked to continue, so do that here. + SetRestarted(true); + // Use the public resume method here, since this is just extending a + // public resume. + process_sp->PrivateResume(); + } else { + bool hijacked = process_sp->IsHijackedForEvent(eBroadcastBitStateChanged) && + !process_sp->StateChangedIsHijackedForSynchronousResume(); + + if (!hijacked) { + // If we didn't restart, run the Stop Hooks here. + // Don't do that if state changed events aren't hooked up to the + // public (or SyncResume) broadcasters. StopHooks are just for + // real public stops. They might also restart the target, + // so watch for that. + if (process_sp->GetTarget().RunStopHooks()) + SetRestarted(true); + } + } + } + + void Process::ProcessEventData::Dump(Stream *s) const { + ProcessSP process_sp(m_process_wp.lock()); + + if (process_sp) + s->Printf(" process = %p (pid = %" PRIu64 "), ", + static_cast(process_sp.get()), process_sp->GetID()); + else + s->PutCString(" process = NULL, "); + + s->Printf("state = %s", StateAsCString(GetState())); + } + + const Process::ProcessEventData * + Process::ProcessEventData::GetEventDataFromEvent(const Event *event_ptr) { + if (event_ptr) { + const EventData *event_data = event_ptr->GetData(); + if (event_data && + event_data->GetFlavor() == ProcessEventData::GetFlavorString()) + return static_cast(event_ptr->GetData()); + } + return nullptr; + } + + ProcessSP + Process::ProcessEventData::GetProcessFromEvent(const Event *event_ptr) { + ProcessSP process_sp; + const ProcessEventData *data = GetEventDataFromEvent(event_ptr); + if (data) + process_sp = data->GetProcessSP(); + return process_sp; + } + + StateType Process::ProcessEventData::GetStateFromEvent(const Event *event_ptr) { + const ProcessEventData *data = GetEventDataFromEvent(event_ptr); + if (data == nullptr) + return eStateInvalid; + else + return data->GetState(); + } + + bool Process::ProcessEventData::GetRestartedFromEvent(const Event *event_ptr) { + const ProcessEventData *data = GetEventDataFromEvent(event_ptr); + if (data == nullptr) + return false; + else + return data->GetRestarted(); + } + + void Process::ProcessEventData::SetRestartedInEvent(Event *event_ptr, + bool new_value) { + ProcessEventData *data = + const_cast(GetEventDataFromEvent(event_ptr)); + if (data != nullptr) + data->SetRestarted(new_value); + } + + size_t + Process::ProcessEventData::GetNumRestartedReasons(const Event *event_ptr) { + ProcessEventData *data = + const_cast(GetEventDataFromEvent(event_ptr)); + if (data != nullptr) + return data->GetNumRestartedReasons(); + else + return 0; + } + + const char * + Process::ProcessEventData::GetRestartedReasonAtIndex(const Event *event_ptr, + size_t idx) { + ProcessEventData *data = + const_cast(GetEventDataFromEvent(event_ptr)); + if (data != nullptr) + return data->GetRestartedReasonAtIndex(idx); + else + return nullptr; + } + + void Process::ProcessEventData::AddRestartedReason(Event *event_ptr, + const char *reason) { + ProcessEventData *data = + const_cast(GetEventDataFromEvent(event_ptr)); + if (data != nullptr) + data->AddRestartedReason(reason); + } + + bool Process::ProcessEventData::GetInterruptedFromEvent( + const Event *event_ptr) { + const ProcessEventData *data = GetEventDataFromEvent(event_ptr); + if (data == nullptr) + return false; + else + return data->GetInterrupted(); + } + + void Process::ProcessEventData::SetInterruptedInEvent(Event *event_ptr, + bool new_value) { + ProcessEventData *data = + const_cast(GetEventDataFromEvent(event_ptr)); + if (data != nullptr) + data->SetInterrupted(new_value); + } + + bool Process::ProcessEventData::SetUpdateStateOnRemoval(Event *event_ptr) { + ProcessEventData *data = + const_cast(GetEventDataFromEvent(event_ptr)); + if (data) { + data->SetUpdateStateOnRemoval(); + return true; + } + return false; + } + + lldb::TargetSP Process::CalculateTarget() { return m_target_wp.lock(); } + + void Process::CalculateExecutionContext(ExecutionContext &exe_ctx) { + exe_ctx.SetTargetPtr(&GetTarget()); + exe_ctx.SetProcessPtr(this); + exe_ctx.SetThreadPtr(nullptr); + exe_ctx.SetFramePtr(nullptr); + } + + // uint32_t + // Process::ListProcessesMatchingName (const char *name, StringList &matches, + // std::vector &pids) + //{ + // return 0; + //} + // + // ArchSpec + // Process::GetArchSpecForExistingProcess (lldb::pid_t pid) + //{ + // return Host::GetArchSpecForExistingProcess (pid); + //} + // + // ArchSpec + // Process::GetArchSpecForExistingProcess (const char *process_name) + //{ + // return Host::GetArchSpecForExistingProcess (process_name); + //} + + void Process::AppendSTDOUT(const char *s, size_t len) { + std::lock_guard guard(m_stdio_communication_mutex); + m_stdout_data.append(s, len); + BroadcastEventIfUnique(eBroadcastBitSTDOUT, + new ProcessEventData(shared_from_this(), GetState())); + } + + void Process::AppendSTDERR(const char *s, size_t len) { + std::lock_guard guard(m_stdio_communication_mutex); + m_stderr_data.append(s, len); + BroadcastEventIfUnique(eBroadcastBitSTDERR, + new ProcessEventData(shared_from_this(), GetState())); + } + + void Process::BroadcastAsyncProfileData(const std::string &one_profile_data) { + std::lock_guard guard(m_profile_data_comm_mutex); + m_profile_data.push_back(one_profile_data); + BroadcastEventIfUnique(eBroadcastBitProfileData, + new ProcessEventData(shared_from_this(), GetState())); + } + + void Process::BroadcastStructuredData(const StructuredData::ObjectSP &object_sp, + const StructuredDataPluginSP &plugin_sp) { + BroadcastEvent( + eBroadcastBitStructuredData, + new EventDataStructuredData(shared_from_this(), object_sp, plugin_sp)); + } + + StructuredDataPluginSP + Process::GetStructuredDataPlugin(ConstString type_name) const { + auto find_it = m_structured_data_plugin_map.find(type_name); + if (find_it != m_structured_data_plugin_map.end()) + return find_it->second; + else + return StructuredDataPluginSP(); + } + + size_t Process::GetAsyncProfileData(char *buf, size_t buf_size, Status &error) { + std::lock_guard guard(m_profile_data_comm_mutex); + if (m_profile_data.empty()) + return 0; + + std::string &one_profile_data = m_profile_data.front(); + size_t bytes_available = one_profile_data.size(); + if (bytes_available > 0) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, "Process::GetProfileData (buf = %p, size = %" PRIu64 ")", + static_cast(buf), static_cast(buf_size)); + if (bytes_available > buf_size) { + memcpy(buf, one_profile_data.c_str(), buf_size); + one_profile_data.erase(0, buf_size); + bytes_available = buf_size; + } else { + memcpy(buf, one_profile_data.c_str(), bytes_available); + m_profile_data.erase(m_profile_data.begin()); + } + } + return bytes_available; + } + + // Process STDIO + + size_t Process::GetSTDOUT(char *buf, size_t buf_size, Status &error) { + std::lock_guard guard(m_stdio_communication_mutex); + size_t bytes_available = m_stdout_data.size(); + if (bytes_available > 0) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, "Process::GetSTDOUT (buf = %p, size = %" PRIu64 ")", + static_cast(buf), static_cast(buf_size)); + if (bytes_available > buf_size) { + memcpy(buf, m_stdout_data.c_str(), buf_size); + m_stdout_data.erase(0, buf_size); + bytes_available = buf_size; + } else { + memcpy(buf, m_stdout_data.c_str(), bytes_available); + m_stdout_data.clear(); + } + } + return bytes_available; + } + + size_t Process::GetSTDERR(char *buf, size_t buf_size, Status &error) { + std::lock_guard gaurd(m_stdio_communication_mutex); + size_t bytes_available = m_stderr_data.size(); + if (bytes_available > 0) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, "Process::GetSTDERR (buf = %p, size = %" PRIu64 ")", + static_cast(buf), static_cast(buf_size)); + if (bytes_available > buf_size) { + memcpy(buf, m_stderr_data.c_str(), buf_size); + m_stderr_data.erase(0, buf_size); + bytes_available = buf_size; + } else { + memcpy(buf, m_stderr_data.c_str(), bytes_available); + m_stderr_data.clear(); + } + } + return bytes_available; + } + + void Process::STDIOReadThreadBytesReceived(void *baton, const void *src, + size_t src_len) { + Process *process = (Process *)baton; + process->AppendSTDOUT(static_cast(src), src_len); + } + + class IOHandlerProcessSTDIO : public IOHandler { + public: + IOHandlerProcessSTDIO(Process *process, int write_fd) + : IOHandler(process->GetTarget().GetDebugger(), + IOHandler::Type::ProcessIO), + m_process(process), + m_read_file(GetInputFD(), File::eOpenOptionReadOnly, false), + m_write_file(write_fd, File::eOpenOptionWriteOnly, false) { + m_pipe.CreateNew(false); + } + + ~IOHandlerProcessSTDIO() override = default; + + // Each IOHandler gets to run until it is done. It should read data from the + // "in" and place output into "out" and "err and return when done. + void Run() override { + if (!m_read_file.IsValid() || !m_write_file.IsValid() || + !m_pipe.CanRead() || !m_pipe.CanWrite()) { + SetIsDone(true); + return; + } + + SetIsDone(false); + const int read_fd = m_read_file.GetDescriptor(); + Terminal terminal(read_fd); + TerminalState terminal_state(terminal, false); + // FIXME: error handling? + llvm::consumeError(terminal.SetCanonical(false)); + llvm::consumeError(terminal.SetEcho(false)); + // FD_ZERO, FD_SET are not supported on windows + #ifndef _WIN32 + const int pipe_read_fd = m_pipe.GetReadFileDescriptor(); + m_is_running = true; + while (!GetIsDone()) { + SelectHelper select_helper; + select_helper.FDSetRead(read_fd); + select_helper.FDSetRead(pipe_read_fd); + Status error = select_helper.Select(); + + if (error.Fail()) { + SetIsDone(true); + } else { + char ch = 0; + size_t n; + if (select_helper.FDIsSetRead(read_fd)) { + n = 1; + if (m_read_file.Read(&ch, n).Success() && n == 1) { + if (m_write_file.Write(&ch, n).Fail() || n != 1) + SetIsDone(true); + } else + SetIsDone(true); + } + if (select_helper.FDIsSetRead(pipe_read_fd)) { + size_t bytes_read; + // Consume the interrupt byte + Status error = m_pipe.Read(&ch, 1, bytes_read); + if (error.Success()) { + switch (ch) { + case 'q': + SetIsDone(true); + break; + case 'i': + if (StateIsRunningState(m_process->GetState())) + m_process->SendAsyncInterrupt(); + break; + } + } + } + } + } + m_is_running = false; + #endif + } + + void Cancel() override { + SetIsDone(true); + // Only write to our pipe to cancel if we are in + // IOHandlerProcessSTDIO::Run(). We can end up with a python command that + // is being run from the command interpreter: + // + // (lldb) step_process_thousands_of_times + // + // In this case the command interpreter will be in the middle of handling + // the command and if the process pushes and pops the IOHandler thousands + // of times, we can end up writing to m_pipe without ever consuming the + // bytes from the pipe in IOHandlerProcessSTDIO::Run() and end up + // deadlocking when the pipe gets fed up and blocks until data is consumed. + if (m_is_running) { + char ch = 'q'; // Send 'q' for quit + size_t bytes_written = 0; + m_pipe.Write(&ch, 1, bytes_written); + } + } + + bool Interrupt() override { + // Do only things that are safe to do in an interrupt context (like in a + // SIGINT handler), like write 1 byte to a file descriptor. This will + // interrupt the IOHandlerProcessSTDIO::Run() and we can look at the byte + // that was written to the pipe and then call + // m_process->SendAsyncInterrupt() from a much safer location in code. + if (m_active) { + char ch = 'i'; // Send 'i' for interrupt + size_t bytes_written = 0; + Status result = m_pipe.Write(&ch, 1, bytes_written); + return result.Success(); + } else { + // This IOHandler might be pushed on the stack, but not being run + // currently so do the right thing if we aren't actively watching for + // STDIN by sending the interrupt to the process. Otherwise the write to + // the pipe above would do nothing. This can happen when the command + // interpreter is running and gets a "expression ...". It will be on the + // IOHandler thread and sending the input is complete to the delegate + // which will cause the expression to run, which will push the process IO + // handler, but not run it. + + if (StateIsRunningState(m_process->GetState())) { + m_process->SendAsyncInterrupt(); + return true; + } + } + return false; + } + + void GotEOF() override {} + + protected: + Process *m_process; + NativeFile m_read_file; // Read from this file (usually actual STDIN for LLDB + NativeFile m_write_file; // Write to this file (usually the primary pty for + // getting io to debuggee) + Pipe m_pipe; + std::atomic m_is_running{false}; + }; + + void Process::SetSTDIOFileDescriptor(int fd) { + // First set up the Read Thread for reading/handling process I/O + m_stdio_communication.SetConnection( + std::make_unique(fd, true)); + if (m_stdio_communication.IsConnected()) { + m_stdio_communication.SetReadThreadBytesReceivedCallback( + STDIOReadThreadBytesReceived, this); + m_stdio_communication.StartReadThread(); + + // Now read thread is set up, set up input reader. + + if (!m_process_input_reader) + m_process_input_reader = + std::make_shared(this, fd); + } + } + + bool Process::ProcessIOHandlerIsActive() { + IOHandlerSP io_handler_sp(m_process_input_reader); + if (io_handler_sp) + return GetTarget().GetDebugger().IsTopIOHandler(io_handler_sp); + return false; + } + bool Process::PushProcessIOHandler() { + IOHandlerSP io_handler_sp(m_process_input_reader); + if (io_handler_sp) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, "Process::%s pushing IO handler", __FUNCTION__); + + io_handler_sp->SetIsDone(false); + // If we evaluate an utility function, then we don't cancel the current + // IOHandler. Our IOHandler is non-interactive and shouldn't disturb the + // existing IOHandler that potentially provides the user interface (e.g. + // the IOHandler for Editline). + bool cancel_top_handler = !m_mod_id.IsRunningUtilityFunction(); + GetTarget().GetDebugger().RunIOHandlerAsync(io_handler_sp, + cancel_top_handler); + return true; + } + return false; + } + + bool Process::PopProcessIOHandler() { + IOHandlerSP io_handler_sp(m_process_input_reader); + if (io_handler_sp) + return GetTarget().GetDebugger().RemoveIOHandler(io_handler_sp); + return false; + } + + // The process needs to know about installed plug-ins + void Process::SettingsInitialize() { Thread::SettingsInitialize(); } + + void Process::SettingsTerminate() { Thread::SettingsTerminate(); } + + namespace { + // RestorePlanState is used to record the "is private", "is controlling" and + // "okay + // to discard" fields of the plan we are running, and reset it on Clean or on + // destruction. It will only reset the state once, so you can call Clean and + // then monkey with the state and it won't get reset on you again. + + class RestorePlanState { + public: + RestorePlanState(lldb::ThreadPlanSP thread_plan_sp) + : m_thread_plan_sp(thread_plan_sp), m_already_reset(false) { + if (m_thread_plan_sp) { + m_private = m_thread_plan_sp->GetPrivate(); + m_is_controlling = m_thread_plan_sp->IsControllingPlan(); + m_okay_to_discard = m_thread_plan_sp->OkayToDiscard(); + } + } + + ~RestorePlanState() { Clean(); } + + void Clean() { + if (!m_already_reset && m_thread_plan_sp) { + m_already_reset = true; + m_thread_plan_sp->SetPrivate(m_private); + m_thread_plan_sp->SetIsControllingPlan(m_is_controlling); + m_thread_plan_sp->SetOkayToDiscard(m_okay_to_discard); + } + } + + private: + lldb::ThreadPlanSP m_thread_plan_sp; + bool m_already_reset; + bool m_private; + bool m_is_controlling; + bool m_okay_to_discard; + }; + } // anonymous namespace + + static microseconds + GetOneThreadExpressionTimeout(const EvaluateExpressionOptions &options) { + const milliseconds default_one_thread_timeout(250); + + // If the overall wait is forever, then we don't need to worry about it. + if (!options.GetTimeout()) { + return options.GetOneThreadTimeout() ? *options.GetOneThreadTimeout() + : default_one_thread_timeout; + } + + // If the one thread timeout is set, use it. + if (options.GetOneThreadTimeout()) + return *options.GetOneThreadTimeout(); + + // Otherwise use half the total timeout, bounded by the + // default_one_thread_timeout. + return std::min(default_one_thread_timeout, + *options.GetTimeout() / 2); + } + + static Timeout + GetExpressionTimeout(const EvaluateExpressionOptions &options, + bool before_first_timeout) { + // If we are going to run all threads the whole time, or if we are only going + // to run one thread, we can just return the overall timeout. + if (!options.GetStopOthers() || !options.GetTryAllThreads()) + return options.GetTimeout(); + + if (before_first_timeout) + return GetOneThreadExpressionTimeout(options); + + if (!options.GetTimeout()) + return llvm::None; + else + return *options.GetTimeout() - GetOneThreadExpressionTimeout(options); + } + + static llvm::Optional + HandleStoppedEvent(lldb::tid_t thread_id, const ThreadPlanSP &thread_plan_sp, + RestorePlanState &restorer, const EventSP &event_sp, + EventSP &event_to_broadcast_sp, + const EvaluateExpressionOptions &options, + bool handle_interrupts) { + Log *log = GetLog(LLDBLog::Step | LLDBLog::Process); + + ThreadSP thread_sp = thread_plan_sp->GetTarget() + .GetProcessSP() + ->GetThreadList() + .FindThreadByID(thread_id); + if (!thread_sp) { + LLDB_LOG(log, + "The thread on which we were running the " + "expression: tid = {0}, exited while " + "the expression was running.", + thread_id); + return eExpressionThreadVanished; + } + + ThreadPlanSP plan = thread_sp->GetCompletedPlan(); + if (plan == thread_plan_sp && plan->PlanSucceeded()) { + LLDB_LOG(log, "execution completed successfully"); + + // Restore the plan state so it will get reported as intended when we are + // done. + restorer.Clean(); + return eExpressionCompleted; + } + + StopInfoSP stop_info_sp = thread_sp->GetStopInfo(); + if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonBreakpoint && + stop_info_sp->ShouldNotify(event_sp.get())) { + LLDB_LOG(log, "stopped for breakpoint: {0}.", stop_info_sp->GetDescription()); + if (!options.DoesIgnoreBreakpoints()) { + // Restore the plan state and then force Private to false. We are going + // to stop because of this plan so we need it to become a public plan or + // it won't report correctly when we continue to its termination later + // on. + restorer.Clean(); + thread_plan_sp->SetPrivate(false); + event_to_broadcast_sp = event_sp; + } + return eExpressionHitBreakpoint; + } + + if (!handle_interrupts && + Process::ProcessEventData::GetInterruptedFromEvent(event_sp.get())) + return llvm::None; + + LLDB_LOG(log, "thread plan did not successfully complete"); + if (!options.DoesUnwindOnError()) + event_to_broadcast_sp = event_sp; + return eExpressionInterrupted; + } + + ExpressionResults + Process::RunThreadPlan(ExecutionContext &exe_ctx, + lldb::ThreadPlanSP &thread_plan_sp, + const EvaluateExpressionOptions &options, + DiagnosticManager &diagnostic_manager) { + ExpressionResults return_value = eExpressionSetupError; + + std::lock_guard run_thread_plan_locker(m_run_thread_plan_lock); + + if (!thread_plan_sp) { + diagnostic_manager.PutString( + eDiagnosticSeverityError, + "RunThreadPlan called with empty thread plan."); + return eExpressionSetupError; + } + + if (!thread_plan_sp->ValidatePlan(nullptr)) { + diagnostic_manager.PutString( + eDiagnosticSeverityError, + "RunThreadPlan called with an invalid thread plan."); + return eExpressionSetupError; + } + + if (exe_ctx.GetProcessPtr() != this) { + diagnostic_manager.PutString(eDiagnosticSeverityError, + "RunThreadPlan called on wrong process."); + return eExpressionSetupError; + } + + Thread *thread = exe_ctx.GetThreadPtr(); + if (thread == nullptr) { + diagnostic_manager.PutString(eDiagnosticSeverityError, + "RunThreadPlan called with invalid thread."); + return eExpressionSetupError; + } + + // Record the thread's id so we can tell when a thread we were using + // to run the expression exits during the expression evaluation. + lldb::tid_t expr_thread_id = thread->GetID(); + + // We need to change some of the thread plan attributes for the thread plan + // runner. This will restore them when we are done: + + RestorePlanState thread_plan_restorer(thread_plan_sp); + + // We rely on the thread plan we are running returning "PlanCompleted" if + // when it successfully completes. For that to be true the plan can't be + // private - since private plans suppress themselves in the GetCompletedPlan + // call. + + thread_plan_sp->SetPrivate(false); + + // The plans run with RunThreadPlan also need to be terminal controlling plans + // or when they are done we will end up asking the plan above us whether we + // should stop, which may give the wrong answer. + + thread_plan_sp->SetIsControllingPlan(true); + thread_plan_sp->SetOkayToDiscard(false); + + // If we are running some utility expression for LLDB, we now have to mark + // this in the ProcesModID of this process. This RAII takes care of marking + // and reverting the mark it once we are done running the expression. + UtilityFunctionScope util_scope(options.IsForUtilityExpr() ? this : nullptr); + + if (m_private_state.GetValue() != eStateStopped) { + diagnostic_manager.PutString( + eDiagnosticSeverityError, + "RunThreadPlan called while the private state was not stopped."); + return eExpressionSetupError; + } + + // Save the thread & frame from the exe_ctx for restoration after we run + const uint32_t thread_idx_id = thread->GetIndexID(); + StackFrameSP selected_frame_sp = thread->GetSelectedFrame(); + if (!selected_frame_sp) { + thread->SetSelectedFrame(nullptr); + selected_frame_sp = thread->GetSelectedFrame(); + if (!selected_frame_sp) { + diagnostic_manager.Printf( + eDiagnosticSeverityError, + "RunThreadPlan called without a selected frame on thread %d", + thread_idx_id); + return eExpressionSetupError; + } + } + + // Make sure the timeout values make sense. The one thread timeout needs to + // be smaller than the overall timeout. + if (options.GetOneThreadTimeout() && options.GetTimeout() && + *options.GetTimeout() < *options.GetOneThreadTimeout()) { + diagnostic_manager.PutString(eDiagnosticSeverityError, + "RunThreadPlan called with one thread " + "timeout greater than total timeout"); + return eExpressionSetupError; + } + + StackID ctx_frame_id = selected_frame_sp->GetStackID(); + + // N.B. Running the target may unset the currently selected thread and frame. + // We don't want to do that either, so we should arrange to reset them as + // well. + + lldb::ThreadSP selected_thread_sp = GetThreadList().GetSelectedThread(); + + uint32_t selected_tid; + StackID selected_stack_id; + if (selected_thread_sp) { + selected_tid = selected_thread_sp->GetIndexID(); + selected_stack_id = selected_thread_sp->GetSelectedFrame()->GetStackID(); + } else { + selected_tid = LLDB_INVALID_THREAD_ID; + } + + HostThread backup_private_state_thread; + lldb::StateType old_state = eStateInvalid; + lldb::ThreadPlanSP stopper_base_plan_sp; + + Log *log(GetLog(LLDBLog::Step | LLDBLog::Process)); + if (m_private_state_thread.EqualsThread(Host::GetCurrentThread())) { + // Yikes, we are running on the private state thread! So we can't wait for + // public events on this thread, since we are the thread that is generating + // public events. The simplest thing to do is to spin up a temporary thread + // to handle private state thread events while we are fielding public + // events here. + LLDB_LOGF(log, "Running thread plan on private state thread, spinning up " + "another state thread to handle the events."); + + backup_private_state_thread = m_private_state_thread; + + // One other bit of business: we want to run just this thread plan and + // anything it pushes, and then stop, returning control here. But in the + // normal course of things, the plan above us on the stack would be given a + // shot at the stop event before deciding to stop, and we don't want that. + // So we insert a "stopper" base plan on the stack before the plan we want + // to run. Since base plans always stop and return control to the user, + // that will do just what we want. + stopper_base_plan_sp.reset(new ThreadPlanBase(*thread)); + thread->QueueThreadPlan(stopper_base_plan_sp, false); + // Have to make sure our public state is stopped, since otherwise the + // reporting logic below doesn't work correctly. + old_state = m_public_state.GetValue(); + m_public_state.SetValueNoLock(eStateStopped); + + // Now spin up the private state thread: + StartPrivateStateThread(true); + } + + thread->QueueThreadPlan( + thread_plan_sp, false); // This used to pass "true" does that make sense? + + if (options.GetDebug()) { + // In this case, we aren't actually going to run, we just want to stop + // right away. Flush this thread so we will refetch the stacks and show the + // correct backtrace. + // FIXME: To make this prettier we should invent some stop reason for this, + // but that + // is only cosmetic, and this functionality is only of use to lldb + // developers who can live with not pretty... + thread->Flush(); + return eExpressionStoppedForDebug; + } + + ListenerSP listener_sp( + Listener::MakeListener("lldb.process.listener.run-thread-plan")); + + lldb::EventSP event_to_broadcast_sp; + + { + // This process event hijacker Hijacks the Public events and its destructor + // makes sure that the process events get restored on exit to the function. + // + // If the event needs to propagate beyond the hijacker (e.g., the process + // exits during execution), then the event is put into + // event_to_broadcast_sp for rebroadcasting. + + ProcessEventHijacker run_thread_plan_hijacker(*this, listener_sp); + + if (log) { + StreamString s; + thread_plan_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); + LLDB_LOGF(log, + "Process::RunThreadPlan(): Resuming thread %u - 0x%4.4" PRIx64 + " to run thread plan \"%s\".", + thread_idx_id, expr_thread_id, s.GetData()); + } + + bool got_event; + lldb::EventSP event_sp; + lldb::StateType stop_state = lldb::eStateInvalid; + + bool before_first_timeout = true; // This is set to false the first time + // that we have to halt the target. + bool do_resume = true; + bool handle_running_event = true; + + // This is just for accounting: + uint32_t num_resumes = 0; + + // If we are going to run all threads the whole time, or if we are only + // going to run one thread, then we don't need the first timeout. So we + // pretend we are after the first timeout already. + if (!options.GetStopOthers() || !options.GetTryAllThreads()) + before_first_timeout = false; + + LLDB_LOGF(log, "Stop others: %u, try all: %u, before_first: %u.\n", + options.GetStopOthers(), options.GetTryAllThreads(), + before_first_timeout); + + // This isn't going to work if there are unfetched events on the queue. Are + // there cases where we might want to run the remaining events here, and + // then try to call the function? That's probably being too tricky for our + // own good. + + Event *other_events = listener_sp->PeekAtNextEvent(); + if (other_events != nullptr) { + diagnostic_manager.PutString( + eDiagnosticSeverityError, + "RunThreadPlan called with pending events on the queue."); + return eExpressionSetupError; + } + + // We also need to make sure that the next event is delivered. We might be + // calling a function as part of a thread plan, in which case the last + // delivered event could be the running event, and we don't want event + // coalescing to cause us to lose OUR running event... + ForceNextEventDelivery(); + + // This while loop must exit out the bottom, there's cleanup that we need to do + // when we are done. So don't call return anywhere within it. + + #ifdef LLDB_RUN_THREAD_HALT_WITH_EVENT + // It's pretty much impossible to write test cases for things like: One + // thread timeout expires, I go to halt, but the process already stopped on + // the function call stop breakpoint. Turning on this define will make us + // not fetch the first event till after the halt. So if you run a quick + // function, it will have completed, and the completion event will be + // waiting, when you interrupt for halt. The expression evaluation should + // still succeed. + bool miss_first_event = true; + #endif + while (true) { + // We usually want to resume the process if we get to the top of the + // loop. The only exception is if we get two running events with no + // intervening stop, which can happen, we will just wait for then next + // stop event. + LLDB_LOGF(log, + "Top of while loop: do_resume: %i handle_running_event: %i " + "before_first_timeout: %i.", + do_resume, handle_running_event, before_first_timeout); + + if (do_resume || handle_running_event) { + // Do the initial resume and wait for the running event before going + // further. + + if (do_resume) { + num_resumes++; + Status resume_error = PrivateResume(); + if (!resume_error.Success()) { + diagnostic_manager.Printf( + eDiagnosticSeverityError, + "couldn't resume inferior the %d time: \"%s\".", num_resumes, + resume_error.AsCString()); + return_value = eExpressionSetupError; + break; + } + } + + got_event = + listener_sp->GetEvent(event_sp, GetUtilityExpressionTimeout()); + if (!got_event) { + LLDB_LOGF(log, + "Process::RunThreadPlan(): didn't get any event after " + "resume %" PRIu32 ", exiting.", + num_resumes); + + diagnostic_manager.Printf(eDiagnosticSeverityError, + "didn't get any event after resume %" PRIu32 + ", exiting.", + num_resumes); + return_value = eExpressionSetupError; + break; + } + + stop_state = + Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + + if (stop_state != eStateRunning) { + bool restarted = false; + + if (stop_state == eStateStopped) { + restarted = Process::ProcessEventData::GetRestartedFromEvent( + event_sp.get()); + LLDB_LOGF( + log, + "Process::RunThreadPlan(): didn't get running event after " + "resume %d, got %s instead (restarted: %i, do_resume: %i, " + "handle_running_event: %i).", + num_resumes, StateAsCString(stop_state), restarted, do_resume, + handle_running_event); + } + + if (restarted) { + // This is probably an overabundance of caution, I don't think I + // should ever get a stopped & restarted event here. But if I do, + // the best thing is to Halt and then get out of here. + const bool clear_thread_plans = false; + const bool use_run_lock = false; + Halt(clear_thread_plans, use_run_lock); + } + + diagnostic_manager.Printf( + eDiagnosticSeverityError, + "didn't get running event after initial resume, got %s instead.", + StateAsCString(stop_state)); + return_value = eExpressionSetupError; + break; + } + + if (log) + log->PutCString("Process::RunThreadPlan(): resuming succeeded."); + // We need to call the function synchronously, so spin waiting for it + // to return. If we get interrupted while executing, we're going to + // lose our context, and won't be able to gather the result at this + // point. We set the timeout AFTER the resume, since the resume takes + // some time and we don't want to charge that to the timeout. + } else { + if (log) + log->PutCString("Process::RunThreadPlan(): waiting for next event."); + } + + do_resume = true; + handle_running_event = true; + + // Now wait for the process to stop again: + event_sp.reset(); + + Timeout timeout = + GetExpressionTimeout(options, before_first_timeout); + if (log) { + if (timeout) { + auto now = system_clock::now(); + LLDB_LOGF(log, + "Process::RunThreadPlan(): about to wait - now is %s - " + "endpoint is %s", + llvm::to_string(now).c_str(), + llvm::to_string(now + *timeout).c_str()); + } else { + LLDB_LOGF(log, "Process::RunThreadPlan(): about to wait forever."); + } + } + + #ifdef LLDB_RUN_THREAD_HALT_WITH_EVENT + // See comment above... + if (miss_first_event) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + miss_first_event = false; + got_event = false; + } else + #endif + got_event = listener_sp->GetEvent(event_sp, timeout); + + if (got_event) { + if (event_sp) { + bool keep_going = false; + if (event_sp->GetType() == eBroadcastBitInterrupt) { + const bool clear_thread_plans = false; + const bool use_run_lock = false; + Halt(clear_thread_plans, use_run_lock); + return_value = eExpressionInterrupted; + diagnostic_manager.PutString(eDiagnosticSeverityRemark, + "execution halted by user interrupt."); + LLDB_LOGF(log, "Process::RunThreadPlan(): Got interrupted by " + "eBroadcastBitInterrupted, exiting."); + break; + } else { + stop_state = + Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + LLDB_LOGF(log, + "Process::RunThreadPlan(): in while loop, got event: %s.", + StateAsCString(stop_state)); + + switch (stop_state) { + case lldb::eStateStopped: { + if (Process::ProcessEventData::GetRestartedFromEvent( + event_sp.get())) { + // If we were restarted, we just need to go back up to fetch + // another event. + LLDB_LOGF(log, "Process::RunThreadPlan(): Got a stop and " + "restart, so we'll continue waiting."); + keep_going = true; + do_resume = false; + handle_running_event = true; + } else { + const bool handle_interrupts = true; + return_value = *HandleStoppedEvent( + expr_thread_id, thread_plan_sp, thread_plan_restorer, + event_sp, event_to_broadcast_sp, options, + handle_interrupts); + if (return_value == eExpressionThreadVanished) + keep_going = false; + } + } break; + + case lldb::eStateRunning: + // This shouldn't really happen, but sometimes we do get two + // running events without an intervening stop, and in that case + // we should just go back to waiting for the stop. + do_resume = false; + keep_going = true; + handle_running_event = false; + break; + + default: + LLDB_LOGF(log, + "Process::RunThreadPlan(): execution stopped with " + "unexpected state: %s.", + StateAsCString(stop_state)); + + if (stop_state == eStateExited) + event_to_broadcast_sp = event_sp; + + diagnostic_manager.PutString( + eDiagnosticSeverityError, + "execution stopped with unexpected state."); + return_value = eExpressionInterrupted; + break; + } + } + + if (keep_going) + continue; + else + break; + } else { + if (log) + log->PutCString("Process::RunThreadPlan(): got_event was true, but " + "the event pointer was null. How odd..."); + return_value = eExpressionInterrupted; + break; + } + } else { + // If we didn't get an event that means we've timed out... We will + // interrupt the process here. Depending on what we were asked to do + // we will either exit, or try with all threads running for the same + // timeout. + + if (log) { + if (options.GetTryAllThreads()) { + if (before_first_timeout) { + LLDB_LOG(log, + "Running function with one thread timeout timed out."); + } else + LLDB_LOG(log, "Restarting function with all threads enabled and " + "timeout: {0} timed out, abandoning execution.", + timeout); + } else + LLDB_LOG(log, "Running function with timeout: {0} timed out, " + "abandoning execution.", + timeout); + } + + // It is possible that between the time we issued the Halt, and we get + // around to calling Halt the target could have stopped. That's fine, + // Halt will figure that out and send the appropriate Stopped event. + // BUT it is also possible that we stopped & restarted (e.g. hit a + // signal with "stop" set to false.) In + // that case, we'll get the stopped & restarted event, and we should go + // back to waiting for the Halt's stopped event. That's what this + // while loop does. + + bool back_to_top = true; + uint32_t try_halt_again = 0; + bool do_halt = true; + const uint32_t num_retries = 5; + while (try_halt_again < num_retries) { + Status halt_error; + if (do_halt) { + LLDB_LOGF(log, "Process::RunThreadPlan(): Running Halt."); + const bool clear_thread_plans = false; + const bool use_run_lock = false; + Halt(clear_thread_plans, use_run_lock); + } + if (halt_error.Success()) { + if (log) + log->PutCString("Process::RunThreadPlan(): Halt succeeded."); + + got_event = + listener_sp->GetEvent(event_sp, GetUtilityExpressionTimeout()); + + if (got_event) { + stop_state = + Process::ProcessEventData::GetStateFromEvent(event_sp.get()); + if (log) { + LLDB_LOGF(log, + "Process::RunThreadPlan(): Stopped with event: %s", + StateAsCString(stop_state)); + if (stop_state == lldb::eStateStopped && + Process::ProcessEventData::GetInterruptedFromEvent( + event_sp.get())) + log->PutCString(" Event was the Halt interruption event."); + } + + if (stop_state == lldb::eStateStopped) { + if (Process::ProcessEventData::GetRestartedFromEvent( + event_sp.get())) { + if (log) + log->PutCString("Process::RunThreadPlan(): Went to halt " + "but got a restarted event, there must be " + "an un-restarted stopped event so try " + "again... " + "Exiting wait loop."); + try_halt_again++; + do_halt = false; + continue; + } + + // Between the time we initiated the Halt and the time we + // delivered it, the process could have already finished its + // job. Check that here: + const bool handle_interrupts = false; + if (auto result = HandleStoppedEvent( + expr_thread_id, thread_plan_sp, thread_plan_restorer, + event_sp, event_to_broadcast_sp, options, + handle_interrupts)) { + return_value = *result; + back_to_top = false; + break; + } + + if (!options.GetTryAllThreads()) { + if (log) + log->PutCString("Process::RunThreadPlan(): try_all_threads " + "was false, we stopped so now we're " + "quitting."); + return_value = eExpressionInterrupted; + back_to_top = false; + break; + } + + if (before_first_timeout) { + // Set all the other threads to run, and return to the top of + // the loop, which will continue; + before_first_timeout = false; + thread_plan_sp->SetStopOthers(false); + if (log) + log->PutCString( + "Process::RunThreadPlan(): about to resume."); + + back_to_top = true; + break; + } else { + // Running all threads failed, so return Interrupted. + if (log) + log->PutCString("Process::RunThreadPlan(): running all " + "threads timed out."); + return_value = eExpressionInterrupted; + back_to_top = false; + break; + } + } + } else { + if (log) + log->PutCString("Process::RunThreadPlan(): halt said it " + "succeeded, but I got no event. " + "I'm getting out of here passing Interrupted."); + return_value = eExpressionInterrupted; + back_to_top = false; + break; + } + } else { + try_halt_again++; + continue; + } + } + + if (!back_to_top || try_halt_again > num_retries) + break; + else + continue; + } + } // END WAIT LOOP + + // If we had to start up a temporary private state thread to run this + // thread plan, shut it down now. + if (backup_private_state_thread.IsJoinable()) { + StopPrivateStateThread(); + Status error; + m_private_state_thread = backup_private_state_thread; + if (stopper_base_plan_sp) { + thread->DiscardThreadPlansUpToPlan(stopper_base_plan_sp); + } + if (old_state != eStateInvalid) + m_public_state.SetValueNoLock(old_state); + } + + // If our thread went away on us, we need to get out of here without + // doing any more work. We don't have to clean up the thread plan, that + // will have happened when the Thread was destroyed. + if (return_value == eExpressionThreadVanished) { + return return_value; + } + + if (return_value != eExpressionCompleted && log) { + // Print a backtrace into the log so we can figure out where we are: + StreamString s; + s.PutCString("Thread state after unsuccessful completion: \n"); + thread->GetStackFrameStatus(s, 0, UINT32_MAX, true, UINT32_MAX); + log->PutString(s.GetString()); + } + // Restore the thread state if we are going to discard the plan execution. + // There are three cases where this could happen: 1) The execution + // successfully completed 2) We hit a breakpoint, and ignore_breakpoints + // was true 3) We got some other error, and discard_on_error was true + bool should_unwind = (return_value == eExpressionInterrupted && + options.DoesUnwindOnError()) || + (return_value == eExpressionHitBreakpoint && + options.DoesIgnoreBreakpoints()); + + if (return_value == eExpressionCompleted || should_unwind) { + thread_plan_sp->RestoreThreadState(); + } + + // Now do some processing on the results of the run: + if (return_value == eExpressionInterrupted || + return_value == eExpressionHitBreakpoint) { + if (log) { + StreamString s; + if (event_sp) + event_sp->Dump(&s); + else { + log->PutCString("Process::RunThreadPlan(): Stop event that " + "interrupted us is NULL."); + } + + StreamString ts; + + const char *event_explanation = nullptr; + + do { + if (!event_sp) { + event_explanation = ""; + break; + } else if (event_sp->GetType() == eBroadcastBitInterrupt) { + event_explanation = ""; + break; + } else { + const Process::ProcessEventData *event_data = + Process::ProcessEventData::GetEventDataFromEvent( + event_sp.get()); + + if (!event_data) { + event_explanation = ""; + break; + } + + Process *process = event_data->GetProcessSP().get(); + + if (!process) { + event_explanation = ""; + break; + } + + ThreadList &thread_list = process->GetThreadList(); + + uint32_t num_threads = thread_list.GetSize(); + uint32_t thread_index; + + ts.Printf("<%u threads> ", num_threads); + + for (thread_index = 0; thread_index < num_threads; ++thread_index) { + Thread *thread = thread_list.GetThreadAtIndex(thread_index).get(); + + if (!thread) { + ts.Printf(" "); + continue; + } + + ts.Printf("<0x%4.4" PRIx64 " ", thread->GetID()); + RegisterContext *register_context = + thread->GetRegisterContext().get(); + + if (register_context) + ts.Printf("[ip 0x%" PRIx64 "] ", register_context->GetPC()); + else + ts.Printf("[ip unknown] "); + + // Show the private stop info here, the public stop info will be + // from the last natural stop. + lldb::StopInfoSP stop_info_sp = thread->GetPrivateStopInfo(); + if (stop_info_sp) { + const char *stop_desc = stop_info_sp->GetDescription(); + if (stop_desc) + ts.PutCString(stop_desc); + } + ts.Printf(">"); + } + + event_explanation = ts.GetData(); + } + } while (false); + + if (event_explanation) + LLDB_LOGF(log, + "Process::RunThreadPlan(): execution interrupted: %s %s", + s.GetData(), event_explanation); + else + LLDB_LOGF(log, "Process::RunThreadPlan(): execution interrupted: %s", + s.GetData()); + } + + if (should_unwind) { + LLDB_LOGF(log, + "Process::RunThreadPlan: ExecutionInterrupted - " + "discarding thread plans up to %p.", + static_cast(thread_plan_sp.get())); + thread->DiscardThreadPlansUpToPlan(thread_plan_sp); + } else { + LLDB_LOGF(log, + "Process::RunThreadPlan: ExecutionInterrupted - for " + "plan: %p not discarding.", + static_cast(thread_plan_sp.get())); + } + } else if (return_value == eExpressionSetupError) { + if (log) + log->PutCString("Process::RunThreadPlan(): execution set up error."); + + if (options.DoesUnwindOnError()) { + thread->DiscardThreadPlansUpToPlan(thread_plan_sp); + } + } else { + if (thread->IsThreadPlanDone(thread_plan_sp.get())) { + if (log) + log->PutCString("Process::RunThreadPlan(): thread plan is done"); + return_value = eExpressionCompleted; + } else if (thread->WasThreadPlanDiscarded(thread_plan_sp.get())) { + if (log) + log->PutCString( + "Process::RunThreadPlan(): thread plan was discarded"); + return_value = eExpressionDiscarded; + } else { + if (log) + log->PutCString( + "Process::RunThreadPlan(): thread plan stopped in mid course"); + if (options.DoesUnwindOnError() && thread_plan_sp) { + if (log) + log->PutCString("Process::RunThreadPlan(): discarding thread plan " + "'cause unwind_on_error is set."); + thread->DiscardThreadPlansUpToPlan(thread_plan_sp); + } + } + } + + // Thread we ran the function in may have gone away because we ran the + // target Check that it's still there, and if it is put it back in the + // context. Also restore the frame in the context if it is still present. + thread = GetThreadList().FindThreadByIndexID(thread_idx_id, true).get(); + if (thread) { + exe_ctx.SetFrameSP(thread->GetFrameWithStackID(ctx_frame_id)); + } + + // Also restore the current process'es selected frame & thread, since this + // function calling may be done behind the user's back. + + if (selected_tid != LLDB_INVALID_THREAD_ID) { + if (GetThreadList().SetSelectedThreadByIndexID(selected_tid) && + selected_stack_id.IsValid()) { + // We were able to restore the selected thread, now restore the frame: + std::lock_guard guard(GetThreadList().GetMutex()); + StackFrameSP old_frame_sp = + GetThreadList().GetSelectedThread()->GetFrameWithStackID( + selected_stack_id); + if (old_frame_sp) + GetThreadList().GetSelectedThread()->SetSelectedFrame( + old_frame_sp.get()); + } + } + } + + // If the process exited during the run of the thread plan, notify everyone. + + if (event_to_broadcast_sp) { + if (log) + log->PutCString("Process::RunThreadPlan(): rebroadcasting event."); + BroadcastEvent(event_to_broadcast_sp); + } + + return return_value; + } + + const char *Process::ExecutionResultAsCString(ExpressionResults result) { + const char *result_name = ""; + + switch (result) { + case eExpressionCompleted: + result_name = "eExpressionCompleted"; + break; + case eExpressionDiscarded: + result_name = "eExpressionDiscarded"; + break; + case eExpressionInterrupted: + result_name = "eExpressionInterrupted"; + break; + case eExpressionHitBreakpoint: + result_name = "eExpressionHitBreakpoint"; + break; + case eExpressionSetupError: + result_name = "eExpressionSetupError"; + break; + case eExpressionParseError: + result_name = "eExpressionParseError"; + break; + case eExpressionResultUnavailable: + result_name = "eExpressionResultUnavailable"; + break; + case eExpressionTimedOut: + result_name = "eExpressionTimedOut"; + break; + case eExpressionStoppedForDebug: + result_name = "eExpressionStoppedForDebug"; + break; + case eExpressionThreadVanished: + result_name = "eExpressionThreadVanished"; + } + return result_name; + } + + void Process::GetStatus(Stream &strm) { + const StateType state = GetState(); + if (StateIsStoppedState(state, false)) { + if (state == eStateExited) { + int exit_status = GetExitStatus(); + const char *exit_description = GetExitDescription(); + strm.Printf("Process %" PRIu64 " exited with status = %i (0x%8.8x) %s\n", + GetID(), exit_status, exit_status, + exit_description ? exit_description : ""); + } else { + if (state == eStateConnected) + strm.Printf("Connected to remote target.\n"); + else + strm.Printf("Process %" PRIu64 " %s\n", GetID(), StateAsCString(state)); + } + } else { + strm.Printf("Process %" PRIu64 " is running.\n", GetID()); + } + } + + size_t Process::GetThreadStatus(Stream &strm, + bool only_threads_with_stop_reason, + uint32_t start_frame, uint32_t num_frames, + uint32_t num_frames_with_source, + bool stop_format) { + size_t num_thread_infos_dumped = 0; + + // You can't hold the thread list lock while calling Thread::GetStatus. That + // very well might run code (e.g. if we need it to get return values or + // arguments.) For that to work the process has to be able to acquire it. + // So instead copy the thread ID's, and look them up one by one: + + uint32_t num_threads; + std::vector thread_id_array; + // Scope for thread list locker; + { + std::lock_guard guard(GetThreadList().GetMutex()); + ThreadList &curr_thread_list = GetThreadList(); + num_threads = curr_thread_list.GetSize(); + uint32_t idx; + thread_id_array.resize(num_threads); + for (idx = 0; idx < num_threads; ++idx) + thread_id_array[idx] = curr_thread_list.GetThreadAtIndex(idx)->GetID(); + } + + for (uint32_t i = 0; i < num_threads; i++) { + ThreadSP thread_sp(GetThreadList().FindThreadByID(thread_id_array[i])); + if (thread_sp) { + if (only_threads_with_stop_reason) { + StopInfoSP stop_info_sp = thread_sp->GetStopInfo(); + if (!stop_info_sp || !stop_info_sp->IsValid()) + continue; + } + thread_sp->GetStatus(strm, start_frame, num_frames, + num_frames_with_source, + stop_format); + ++num_thread_infos_dumped; + } else { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, "Process::GetThreadStatus - thread 0x" PRIu64 + " vanished while running Thread::GetStatus."); + } + } + return num_thread_infos_dumped; + } + + void Process::AddInvalidMemoryRegion(const LoadRange ®ion) { + m_memory_cache.AddInvalidRange(region.GetRangeBase(), region.GetByteSize()); + } + + bool Process::RemoveInvalidMemoryRange(const LoadRange ®ion) { + return m_memory_cache.RemoveInvalidRange(region.GetRangeBase(), + region.GetByteSize()); + } + + void Process::AddPreResumeAction(PreResumeActionCallback callback, + void *baton) { + m_pre_resume_actions.push_back(PreResumeCallbackAndBaton(callback, baton)); + } + + bool Process::RunPreResumeActions() { + bool result = true; + while (!m_pre_resume_actions.empty()) { + struct PreResumeCallbackAndBaton action = m_pre_resume_actions.back(); + m_pre_resume_actions.pop_back(); + bool this_result = action.callback(action.baton); + if (result) + result = this_result; + } + return result; + } + + void Process::ClearPreResumeActions() { m_pre_resume_actions.clear(); } + + void Process::ClearPreResumeAction(PreResumeActionCallback callback, void *baton) + { + PreResumeCallbackAndBaton element(callback, baton); + auto found_iter = std::find(m_pre_resume_actions.begin(), m_pre_resume_actions.end(), element); + if (found_iter != m_pre_resume_actions.end()) + { + m_pre_resume_actions.erase(found_iter); + } + } + + ProcessRunLock &Process::GetRunLock() { + if (m_private_state_thread.EqualsThread(Host::GetCurrentThread())) + return m_private_run_lock; + else + return m_public_run_lock; + } + + bool Process::CurrentThreadIsPrivateStateThread() + { + return m_private_state_thread.EqualsThread(Host::GetCurrentThread()); + } + + + void Process::Flush() { + m_thread_list.Flush(); + m_extended_thread_list.Flush(); + m_extended_thread_stop_id = 0; + m_queue_list.Clear(); + m_queue_list_stop_id = 0; + } + + lldb::addr_t Process::GetCodeAddressMask() { + if (m_code_address_mask == 0) { + if (uint32_t number_of_addressable_bits = GetVirtualAddressableBits()) { + lldb::addr_t address_mask = ~((1ULL << number_of_addressable_bits) - 1); + SetCodeAddressMask(address_mask); + } + } + return m_code_address_mask; + } + + lldb::addr_t Process::GetDataAddressMask() { + if (m_data_address_mask == 0) { + if (uint32_t number_of_addressable_bits = GetVirtualAddressableBits()) { + lldb::addr_t address_mask = ~((1ULL << number_of_addressable_bits) - 1); + SetDataAddressMask(address_mask); + } + } + return m_data_address_mask; + } + + void Process::DidExec() { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, "Process::%s()", __FUNCTION__); + + Target &target = GetTarget(); + target.CleanupProcess(); + target.ClearModules(false); + m_dynamic_checkers_up.reset(); + m_abi_sp.reset(); + m_system_runtime_up.reset(); + m_os_up.reset(); + m_dyld_up.reset(); + m_jit_loaders_up.reset(); + m_image_tokens.clear(); + m_allocated_memory_cache.Clear(); + { + std::lock_guard guard(m_language_runtimes_mutex); + m_language_runtimes.clear(); + } + m_instrumentation_runtimes.clear(); + m_thread_list.DiscardThreadPlans(); + m_memory_cache.Clear(true); + DoDidExec(); + CompleteAttach(); + // Flush the process (threads and all stack frames) after running + // CompleteAttach() in case the dynamic loader loaded things in new + // locations. + Flush(); + + // After we figure out what was loaded/unloaded in CompleteAttach, we need to + // let the target know so it can do any cleanup it needs to. + target.DidExec(); + } + + addr_t Process::ResolveIndirectFunction(const Address *address, Status &error) { + if (address == nullptr) { + error.SetErrorString("Invalid address argument"); + return LLDB_INVALID_ADDRESS; + } + + addr_t function_addr = LLDB_INVALID_ADDRESS; + + addr_t addr = address->GetLoadAddress(&GetTarget()); + std::map::const_iterator iter = + m_resolved_indirect_addresses.find(addr); + if (iter != m_resolved_indirect_addresses.end()) { + function_addr = (*iter).second; + } else { + if (!CallVoidArgVoidPtrReturn(address, function_addr)) { + Symbol *symbol = address->CalculateSymbolContextSymbol(); + error.SetErrorStringWithFormat( + "Unable to call resolver for indirect function %s", + symbol ? symbol->GetName().AsCString() : ""); + function_addr = LLDB_INVALID_ADDRESS; + } else { + if (ABISP abi_sp = GetABI()) + function_addr = abi_sp->FixCodeAddress(function_addr); + m_resolved_indirect_addresses.insert( + std::pair(addr, function_addr)); + } + } + return function_addr; + } + + void Process::ModulesDidLoad(ModuleList &module_list) { + // Inform the system runtime of the modified modules. + SystemRuntime *sys_runtime = GetSystemRuntime(); + if (sys_runtime) + sys_runtime->ModulesDidLoad(module_list); + + GetJITLoaders().ModulesDidLoad(module_list); + + // Give the instrumentation runtimes a chance to be created before informing + // them of the modified modules. + InstrumentationRuntime::ModulesDidLoad(module_list, this, + m_instrumentation_runtimes); + for (auto &runtime : m_instrumentation_runtimes) + runtime.second->ModulesDidLoad(module_list); + + // Give the language runtimes a chance to be created before informing them of + // the modified modules. + for (const lldb::LanguageType lang_type : Language::GetSupportedLanguages()) { + if (LanguageRuntime *runtime = GetLanguageRuntime(lang_type)) + runtime->ModulesDidLoad(module_list); + } + + // If we don't have an operating system plug-in, try to load one since + // loading shared libraries might cause a new one to try and load + if (!m_os_up) + LoadOperatingSystemPlugin(false); + + // Inform the structured-data plugins of the modified modules. + for (auto pair : m_structured_data_plugin_map) { + if (pair.second) + pair.second->ModulesDidLoad(*this, module_list); + } + } + + void Process::PrintWarning(uint64_t warning_type, const void *repeat_key, + const char *fmt, ...) { + bool print_warning = true; + + StreamSP stream_sp = GetTarget().GetDebugger().GetAsyncOutputStream(); + if (!stream_sp) + return; + + if (repeat_key != nullptr) { + WarningsCollection::iterator it = m_warnings_issued.find(warning_type); + if (it == m_warnings_issued.end()) { + m_warnings_issued[warning_type] = WarningsPointerSet(); + m_warnings_issued[warning_type].insert(repeat_key); + } else { + if (it->second.find(repeat_key) != it->second.end()) { + print_warning = false; + } else { + it->second.insert(repeat_key); + } + } + } + + if (print_warning) { + va_list args; + va_start(args, fmt); + stream_sp->PrintfVarArg(fmt, args); + va_end(args); + } + } + + void Process::PrintWarningOptimization(const SymbolContext &sc) { + if (!GetWarningsOptimization()) + return; + if (!sc.module_sp) + return; + if (!sc.module_sp->GetFileSpec().GetFilename().IsEmpty() && sc.function && + sc.function->GetIsOptimized()) { + PrintWarning(Process::Warnings::eWarningsOptimization, sc.module_sp.get(), + "%s was compiled with optimization - stepping may behave " + "oddly; variables may not be available.\n", + sc.module_sp->GetFileSpec().GetFilename().GetCString()); + } + } + + void Process::PrintWarningUnsupportedLanguage(const SymbolContext &sc) { + if (!GetWarningsUnsupportedLanguage()) + return; + if (!sc.module_sp) + return; + LanguageType language = sc.GetLanguage(); + if (language == eLanguageTypeUnknown) + return; + LanguageSet plugins = + PluginManager::GetAllTypeSystemSupportedLanguagesForTypes(); + if (!plugins[language]) { + PrintWarning(Process::Warnings::eWarningsUnsupportedLanguage, + sc.module_sp.get(), + "This version of LLDB has no plugin for the language \"%s\". " + "Inspection of frame variables will be limited.\n", + Language::GetNameForLanguageType(language)); + } + } + + bool Process::GetProcessInfo(ProcessInstanceInfo &info) { + info.Clear(); + + PlatformSP platform_sp = GetTarget().GetPlatform(); + if (!platform_sp) + return false; + + return platform_sp->GetProcessInfo(GetID(), info); + } + + ThreadCollectionSP Process::GetHistoryThreads(lldb::addr_t addr) { + ThreadCollectionSP threads; + + const MemoryHistorySP &memory_history = + MemoryHistory::FindPlugin(shared_from_this()); + + if (!memory_history) { + return threads; + } + + threads = std::make_shared( + memory_history->GetHistoryThreads(addr)); + + return threads; + } + + InstrumentationRuntimeSP + Process::GetInstrumentationRuntime(lldb::InstrumentationRuntimeType type) { + InstrumentationRuntimeCollection::iterator pos; + pos = m_instrumentation_runtimes.find(type); + if (pos == m_instrumentation_runtimes.end()) { + return InstrumentationRuntimeSP(); + } else + return (*pos).second; + } + + bool Process::GetModuleSpec(const FileSpec &module_file_spec, + const ArchSpec &arch, ModuleSpec &module_spec) { + module_spec.Clear(); + return false; + } + + size_t Process::AddImageToken(lldb::addr_t image_ptr) { + m_image_tokens.push_back(image_ptr); + return m_image_tokens.size() - 1; + } + + lldb::addr_t Process::GetImagePtrFromToken(size_t token) const { + if (token < m_image_tokens.size()) + return m_image_tokens[token]; + return LLDB_INVALID_ADDRESS; + } + + void Process::ResetImageToken(size_t token) { + if (token < m_image_tokens.size()) + m_image_tokens[token] = LLDB_INVALID_ADDRESS; + } + + Address + Process::AdvanceAddressToNextBranchInstruction(Address default_stop_addr, + AddressRange range_bounds) { + Target &target = GetTarget(); + DisassemblerSP disassembler_sp; + InstructionList *insn_list = nullptr; + + Address retval = default_stop_addr; + + if (!target.GetUseFastStepping()) + return retval; + if (!default_stop_addr.IsValid()) + return retval; + + const char *plugin_name = nullptr; + const char *flavor = nullptr; + disassembler_sp = Disassembler::DisassembleRange( + target.GetArchitecture(), plugin_name, flavor, GetTarget(), range_bounds); + if (disassembler_sp) + insn_list = &disassembler_sp->GetInstructionList(); + + if (insn_list == nullptr) { + return retval; + } + + size_t insn_offset = + insn_list->GetIndexOfInstructionAtAddress(default_stop_addr); + if (insn_offset == UINT32_MAX) { + return retval; + } + + uint32_t branch_index = insn_list->GetIndexOfNextBranchInstruction( + insn_offset, false /* ignore_calls*/, nullptr); + if (branch_index == UINT32_MAX) { + return retval; + } + + if (branch_index > insn_offset) { + Address next_branch_insn_address = + insn_list->GetInstructionAtIndex(branch_index)->GetAddress(); + if (next_branch_insn_address.IsValid() && + range_bounds.ContainsFileAddress(next_branch_insn_address)) { + retval = next_branch_insn_address; + } + } + + return retval; + } + + Status Process::GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) { + if (const lldb::ABISP &abi = GetABI()) + load_addr = abi->FixDataAddress(load_addr); + return DoGetMemoryRegionInfo(load_addr, range_info); + } + + Status Process::GetMemoryRegions(lldb_private::MemoryRegionInfos ®ion_list) { + Status error; + + lldb::addr_t range_end = 0; + const lldb::ABISP &abi = GetABI(); + + region_list.clear(); + do { + lldb_private::MemoryRegionInfo region_info; + error = GetMemoryRegionInfo(range_end, region_info); + // GetMemoryRegionInfo should only return an error if it is unimplemented. + if (error.Fail()) { + region_list.clear(); + break; + } + + // We only check the end address, not start and end, because we assume that + // the start will not have non-address bits until the first unmappable + // region. We will have exited the loop by that point because the previous + // region, the last mappable region, will have non-address bits in its end + // address. + range_end = region_info.GetRange().GetRangeEnd(); + if (region_info.GetMapped() == MemoryRegionInfo::eYes) { + region_list.push_back(std::move(region_info)); + } + } while ( + // For a process with no non-address bits, all address bits + // set means the end of memory. + range_end != LLDB_INVALID_ADDRESS && + // If we have non-address bits and some are set then the end + // is at or beyond the end of mappable memory. + !(abi && (abi->FixDataAddress(range_end) != range_end))); + + return error; + } + + Status + Process::ConfigureStructuredData(ConstString type_name, + const StructuredData::ObjectSP &config_sp) { + // If you get this, the Process-derived class needs to implement a method to + // enable an already-reported asynchronous structured data feature. See + // ProcessGDBRemote for an example implementation over gdb-remote. + return Status("unimplemented"); + } + + void Process::MapSupportedStructuredDataPlugins( + const StructuredData::Array &supported_type_names) { + Log *log = GetLog(LLDBLog::Process); + + // Bail out early if there are no type names to map. + if (supported_type_names.GetSize() == 0) { + LLDB_LOGF(log, "Process::%s(): no structured data types supported", + __FUNCTION__); + return; + } + + // Convert StructuredData type names to ConstString instances. + std::set const_type_names; + + LLDB_LOGF(log, + "Process::%s(): the process supports the following async " + "structured data types:", + __FUNCTION__); + + supported_type_names.ForEach( + [&const_type_names, &log](StructuredData::Object *object) { + if (!object) { + // Invalid - shouldn't be null objects in the array. + return false; + } + + auto type_name = object->GetAsString(); + if (!type_name) { + // Invalid format - all type names should be strings. + return false; + } + + const_type_names.insert(ConstString(type_name->GetValue())); + LLDB_LOG(log, "- {0}", type_name->GetValue()); + return true; + }); + + // For each StructuredDataPlugin, if the plugin handles any of the types in + // the supported_type_names, map that type name to that plugin. Stop when + // we've consumed all the type names. + // FIXME: should we return an error if there are type names nobody + // supports? + for (uint32_t plugin_index = 0; !const_type_names.empty(); plugin_index++) { + auto create_instance = + PluginManager::GetStructuredDataPluginCreateCallbackAtIndex( + plugin_index); + if (!create_instance) + break; + + // Create the plugin. + StructuredDataPluginSP plugin_sp = (*create_instance)(*this); + if (!plugin_sp) { + // This plugin doesn't think it can work with the process. Move on to the + // next. + continue; + } + + // For any of the remaining type names, map any that this plugin supports. + std::vector names_to_remove; + for (auto &type_name : const_type_names) { + if (plugin_sp->SupportsStructuredDataType(type_name)) { + m_structured_data_plugin_map.insert( + std::make_pair(type_name, plugin_sp)); + names_to_remove.push_back(type_name); + LLDB_LOG(log, "using plugin {0} for type name {1}", + plugin_sp->GetPluginName(), type_name); + } + } + + // Remove the type names that were consumed by this plugin. + for (auto &type_name : names_to_remove) + const_type_names.erase(type_name); + } + } + + bool Process::RouteAsyncStructuredData( + const StructuredData::ObjectSP object_sp) { + // Nothing to do if there's no data. + if (!object_sp) + return false; + + // The contract is this must be a dictionary, so we can look up the routing + // key via the top-level 'type' string value within the dictionary. + StructuredData::Dictionary *dictionary = object_sp->GetAsDictionary(); + if (!dictionary) + return false; + + // Grab the async structured type name (i.e. the feature/plugin name). + ConstString type_name; + if (!dictionary->GetValueForKeyAsString("type", type_name)) + return false; + + // Check if there's a plugin registered for this type name. + auto find_it = m_structured_data_plugin_map.find(type_name); + if (find_it == m_structured_data_plugin_map.end()) { + // We don't have a mapping for this structured data type. + return false; + } + + // Route the structured data to the plugin. + find_it->second->HandleArrivalOfStructuredData(*this, type_name, object_sp); + return true; + } + + Status Process::UpdateAutomaticSignalFiltering() { + // Default implementation does nothign. + // No automatic signal filtering to speak of. + return Status(); + } + + UtilityFunction *Process::GetLoadImageUtilityFunction( + Platform *platform, + llvm::function_ref()> factory) { + if (platform != GetTarget().GetPlatform().get()) + return nullptr; + llvm::call_once(m_dlopen_utility_func_flag_once, + [&] { m_dlopen_utility_func_up = factory(); }); + return m_dlopen_utility_func_up.get(); + } + + llvm::Expected Process::TraceSupported() { + if (!IsLiveDebugSession()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Can't trace a non-live process."); + return llvm::make_error(); + } + + bool Process::CallVoidArgVoidPtrReturn(const Address *address, + addr_t &returned_func, + bool trap_exceptions) { + Thread *thread = GetThreadList().GetExpressionExecutionThread().get(); + if (thread == nullptr || address == nullptr) + return false; + + EvaluateExpressionOptions options; + options.SetStopOthers(true); + options.SetUnwindOnError(true); + options.SetIgnoreBreakpoints(true); + options.SetTryAllThreads(true); + options.SetDebug(false); + options.SetTimeout(GetUtilityExpressionTimeout()); + options.SetTrapExceptions(trap_exceptions); + + auto type_system_or_err = + GetTarget().GetScratchTypeSystemForLanguage(eLanguageTypeC); + if (!type_system_or_err) { + llvm::consumeError(type_system_or_err.takeError()); + return false; + } + CompilerType void_ptr_type = + type_system_or_err->GetBasicTypeFromAST(eBasicTypeVoid).GetPointerType(); + lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallFunction( + *thread, *address, void_ptr_type, llvm::ArrayRef(), options)); + if (call_plan_sp) { + DiagnosticManager diagnostics; + + StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); + if (frame) { + ExecutionContext exe_ctx; + frame->CalculateExecutionContext(exe_ctx); + ExpressionResults result = + RunThreadPlan(exe_ctx, call_plan_sp, options, diagnostics); + if (result == eExpressionCompleted) { + returned_func = + call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned( + LLDB_INVALID_ADDRESS); + + if (GetAddressByteSize() == 4) { + if (returned_func == UINT32_MAX) + return false; + } else if (GetAddressByteSize() == 8) { + if (returned_func == UINT64_MAX) + return false; + } + return true; + } + } + } + + return false; + } + + llvm::Expected Process::GetMemoryTagManager() { + Architecture *arch = GetTarget().GetArchitecturePlugin(); + const MemoryTagManager *tag_manager = + arch ? arch->GetMemoryTagManager() : nullptr; + if (!arch || !tag_manager) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "This architecture does not support memory tagging"); + } + + if (!SupportsMemoryTagging()) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Process does not support memory tagging"); + } + + return tag_manager; + } + + llvm::Expected> + Process::ReadMemoryTags(lldb::addr_t addr, size_t len) { + llvm::Expected tag_manager_or_err = + GetMemoryTagManager(); + if (!tag_manager_or_err) + return tag_manager_or_err.takeError(); + + const MemoryTagManager *tag_manager = *tag_manager_or_err; + llvm::Expected> tag_data = + DoReadMemoryTags(addr, len, tag_manager->GetAllocationTagType()); + if (!tag_data) + return tag_data.takeError(); + + return tag_manager->UnpackTagsData(*tag_data, + len / tag_manager->GetGranuleSize()); + } + + Status Process::WriteMemoryTags(lldb::addr_t addr, size_t len, + const std::vector &tags) { + llvm::Expected tag_manager_or_err = + GetMemoryTagManager(); + if (!tag_manager_or_err) + return Status(tag_manager_or_err.takeError()); + + const MemoryTagManager *tag_manager = *tag_manager_or_err; + llvm::Expected> packed_tags = + tag_manager->PackTags(tags); + if (!packed_tags) { + return Status(packed_tags.takeError()); + } + + return DoWriteMemoryTags(addr, len, tag_manager->GetAllocationTagType(), + *packed_tags); + } +diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp +index d7023a159046..883fd022ece7 100644 +--- a/lldb/source/Target/Target.cpp ++++ b/lldb/source/Target/Target.cpp +@@ -1,4512 +1,4540 @@ + //===-- 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/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/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/StreamFile.h" + #include "lldb/Core/StructuredDataImpl.h" + #include "lldb/Core/ValueObject.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/Interpreter/CommandInterpreter.h" + #include "lldb/Interpreter/CommandReturnObject.h" + #include "lldb/Interpreter/OptionGroupWatchpoint.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/Language.h" + #include "lldb/Target/LanguageRuntime.h" + #include "lldb/Target/Process.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/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/State.h" + #include "lldb/Utility/StreamString.h" + #include "lldb/Utility/Timer.h" + + #include "llvm/ADT/ScopeExit.h" + #include "llvm/ADT/SetVector.h" + + #include + #include + + using namespace lldb; + using namespace lldb_private; + + constexpr std::chrono::milliseconds EvaluateExpressionOptions::default_timeout; + + 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; + } + + ConstString &Target::GetStaticBroadcasterClass() { + static ConstString 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().AsCString()), + 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_latest_stop_hook_id(0), m_valid(true), m_suppress_stop_hooks(false), + m_is_dummy_target(is_dummy_target), + m_frame_recognizer_manager_up( + std::make_unique()) { + SetEventName(eBroadcastBitBreakpointChanged, "breakpoint-changed"); + SetEventName(eBroadcastBitModulesLoaded, "modules-loaded"); + SetEventName(eBroadcastBitModulesUnloaded, "modules-unloaded"); + SetEventName(eBroadcastBitWatchpointChanged, "watchpoint-changed"); + SetEventName(eBroadcastBitSymbolsLoaded, "symbols-loaded"); + + CheckInWithManager(); + + LLDB_LOG(GetLog(LLDBLog::Object), "{0} Target::Target()", + static_cast(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(this)); + DeleteCurrentProcess(); + } + + void Target::PrimeFromDummyTarget(Target &target) { + m_stop_hooks = target.m_stop_hooks; + + 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 (auto bp_name_entry : target.m_breakpoint_names) { + + BreakpointName *new_bp_name = new BreakpointName(*bp_name_entry.second); + AddBreakpointName(new_bp_name); + } + + m_frame_recognizer_manager_up = std::make_unique( + *target.m_frame_recognizer_manager_up); + } + + 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().GetCString()); + 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(); + // Disable watchpoints just on the debugger side. + std::unique_lock lock; + this->GetWatchpointList().GetListMutex(lock); + DisableAllWatchpoints(false); + ClearAllWatchpointHitCounts(); + ClearAllWatchpointHistoricValues(); + m_latest_stop_hook_id = 0; + } + + void Target::DeleteCurrentProcess() { + if (m_process_sp) { + m_section_load_history.Clear(); + if (m_process_sp->IsAlive()) + m_process_sp->Destroy(false); + + m_process_sp->Finalize(); + + 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); ++ ++ if (m_process_sp && m_hijacker_on_process_creation_sp) ++ m_process_sp->HijackProcessEvents(m_hijacker_on_process_creation_sp); ++ + 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.SetErrorString( + "LLDB isn't configured with REPL support for any languages."); + return REPLSP(); + } else { + err.SetErrorString( + "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.SetErrorStringWithFormat( + "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.SetErrorStringWithFormat("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 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_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_suppress_stop_hooks = false; + } + + 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; + } + + BreakpointSP Target::CreateSourceRegexBreakpoint( + const FileSpecList *containingModules, + const FileSpecList *source_file_spec_list, + const std::unordered_set &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(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; + if (!GetSourcePathMap().ReverseRemapPath(file, remapped_file)) + 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(move_to_nearest_code)); + if (!location_spec) + return nullptr; + + BreakpointResolverSP resolver_sp(new BreakpointResolverFileLine( + nullptr, offset, skip_prologue, location_spec)); + 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( + new SearchFilterForUnconstrainedSearches(shared_from_this())); + BreakpointResolverSP resolver_sp( + new 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( + new SearchFilterForUnconstrainedSearches(shared_from_this())); + BreakpointResolverSP resolver_sp(new BreakpointResolverAddress( + nullptr, file_addr, file_spec ? *file_spec : FileSpec())); + 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, 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(); + + BreakpointResolverSP resolver_sp(new BreakpointResolverName( + nullptr, func_name, func_name_type_mask, language, Breakpoint::Exact, + offset, 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 &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(); + + 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(); + + 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(shared_from_this(), + *containingModule); + } else { + if (!m_search_filter_sp) + m_search_filter_sp = + std::make_shared( + 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(shared_from_this(), + *containingModules); + } else { + if (!m_search_filter_sp) + m_search_filter_sp = + std::make_shared( + 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( + shared_from_this(), FileSpecList(), *containingSourceFiles); + } else { + filter_sp = std::make_shared( + 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(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( + 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) { + 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, const char *name, + Status &error) { + BreakpointSP bp_sp = + m_breakpoint_list.FindBreakpointByID(id.GetBreakpointID()); + if (!bp_sp) { + StreamString s; + id.GetDescription(&s, eDescriptionLevelBrief); + error.SetErrorStringWithFormat("Could not find breakpoint %s", s.GetData()); + return; + } + AddNameToBreakpoint(bp_sp, name, error); + } + + void Target::AddNameToBreakpoint(BreakpointSP &bp_sp, const char *name, + Status &error) { + if (!bp_sp) + return; + + BreakpointName *bp_name = FindBreakpointName(ConstString(name), true, error); + if (!bp_name) + return; + + bp_name->ConfigureBreakpoint(bp_sp); + bp_sp->AddName(name); + } + + void Target::AddBreakpointName(BreakpointName *bp_name) { + m_breakpoint_names.insert(std::make_pair(bp_name->GetName(), bp_name)); + } + + BreakpointName *Target::FindBreakpointName(ConstString name, bool can_create, + Status &error) { + BreakpointID::StringIsBreakpointName(name.GetStringRef(), error); + if (!error.Success()) + return nullptr; + + BreakpointNameList::iterator iter = m_breakpoint_names.find(name); + if (iter == m_breakpoint_names.end()) { + if (!can_create) { + error.SetErrorStringWithFormat("Breakpoint name \"%s\" doesn't exist and " + "can_create is false.", + name.AsCString()); + return nullptr; + } + + iter = m_breakpoint_names + .insert(std::make_pair(name, new BreakpointName(name))) + .first; + } + return (iter->second); + } + + void Target::DeleteBreakpointName(ConstString name) { + BreakpointNameList::iterator iter = m_breakpoint_names.find(name); + + if (iter != m_breakpoint_names.end()) { + const char *name_cstr = name.AsCString(); + m_breakpoint_names.erase(iter); + for (auto bp_sp : m_breakpoint_list.Breakpoints()) + bp_sp->RemoveName(name_cstr); + } + } + + void Target::RemoveNameFromBreakpoint(lldb::BreakpointSP &bp_sp, + ConstString name) { + bp_sp->RemoveName(name.AsCString()); + } + + 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> expected_vector = + m_breakpoint_list.FindBreakpointsByName(bp_name.GetName().AsCString()); + + 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 &names) { + names.clear(); + for (auto bp_name : m_breakpoint_names) { + names.push_back(bp_name.first.AsCString()); + } + llvm::sort(names.begin(), names.end()); + } + + bool Target::ProcessIsValid() { + return (m_process_sp && m_process_sp->IsAlive()); + } + + static bool CheckIfWatchpointsSupported(Target *target, Status &error) { + uint32_t num_supported_hardware_watchpoints; + Status rc = target->GetProcessSP()->GetWatchpointSupportInfo( + num_supported_hardware_watchpoints); + + // If unable to determine the # of watchpoints available, + // assume they are supported. + if (rc.Fail()) + return true; + + if (num_supported_hardware_watchpoints == 0) { + error.SetErrorStringWithFormat( + "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.SetErrorString("process is not alive"); + return wp_sp; + } + + if (addr == LLDB_INVALID_ADDRESS || size == 0) { + if (size == 0) + error.SetErrorString("cannot set a watchpoint with watch_size of 0"); + else + error.SetErrorStringWithFormat("invalid watch address: %" PRIu64, addr); + return wp_sp; + } + + if (!LLDB_WATCH_TYPE_IS_VALID(kind)) { + error.SetErrorStringWithFormat("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); + + std::unique_lock 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); + // 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.get(), notify); + m_watchpoint_list.Remove(matched_sp->GetID(), true); + } + } + + if (!wp_sp) { + wp_sp = std::make_shared(*this, addr, size, type); + wp_sp->SetWatchpointType(kind, notify); + m_watchpoint_list.Add(wp_sp, true); + } + + error = m_process_sp->EnableWatchpoint(wp_sp.get(), 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); + // See if we could provide more helpful error message. + if (!OptionGroupWatchpoint::IsWatchSizeSupported(size)) + error.SetErrorStringWithFormat( + "watch size of %" PRIu64 " is not supported", (uint64_t)size); + + 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; + } + + Status Target::SerializeBreakpointsToFile(const FileSpec &file, + const BreakpointIDList &bp_ids, + bool append) { + Status error; + + if (!file) { + error.SetErrorString("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.SetErrorStringWithFormat( + "Tried to append to invalid input file %s", path.c_str()); + return error; + } + } + } + + if (!break_store_ptr) { + break_store_sp = std::make_shared(); + 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.SetErrorStringWithFormat("Unable to open output file: %s.", + path.c_str()); + return error; + } + + std::unique_lock 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 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::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.SetErrorStringWithFormat("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 no_names; + return CreateBreakpointsFromFile(file, no_names, new_bps); + } + + Status Target::CreateBreakpointsFromFile(const FileSpec &file, + std::vector &names, + BreakpointIDList &new_bps) { + std::unique_lock 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.SetErrorStringWithFormat("Invalid JSON from input file: %s.", + file.GetPath().c_str()); + return error; + } + + StructuredData::Array *bkpt_array = input_data_sp->GetAsArray(); + if (!bkpt_array) { + error.SetErrorStringWithFormat( + "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.SetErrorStringWithFormat( + "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.SetErrorStringWithFormat( + "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.get()); + 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.get()); + 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.get()); + 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.get()); + 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.get()); + 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() { + // search for the first executable in the module list + for (size_t i = 0; i < m_images.GetSize(); ++i) { + ModuleSP module_sp = m_images.GetModuleAtIndex(i); + lldb_private::ObjectFile *obj = module_sp->GetObjectFile(); + if (obj == nullptr) + continue; + if (obj->GetType() == ObjectFile::Type::eTypeExecutable) + return module_sp; + } + // as fall back return the first module loaded + return m_images.GetModuleAtIndex(0); + } + + Module *Target::GetExecutableModulePointer() { + return GetExecutableModule().get(); + } + + static void LoadScriptingResourceForModule(const ModuleSP &module_sp, + Target *target) { + Status error; + StreamString feedback_stream; + if (module_sp && !module_sp->LoadScriptingResourceInTarget( + target, error, &feedback_stream)) { + if (error.AsCString()) + target->GetDebugger().GetErrorStream().Printf( + "unable to load scripting data for module %s - error reported was " + "%s\n", + module_sp->GetFileSpec().GetFileNameStrippingExtension().GetCString(), + error.AsCString()); + } + if (feedback_stream.GetSize()) + target->GetDebugger().GetErrorStream().Printf("%s\n", + feedback_stream.GetData()); + } + + void Target::ClearModules(bool delete_locations) { + ModulesDidUnload(m_images, delete_locations); + m_section_load_history.Clear(); + m_images.Clear(); + 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::SetExecutableModule(ModuleSP &executable_sp, + LoadDependentFiles load_dependent_files) { + Log *log = GetLog(LLDBLog::Target); + ClearModules(false); + + if (executable_sp) { + ElapsedTime elapsed(m_stats.GetCreateTime()); + LLDB_SCOPED_TIMERF("Target::SetExecutableModule (executable = '%s')", + executable_sp->GetFileSpec().GetPath().c_str()); + + const bool notify = true; + 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, + "setting architecture to {0} ({1}) based on executable file", + m_arch.GetSpec().GetArchitectureName(), + m_arch.GetSpec().GetTriple().getTriple()); + } + + FileSpecList dependent_files; + 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) { + ModuleList added_modules; + executable_objfile->GetDependentModules(dependent_files); + for (uint32_t i = 0; i < dependent_files.GetSize(); i++) { + FileSpec dependent_file_spec(dependent_files.GetFileSpecAtIndex(i)); + 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) + objfile->GetDependentModules(dependent_files); + } + } + ModulesDidLoad(added_modules); + } + } + } + + bool Target::SetArchitecture(const ArchSpec &arch_spec, bool set_platform) { + 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, false, nullptr)) { + ArchSpec platform_arch; + auto arch_platform_sp = + Platform::GetPlatformForArchitecture(other, &platform_arch); + if (arch_platform_sp) { + SetPlatform(arch_platform_sp); + if (platform_arch.IsValid()) + other = platform_arch; + } + } + } + } + + if (!missing_local_arch) { + if (m_arch.GetSpec().IsCompatibleMatch(arch_spec)) { + other.MergeFrom(m_arch.GetSpec()); + + if (m_arch.GetSpec().IsCompatibleMatch(other)) { + compatible_local_arch = true; + bool arch_changed, vendor_changed, os_changed, os_ver_changed, + env_changed; + + m_arch.GetSpec().PiecewiseTripleCompare(other, arch_changed, + vendor_changed, os_changed, + os_ver_changed, env_changed); + + if (!arch_changed && !vendor_changed && !os_changed && !env_changed) + 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, "set architecture to {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)", + arch_spec.GetArchitectureName(), + arch_spec.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); + FileSpecList search_paths = GetExecutableSearchPaths(); + Status error = ModuleList::GetSharedModule(module_spec, executable_sp, + &search_paths, nullptr, nullptr); + + if (!error.Fail() && executable_sp) { + SetExecutableModule(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) { + const size_t num_images = module_list.GetSize(); + if (m_valid && num_images) { + for (size_t idx = 0; idx < num_images; ++idx) { + ModuleSP module_sp(module_list.GetModuleAtIndex(idx)); + LoadScriptingResourceForModule(module_sp, this); + } + 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); + } + BroadcastEvent(eBroadcastBitModulesLoaded, + new TargetEventData(this->shared_from_this(), module_list)); + } + } + + 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); + BroadcastEvent(eBroadcastBitSymbolsLoaded, + new TargetEventData(this->shared_from_this(), module_list)); + } + } + + void Target::ModulesDidUnload(ModuleList &module_list, bool delete_locations) { + if (m_valid && module_list.GetSize()) { + UnloadModuleSections(module_list); + m_breakpoint_list.UpdateBreakpoints(module_list, false, delete_locations); + m_internal_breakpoint_list.UpdateBreakpoints(module_list, false, + delete_locations); + BroadcastEvent(eBroadcastBitModulesUnloaded, + new TargetEventData(this->shared_from_this(), module_list)); + } + } + + 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) { + LLDB_SCOPED_TIMER(); + 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.SetErrorString("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.SetErrorStringWithFormat("error reading data from section %s", + section_sp->GetName().GetCString()); + } else + error.SetErrorString("address isn't from a object file"); + } else + error.SetErrorString("address isn't in a module"); + } else + error.SetErrorString("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) { + error.Clear(); + + // 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 (!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 = addr.GetOffset(); // "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 have a live process that has sections loaded through + // the dynamic loader + load_addr = addr.GetOffset(); // "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 = 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 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(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.SetErrorStringWithFormatv( + "{0:F}[{1:x+}] can't be resolved, {0:F} is not currently loaded", + addr_module_sp->GetFileSpec(), resolved_addr.GetFileAddress()); + else + error.SetErrorStringWithFormat("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.SetErrorStringWithFormat( + "read memory from 0x%" PRIx64 " failed", load_addr); + else + error.SetErrorStringWithFormat( + "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; + 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. + return ReadMemoryFromFileCache(resolved_addr, dst, dst_len, error); + } + 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); + Status error; + 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(bytes_left, cache_line_bytes_left); + size_t bytes_read = ReadMemory(address, curr_dst, bytes_to_read, error, + force_live_memory); + + if (bytes_read == 0) { + result_error = 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.SetErrorString("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(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.SignExtend(byte_size * 8); + return bytes_read; + } + } else { + error.SetErrorStringWithFormat( + "byte size of %u is too large for integer scalar type", byte_size); + } + return 0; + } + + 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 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 &module_spec, bool notify, + Status *error_ptr) { + ModuleSP module_sp; + + Status error; + + // 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 + 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(); + // If there are image search path entries, try to use them first to acquire + // a suitable image. + if (m_image_search_paths.GetSize()) { + ModuleSpec transformed_spec(module_spec); + if (m_image_search_paths.RemapPath( + module_spec.GetFileSpec().GetDirectory(), + transformed_spec.GetFileSpec().GetDirectory())) { + transformed_spec.GetFileSpec().GetFilename() = + module_spec.GetFileSpec().GetFilename(); + error = ModuleList::GetSharedModule(transformed_spec, module_sp, + &search_paths, &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, &search_paths, + &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, m_process_sp.get(), module_sp, &search_paths, + &old_modules, &did_create_module); + } else { + error.SetErrorString("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->SetErrorString("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->SetErrorString("stub libraries aren't valid target " + "modules, please specify an executable"); + return ModuleSP(); + default: + if (error_ptr) + error_ptr->SetErrorString( + "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().IsEmpty() && + !module_spec.GetFileSpec().GetDirectory().IsEmpty()) { + 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) -> bool { + old_modules.push_back(found_module); + return true; + }); + } + + // Preload symbols outside of any lock, so hopefully we can do this for + // each library in parallel. + if (GetPreloadSymbols()) + module_sp->PreloadSymbols(); + + llvm::SmallVector 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()) + m_images.Append(module_sp, notify); + + for (ModuleSP &old_module_sp : replaced_modules) { + Module *old_module_ptr = old_module_sp.get(); + old_module_sp.reset(); + ModuleList::RemoveSharedModuleIfOrphaned(old_module_ptr); + } + } else + module_sp.reset(); + } + } + if (error_ptr) + *error_ptr = 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->SetExecutableModule(exe_module_sp, eLoadDependentsYes); + } + + llvm::Expected + Target::GetScratchTypeSystemForLanguage(lldb::LanguageType language, + bool create_on_demand) { + if (!m_valid) + return llvm::make_error("Invalid Target", + llvm::inconvertibleErrorCode()); + + if (language == eLanguageTypeMipsAssembler // GNU AS and LLVM use it for all + // assembly code + || 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::make_error( + "No expression support for any languages", + llvm::inconvertibleErrorCode()); + language = (LanguageType)languages_for_expressions.bitvector.find_first(); + } + } + + return m_scratch_type_system_map.GetTypeSystemForLanguage(language, this, + create_on_demand); + } + + std::vector 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. + llvm::SetVector 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 '{}' has expression support but no scratch type " + "system available", + Language::GetNameForLanguageType(language)); + else + scratch_type_systems.insert(&type_system_or_err.get()); + } + + 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 {}", + Language::GetNameForLanguageType(language)); + return nullptr; + } + + return type_system_or_err->GetPersistentExpressionState(); + } + + UserExpression *Target::GetUserExpressionForLanguage( + llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language, + Expression::ResultType desired_type, + const EvaluateExpressionOptions &options, ValueObject *ctx_obj, + Status &error) { + auto type_system_or_err = GetScratchTypeSystemForLanguage(language); + if (auto err = type_system_or_err.takeError()) { + error.SetErrorStringWithFormat( + "Could not find type system for language %s: %s", + Language::GetNameForLanguageType(language), + llvm::toString(std::move(err)).c_str()); + return nullptr; + } + + auto *user_expr = type_system_or_err->GetUserExpression( + expr, prefix, language, desired_type, options, ctx_obj); + if (!user_expr) + error.SetErrorStringWithFormat( + "Could not create an expression for language %s", + Language::GetNameForLanguageType(language)); + + 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.SetErrorStringWithFormat( + "Could not find type system for language %s: %s", + Language::GetNameForLanguageType(language), + llvm::toString(std::move(err)).c_str()); + return nullptr; + } + + auto *persistent_fn = type_system_or_err->GetFunctionCaller( + return_type, function_address, arg_value_list, name); + if (!persistent_fn) + error.SetErrorStringWithFormat( + "Could not create an expression for language %s", + Language::GetNameForLanguageType(language)); + + return persistent_fn; + } + + llvm::Expected> + 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(); + + std::unique_ptr utility_fn = + type_system_or_err->CreateUtilityFunction(std::move(expression), + std::move(name)); + if (!utility_fn) + return llvm::make_error( + llvm::StringRef("Could not create an expression for language") + + Language::GetNameForLanguageType(language), + llvm::inconvertibleErrorCode()); + + DiagnosticManager diagnostics; + if (!utility_fn->Install(diagnostics, exe_ctx)) + return llvm::make_error(diagnostics.GetString(), + llvm::inconvertibleErrorCode()); + + 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); + } + + 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; + auto on_exit = llvm::make_scope_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"); + } else { + persistent_var_sp = + type_system_or_err->GetPersistentExpressionState()->GetVariable(expr); + } + } + if (persistent_var_sp) { + result_valobj_sp = persistent_var_sp->GetValueObject(); + execution_results = eExpressionCompleted; + } else { + llvm::StringRef prefix = GetExpressionPrefixContents(); + Status error; + execution_results = UserExpression::Evaluate(exe_ctx, options, expr, prefix, + result_valobj_sp, error, + 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](TypeSystem *type_system) -> bool { + if (PersistentExpressionState *persistent_state = + type_system->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](TypeSystem *type_system) -> bool { + if (PersistentExpressionState *persistent_state = + type_system->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 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::make_error( + "No primary executable found and could not find entry point address in " + "any executable module", + llvm::inconvertibleErrorCode()); + + return llvm::make_error( + "Could not find entry point address for primary executable module \"" + + exe_module->GetFileSpec().GetFilename().GetStringRef() + "\"", + llvm::inconvertibleErrorCode()); + } + + 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; + } + + SourceManager &Target::GetSourceManager() { + if (!m_source_manager_up) + m_source_manager_up = std::make_unique(shared_from_this()); + return *m_source_manager_up; + } + + Target::StopHookSP Target::CreateStopHook(StopHook::StopHookKind kind) { + lldb::user_id_t new_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; + } + 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); + } + } + + bool Target::RunStopHooks() { + 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. + if (m_process_sp->GetState() != eStateStopped) + return false; + + if (m_stop_hooks.empty()) + return false; + + // If there aren't any active stop hooks, don't bother either. + bool any_active_hooks = false; + for (auto hook : m_stop_hooks) { + if (hook.second->IsActive()) { + any_active_hooks = true; + break; + } + } + if (!any_active_hooks) + 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; + + m_latest_stop_hook_id = last_natural_stop; + + std::vector 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. + size_t num_exe_ctx = exc_ctx_with_reasons.size(); + if (num_exe_ctx == 0) + return false; + + StreamSP output_sp = m_debugger.GetAsyncOutputStream(); + + bool auto_continue = false; + bool hooks_ran = false; + bool print_hook_header = (m_stop_hooks.size() != 1); + bool print_thread_header = (num_exe_ctx != 1); + bool should_stop = false; + bool somebody_restarted = false; + + for (auto stop_entry : m_stop_hooks) { + StopHookSP cur_hook_sp = stop_entry.second; + if (!cur_hook_sp->IsActive()) + continue; + + bool any_thread_matched = false; + for (auto exc_ctx : exc_ctx_with_reasons) { + // We detect somebody restarted in the stop-hook loop, and broke out of + // that loop back to here. So break out of here too. + if (somebody_restarted) + break; + + if (!cur_hook_sp->ExecutionContextPasses(exc_ctx)) + continue; + + // We only consult the auto-continue for a stop hook if it matched the + // specifier. + auto_continue |= cur_hook_sp->GetAutoContinue(); + + if (!hooks_ran) + hooks_ran = true; + + if (print_hook_header && !any_thread_matched) { + 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) + output_sp->Printf("-- Thread %d\n", + exc_ctx.GetThreadPtr()->GetIndexID()); + + StopHook::StopHookResult this_result = + cur_hook_sp->HandleStop(exc_ctx, output_sp); + bool this_should_stop = true; + + switch (this_result) { + case StopHook::StopHookResult::KeepStopped: + // If this hook is set to auto-continue that should override the + // HandleStop result... + if (cur_hook_sp->GetAutoContinue()) + this_should_stop = false; + else + this_should_stop = true; + + break; + case StopHook::StopHookResult::RequestContinue: + this_should_stop = false; + 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 bag out if 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()); + somebody_restarted = true; + break; + } + // If we're already restarted, stop processing stop hooks. + // 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. + if (somebody_restarted) + break; + + // If anybody wanted to stop, we should all stop. + if (!should_stop) + should_stop = this_should_stop; + } + } + + output_sp->Flush(); + + // If one of the commands in the stop hook already restarted the target, + // report that fact. + if (somebody_restarted) + return true; + + // Finally, if auto-continue was requested, do it now: + // We only compute should_stop against the hook results if a hook got to run + // which is why we have to do this conjoint test. + if ((hooks_ran && !should_stop) || auto_continue) { + 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) { + if (platform_sp->IsRemote()) { + if (platform_sp->IsConnected()) { + // 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. + 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) { + const bool is_main_executable = module_sp == GetExecutableModule(); + FileSpec local_file(module_sp->GetFileSpec()); + if (local_file) { + FileSpec remote_file(module_sp->GetRemoteInstallFileSpec()); + if (!remote_file) { + if (is_main_executable && GetAutoInstallMainExecutable()) { + // Automatically install the main executable. + remote_file = platform_sp->GetRemoteWorkingDirectory(); + remote_file.AppendPathComponent( + module_sp->GetFileSpec().GetFilename().GetCString()); + } + } + if (remote_file) { + error = platform_sp->Install(local_file, remote_file); + if (error.Success()) { + module_sp->SetPlatformFileSpec(remote_file); + if (is_main_executable) { + platform_sp->SetFilePermissions(remote_file, 0700); + if (launch_info) + launch_info->SetExecutableFile(remote_file, false); + } + } else + break; + } + } + } + } + } + } + } + return error; + } + + bool Target::ResolveLoadAddress(addr_t load_addr, Address &so_addr, + uint32_t stop_id) { + return m_section_load_history.ResolveLoadAddress(stop_id, load_addr, so_addr); + } + + 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(); } + + Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) { ++ constexpr char hijacking_listener_name[] = "lldb.Target.Launch.hijack"; ++ + 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); + + if (launch_info.IsScriptedProcess()) { + // Only copy scripted process launch options. + ProcessLaunchInfo &default_launch_info = const_cast( + GetGlobalProperties().GetProcessLaunchInfo()); + + default_launch_info.SetProcessPluginName("ScriptedProcess"); + default_launch_info.SetScriptedProcessClassName( + launch_info.GetScriptedProcessClassName()); + default_launch_info.SetScriptedProcessDictionarySP( + launch_info.GetScriptedProcessDictionarySP()); + + SetProcessLaunchInfo(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)) { + error.SetErrorString( + "can't launch in tty when launching through a remote connection"); + return error; + } + } + + 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 (synchronous_execution && !launch_info.GetHijackListener()) { ++ // The hijacker is passed this way and then set up in CreateProcess because ++ // it should be activated before Process::Launch is invoked. Platforms don't ++ // activate the existing hijacker passed via launch info on their own before ++ // they invoke Process::Launch. ++ m_hijacker_on_process_creation_sp = ++ Listener::MakeListener(hijacking_listener_name); ++ launch_info.SetHijackListener(m_hijacker_on_process_creation_sp); ++ } ++ ++ auto hijacker_on_process_creation_guard = ++ llvm::make_scope_exit([&m_hijacker_on_process_creation_sp = ++ m_hijacker_on_process_creation_sp]() { ++ m_hijacker_on_process_creation_sp.reset(); ++ }); ++ + // 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. + const char *plugin_name = launch_info.GetProcessPluginName(); + CreateProcess(launch_info.GetListener(), plugin_name, nullptr, false); + } + + // Since we didn't have a platform launch the process, launch it here. + if (m_process_sp) + error = m_process_sp->Launch(launch_info); + } + + if (!m_process_sp && error.Success()) + error.SetErrorString("failed to launch or debug process"); + + if (!error.Success()) + return error; + + auto at_exit = + llvm::make_scope_exit([&]() { m_process_sp->RestoreProcessEvents(); }); + + if (!synchronous_execution && + launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) + return error; + + ListenerSP hijack_listener_sp(launch_info.GetHijackListener()); + if (!hijack_listener_sp) { +- hijack_listener_sp = Listener::MakeListener("lldb.Target.Launch.hijack"); ++ hijack_listener_sp = Listener::MakeListener(hijacking_listener_name); + launch_info.SetHijackListener(hijack_listener_sp); + m_process_sp->HijackProcessEvents(hijack_listener_sp); + } + +- switch (m_process_sp->WaitForProcessToStop(llvm::None, nullptr, false, +- hijack_listener_sp, nullptr)) { ++ state = m_process_sp->WaitForProcessToStop(llvm::None, nullptr, false, ++ hijack_listener_sp, nullptr); ++ ++ at_exit.release(); ++ m_process_sp->RestoreProcessEvents(); ++ ++ switch (state) { + case eStateStopped: { + if (launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) + break; +- if (synchronous_execution) { ++ 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->RestoreProcessEvents(); + m_process_sp->ResumeSynchronous(stream); +- } else { +- m_process_sp->RestoreProcessEvents(); ++ else + error = m_process_sp->PrivateResume(); +- } + if (!error.Success()) { + Status error2; + error2.SetErrorStringWithFormat( + "process resume at entry point failed: %s", error.AsCString()); + error = error2; + } + } 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.SetErrorStringWithFormat( + "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.SetErrorStringWithFormat("process exited with status %i%s", + exit_status, desc.c_str()); + } break; + default: + error.SetErrorStringWithFormat("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 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 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 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 Target::GetTraceOrCreate() { + if (m_trace_sp) + return m_trace_sp; + return CreateTrace(); + } + + Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) { + 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("process attach is in progress"); + return Status("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().GetFilename() = + old_exec_module_sp->GetPlatformFileSpec().GetFilename(); + + if (!attach_info.ProcessInfoSpecified()) { + return Status("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("lldb.Target.Attach.attach.hijack"); + attach_info.SetHijackListener(hijack_listener_sp); + } + + Status error; + if (state != eStateConnected && platform_sp != nullptr && + platform_sp->CanDebugProcess()) { + SetPlatform(platform_sp); + process_sp = platform_sp->Attach(attach_info, GetDebugger(), this, error); + } else { + if (state != eStateConnected) { + const char *plugin_name = attach_info.GetProcessPluginName(); + process_sp = + CreateProcess(attach_info.GetListenerForProcess(GetDebugger()), + plugin_name, nullptr, false); + if (process_sp == nullptr) { + error.SetErrorStringWithFormat( + "failed to create process using plugin %s", + (plugin_name) ? plugin_name : "null"); + 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 { + state = process_sp->WaitForProcessToStop( + llvm::None, nullptr, false, attach_info.GetHijackListener(), stream); + process_sp->RestoreProcessEvents(); + + if (state != eStateStopped) { + const char *exit_desc = process_sp->GetExitDescription(); + if (exit_desc) + error.SetErrorStringWithFormat("%s", exit_desc); + else + error.SetErrorString( + "process did not stop (no such process or permission problem?)"); + process_sp->Destroy(false); + } + } + } + return error; + } + + 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='{1}'", + 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) { + llvm::Error Err = info.SetUpPtyRedirection(); + LLDB_LOG_ERROR(log, std::move(Err), "SetUpPtyRedirection failed: {0}"); + } + } + } + } + + // 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(*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; + } + + unsigned indent_level = s->GetIndentLevel(); + + s->SetIndentLevel(indent_level + 2); + + 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"); + s->SetIndentLevel(indent_level + 4); + m_specifier_sp->GetDescription(s, level); + s->SetIndentLevel(indent_level + 2); + } + + if (m_thread_spec_up) { + StreamString tmp; + s->Indent("Thread:\n"); + m_thread_spec_up->GetDescription(&tmp, level); + s->SetIndentLevel(indent_level + 4); + s->Indent(tmp.GetString()); + s->PutCString("\n"); + s->SetIndentLevel(indent_level + 2); + } + 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"); + s->SetIndentLevel(s->GetIndentLevel() + 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"); + } + s->SetIndentLevel(s->GetIndentLevel() - 4); + } + + // Target::StopHookCommandLine + void Target::StopHookCommandLine::SetActionFromString(const std::string &string) { + GetCommands().SplitIntoLines(string); + } + + void Target::StopHookCommandLine::SetActionFromStrings( + const std::vector &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.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( + std::string class_name, StructuredData::ObjectSP extra_args_sp) { + Status error; + + ScriptInterpreter *script_interp = + GetTarget()->GetDebugger().GetScriptInterpreter(); + if (!script_interp) { + error.SetErrorString("No script interpreter installed."); + return error; + } + + m_class_name = class_name; + m_extra_args.SetObjectSP(extra_args_sp); + + m_implementation_sp = script_interp->CreateScriptedStopHook( + GetTarget(), m_class_name.c_str(), m_extra_args, error); + + return error; + } + + 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"); + + ScriptInterpreter *script_interp = + GetTarget()->GetDebugger().GetScriptInterpreter(); + if (!script_interp) + return StopHookResult::KeepStopped; + + bool should_stop = script_interp->ScriptedStopHookHandleStop( + m_implementation_sp, exc_ctx, output_sp); + + return should_stop ? StopHookResult::KeepStopped + : StopHookResult::RequestContinue; + } + + void Target::StopHookScripted::GetSubclassDescription( + Stream *s, lldb::DescriptionLevel level) const { + if (level == eDescriptionLevelBrief) { + s->PutCString(m_class_name); + return; + } + s->Indent("Class:"); + s->Printf("%s\n", m_class_name.c_str()); + + // Now print the extra args: + // FIXME: We should use StructuredData.GetDescription on the m_extra_args + // but that seems to rely on some printing plugin that doesn't exist. + if (!m_extra_args.IsValid()) + return; + StructuredData::ObjectSP object_sp = m_extra_args.GetObjectSP(); + if (!object_sp || !object_sp->IsValid()) + return; + + StructuredData::Dictionary *as_dict = object_sp->GetAsDictionary(); + if (!as_dict || !as_dict->IsValid()) + return; + + uint32_t num_keys = as_dict->GetSize(); + if (num_keys == 0) + return; + + s->Indent("Args:\n"); + s->SetIndentLevel(s->GetIndentLevel() + 4); + + auto print_one_element = [&s](ConstString key, + StructuredData::Object *object) { + s->Indent(); + s->Printf("%s : %s\n", key.GetCString(), + object->GetStringValue().str().c_str()); + return true; + }; + + as_dict->ForEach(print_one_element); + + s->SetIndentLevel(s->GetIndentLevel() - 4); + } + + 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_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_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.", + }, + }; + + 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 { + public: + TargetOptionValueProperties(ConstString name) : Cloneable(name) {} + + const Property *GetPropertyAtIndex(const ExecutionContext *exe_ctx, + bool will_modify, + uint32_t idx) 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) { + TargetOptionValueProperties *target_properties = + static_cast( + 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 { + public: + TargetExperimentalOptionValueProperties() + : Cloneable(ConstString(Properties::GetExperimentalSettingsName())) {} + }; + + TargetExperimentalProperties::TargetExperimentalProperties() + : Properties(OptionValuePropertiesSP( + new TargetExperimentalOptionValueProperties())) { + m_collection_sp->Initialize(g_target_experimental_properties); + } + + // 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_experimental_properties_up = + std::make_unique(); + m_collection_sp->AppendProperty( + ConstString(Properties::GetExperimentalSettingsName()), + ConstString("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(ConstString("target")); + m_collection_sp->Initialize(g_target_properties); + m_experimental_properties_up = + std::make_unique(); + m_collection_sp->AppendProperty( + ConstString(Properties::GetExperimentalSettingsName()), + ConstString("Experimental settings - setting these won't produce " + "errors if the setting is not present."), + true, m_experimental_properties_up->GetValueProperties()); + m_collection_sp->AppendProperty( + ConstString("process"), ConstString("Settings specific to processes."), + true, Process::GetGlobalProperties().GetValueProperties()); + } + } + + TargetProperties::~TargetProperties() = default; + + void TargetProperties::UpdateLaunchInfoFromProperties() { + Arg0ValueChangedCallback(); + RunArgsValueChangedCallback(); + EnvVarsValueChangedCallback(); + InputPathValueChangedCallback(); + OutputPathValueChangedCallback(); + ErrorPathValueChangedCallback(); + DetachOnErrorValueChangedCallback(); + DisableASLRValueChangedCallback(); + InheritTCCValueChangedCallback(); + DisableSTDIOValueChangedCallback(); + } + + bool TargetProperties::GetInjectLocalVariables( + ExecutionContext *exe_ctx) const { + const Property *exp_property = m_collection_sp->GetPropertyAtIndex( + exe_ctx, false, ePropertyExperimental); + OptionValueProperties *exp_values = + exp_property->GetValue()->GetAsProperties(); + if (exp_values) + return exp_values->GetPropertyAtIndexAsBoolean( + exe_ctx, ePropertyInjectLocalVars, true); + else + return true; + } + + void TargetProperties::SetInjectLocalVariables(ExecutionContext *exe_ctx, + bool b) { + const Property *exp_property = + m_collection_sp->GetPropertyAtIndex(exe_ctx, true, ePropertyExperimental); + OptionValueProperties *exp_values = + exp_property->GetValue()->GetAsProperties(); + if (exp_values) + exp_values->SetPropertyAtIndexAsBoolean(exe_ctx, ePropertyInjectLocalVars, + true); + } + + ArchSpec TargetProperties::GetDefaultArchitecture() const { + OptionValueArch *value = m_collection_sp->GetPropertyAtIndexAsOptionValueArch( + nullptr, ePropertyDefaultArch); + if (value) + return value->GetCurrentValue(); + return ArchSpec(); + } + + void TargetProperties::SetDefaultArchitecture(const ArchSpec &arch) { + OptionValueArch *value = m_collection_sp->GetPropertyAtIndexAsOptionValueArch( + nullptr, ePropertyDefaultArch); + if (value) + return value->SetCurrentValue(arch, true); + } + + bool TargetProperties::GetMoveToNearestCode() const { + const uint32_t idx = ePropertyMoveToNearestCode; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + lldb::DynamicValueType TargetProperties::GetPreferDynamicValue() const { + const uint32_t idx = ePropertyPreferDynamic; + return (lldb::DynamicValueType) + m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_target_properties[idx].default_uint_value); + } + + bool TargetProperties::SetPreferDynamicValue(lldb::DynamicValueType d) { + const uint32_t idx = ePropertyPreferDynamic; + return m_collection_sp->SetPropertyAtIndexAsEnumeration(nullptr, idx, d); + } + + bool TargetProperties::GetPreloadSymbols() const { + const uint32_t idx = ePropertyPreloadSymbols; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + void TargetProperties::SetPreloadSymbols(bool b) { + const uint32_t idx = ePropertyPreloadSymbols; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); + } + + bool TargetProperties::GetDisableASLR() const { + const uint32_t idx = ePropertyDisableASLR; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + void TargetProperties::SetDisableASLR(bool b) { + const uint32_t idx = ePropertyDisableASLR; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); + } + + bool TargetProperties::GetInheritTCC() const { + const uint32_t idx = ePropertyInheritTCC; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + void TargetProperties::SetInheritTCC(bool b) { + const uint32_t idx = ePropertyInheritTCC; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); + } + + bool TargetProperties::GetDetachOnError() const { + const uint32_t idx = ePropertyDetachOnError; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + void TargetProperties::SetDetachOnError(bool b) { + const uint32_t idx = ePropertyDetachOnError; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); + } + + bool TargetProperties::GetDisableSTDIO() const { + const uint32_t idx = ePropertyDisableSTDIO; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + void TargetProperties::SetDisableSTDIO(bool b) { + const uint32_t idx = ePropertyDisableSTDIO; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); + } + + const char *TargetProperties::GetDisassemblyFlavor() const { + const uint32_t idx = ePropertyDisassemblyFlavor; + const char *return_value; + + x86DisassemblyFlavor flavor_value = + (x86DisassemblyFlavor)m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_target_properties[idx].default_uint_value); + return_value = g_x86_dis_flavor_value_types[flavor_value].string_value; + return return_value; + } + + InlineStrategy TargetProperties::GetInlineStrategy() const { + const uint32_t idx = ePropertyInlineStrategy; + return (InlineStrategy)m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_target_properties[idx].default_uint_value); + } + + llvm::StringRef TargetProperties::GetArg0() const { + const uint32_t idx = ePropertyArg0; + return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, + llvm::StringRef()); + } + + void TargetProperties::SetArg0(llvm::StringRef arg) { + const uint32_t idx = ePropertyArg0; + m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, arg); + m_launch_info.SetArg0(arg); + } + + bool TargetProperties::GetRunArguments(Args &args) const { + const uint32_t idx = ePropertyRunArgs; + return m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, args); + } + + void TargetProperties::SetRunArguments(const Args &args) { + const uint32_t idx = ePropertyRunArgs; + m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, args); + m_launch_info.GetArguments() = args; + } + + Environment TargetProperties::ComputeEnvironment() const { + Environment env; + + if (m_target && + m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, 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(nullptr, ePropertyUnsetEnvVars, + property_unset_env); + for (const auto &var : property_unset_env) + env.erase(var.ref()); + + Args property_env; + m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, 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 (!m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, 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(nullptr, 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(nullptr, 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(nullptr, idx, Args(env)); + } + + bool TargetProperties::GetSkipPrologue() const { + const uint32_t idx = ePropertySkipPrologue; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, 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(nullptr, + false, idx); + assert(option_value); + return option_value->GetCurrentValue(); + } + + void TargetProperties::AppendExecutableSearchPaths(const FileSpec &dir) { + const uint32_t idx = ePropertyExecutableSearchPaths; + OptionValueFileSpecList *option_value = + m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, + false, idx); + assert(option_value); + option_value->AppendCurrentValue(dir); + } + + FileSpecList TargetProperties::GetExecutableSearchPaths() { + const uint32_t idx = ePropertyExecutableSearchPaths; + const OptionValueFileSpecList *option_value = + m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, + false, idx); + assert(option_value); + return option_value->GetCurrentValue(); + } + + FileSpecList TargetProperties::GetDebugFileSearchPaths() { + const uint32_t idx = ePropertyDebugFileSearchPaths; + const OptionValueFileSpecList *option_value = + m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, + false, idx); + assert(option_value); + return option_value->GetCurrentValue(); + } + + FileSpecList TargetProperties::GetClangModuleSearchPaths() { + const uint32_t idx = ePropertyClangModuleSearchPaths; + const OptionValueFileSpecList *option_value = + m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, + false, idx); + assert(option_value); + return option_value->GetCurrentValue(); + } + + bool TargetProperties::GetEnableAutoImportClangModules() const { + const uint32_t idx = ePropertyAutoImportClangModules; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + ImportStdModule TargetProperties::GetImportStdModule() const { + const uint32_t idx = ePropertyImportStdModule; + return (ImportStdModule)m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_target_properties[idx].default_uint_value); + } + + bool TargetProperties::GetEnableAutoApplyFixIts() const { + const uint32_t idx = ePropertyAutoApplyFixIts; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + uint64_t TargetProperties::GetNumberOfRetriesWithFixits() const { + const uint32_t idx = ePropertyRetriesWithFixIts; + return m_collection_sp->GetPropertyAtIndexAsUInt64( + nullptr, idx, g_target_properties[idx].default_uint_value); + } + + bool TargetProperties::GetEnableNotifyAboutFixIts() const { + const uint32_t idx = ePropertyNotifyAboutFixIts; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + bool TargetProperties::GetEnableSaveObjects() const { + const uint32_t idx = ePropertySaveObjects; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + bool TargetProperties::GetEnableSyntheticValue() const { + const uint32_t idx = ePropertyEnableSynthetic; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + uint32_t TargetProperties::GetMaxZeroPaddingInFloatFormat() const { + const uint32_t idx = ePropertyMaxZeroPaddingInFloatFormat; + return m_collection_sp->GetPropertyAtIndexAsUInt64( + nullptr, idx, g_target_properties[idx].default_uint_value); + } + + uint32_t TargetProperties::GetMaximumNumberOfChildrenToDisplay() const { + const uint32_t idx = ePropertyMaxChildrenCount; + return m_collection_sp->GetPropertyAtIndexAsSInt64( + nullptr, idx, g_target_properties[idx].default_uint_value); + } + + uint32_t TargetProperties::GetMaximumSizeOfStringSummary() const { + const uint32_t idx = ePropertyMaxSummaryLength; + return m_collection_sp->GetPropertyAtIndexAsSInt64( + nullptr, idx, g_target_properties[idx].default_uint_value); + } + + uint32_t TargetProperties::GetMaximumMemReadSize() const { + const uint32_t idx = ePropertyMaxMemReadSize; + return m_collection_sp->GetPropertyAtIndexAsSInt64( + nullptr, idx, g_target_properties[idx].default_uint_value); + } + + FileSpec TargetProperties::GetStandardInputPath() const { + const uint32_t idx = ePropertyInputPath; + return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); + } + + void TargetProperties::SetStandardInputPath(llvm::StringRef path) { + const uint32_t idx = ePropertyInputPath; + m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, path); + } + + FileSpec TargetProperties::GetStandardOutputPath() const { + const uint32_t idx = ePropertyOutputPath; + return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); + } + + void TargetProperties::SetStandardOutputPath(llvm::StringRef path) { + const uint32_t idx = ePropertyOutputPath; + m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, path); + } + + FileSpec TargetProperties::GetStandardErrorPath() const { + const uint32_t idx = ePropertyErrorPath; + return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); + } + + void TargetProperties::SetStandardErrorPath(llvm::StringRef path) { + const uint32_t idx = ePropertyErrorPath; + m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, path); + } + + LanguageType TargetProperties::GetLanguage() const { + OptionValueLanguage *value = + m_collection_sp->GetPropertyAtIndexAsOptionValueLanguage( + nullptr, ePropertyLanguage); + if (value) + return value->GetCurrentValue(); + return LanguageType(); + } + + llvm::StringRef TargetProperties::GetExpressionPrefixContents() { + const uint32_t idx = ePropertyExprPrefix; + OptionValueFileSpec *file = + m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpec(nullptr, false, + idx); + if (file) { + DataBufferSP data_sp(file->GetFileContents()); + if (data_sp) + return llvm::StringRef( + reinterpret_cast(data_sp->GetBytes()), + data_sp->GetByteSize()); + } + return ""; + } + + uint64_t TargetProperties::GetExprErrorLimit() const { + const uint32_t idx = ePropertyExprErrorLimit; + return m_collection_sp->GetPropertyAtIndexAsUInt64( + nullptr, idx, g_target_properties[idx].default_uint_value); + } + + bool TargetProperties::GetBreakpointsConsultPlatformAvoidList() { + const uint32_t idx = ePropertyBreakpointUseAvoidList; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + bool TargetProperties::GetUseHexImmediates() const { + const uint32_t idx = ePropertyUseHexImmediates; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + bool TargetProperties::GetUseFastStepping() const { + const uint32_t idx = ePropertyUseFastStepping; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + bool TargetProperties::GetDisplayExpressionsInCrashlogs() const { + const uint32_t idx = ePropertyDisplayExpressionsInCrashlogs; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + LoadScriptFromSymFile TargetProperties::GetLoadScriptFromSymbolFile() const { + const uint32_t idx = ePropertyLoadScriptFromSymbolFile; + return (LoadScriptFromSymFile) + m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_target_properties[idx].default_uint_value); + } + + LoadCWDlldbinitFile TargetProperties::GetLoadCWDlldbinitFile() const { + const uint32_t idx = ePropertyLoadCWDlldbinitFile; + return (LoadCWDlldbinitFile)m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_target_properties[idx].default_uint_value); + } + + Disassembler::HexImmediateStyle TargetProperties::GetHexImmediateStyle() const { + const uint32_t idx = ePropertyHexImmediateStyle; + return (Disassembler::HexImmediateStyle) + m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_target_properties[idx].default_uint_value); + } + + MemoryModuleLoadLevel TargetProperties::GetMemoryModuleLoadLevel() const { + const uint32_t idx = ePropertyMemoryModuleLoadLevel; + return (MemoryModuleLoadLevel) + m_collection_sp->GetPropertyAtIndexAsEnumeration( + nullptr, idx, g_target_properties[idx].default_uint_value); + } + + bool TargetProperties::GetUserSpecifiedTrapHandlerNames(Args &args) const { + const uint32_t idx = ePropertyTrapHandlerNames; + return m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, args); + } + + void TargetProperties::SetUserSpecifiedTrapHandlerNames(const Args &args) { + const uint32_t idx = ePropertyTrapHandlerNames; + m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, args); + } + + bool TargetProperties::GetDisplayRuntimeSupportValues() const { + const uint32_t idx = ePropertyDisplayRuntimeSupportValues; + return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, false); + } + + void TargetProperties::SetDisplayRuntimeSupportValues(bool b) { + const uint32_t idx = ePropertyDisplayRuntimeSupportValues; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); + } + + bool TargetProperties::GetDisplayRecognizedArguments() const { + const uint32_t idx = ePropertyDisplayRecognizedArguments; + return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, false); + } + + void TargetProperties::SetDisplayRecognizedArguments(bool b) { + const uint32_t idx = ePropertyDisplayRecognizedArguments; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, 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->GetPath()); + } + const FileAction *output_file_action = + launch_info.GetFileActionForFD(STDOUT_FILENO); + if (output_file_action) { + SetStandardOutputPath(output_file_action->GetPath()); + } + const FileAction *error_file_action = + launch_info.GetFileActionForFD(STDERR_FILENO); + if (error_file_action) { + SetStandardErrorPath(error_file_action->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 m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + void TargetProperties::SetRequireHardwareBreakpoints(bool b) { + const uint32_t idx = ePropertyRequireHardwareBreakpoints; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); + } + + bool TargetProperties::GetAutoInstallMainExecutable() const { + const uint32_t idx = ePropertyAutoInstallMainExecutable; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, 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 m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); + } + + void TargetProperties::SetDebugUtilityExpression(bool debug) { + const uint32_t idx = ePropertyDebugUtilityExpression; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, debug); + } + + // 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() = default; + + ConstString Target::TargetEventData::GetFlavorString() { + static ConstString g_flavor("Target::TargetEventData"); + return g_flavor; + } + + 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(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; + } + + 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; + } + + std::recursive_mutex &Target::GetAPIMutex() { + if (GetProcessSP() && GetProcessSP()->CurrentThreadIsPrivateStateThread()) + return m_private_mutex; + else + return m_mutex; + } + + /// Get metrics associated with this target in JSON format. + llvm::json::Value Target::ReportStatistics() { return m_stats.ToJSON(*this); } +diff --git a/lldb/test/API/tools/lldb-vscode/stop-hooks/Makefile b/lldb/test/API/tools/lldb-vscode/stop-hooks/Makefile +new file mode 100644 +index 000000000000..10495940055b +--- /dev/null ++++ b/lldb/test/API/tools/lldb-vscode/stop-hooks/Makefile +@@ -0,0 +1,3 @@ ++C_SOURCES := main.c ++ ++include Makefile.rules +diff --git a/lldb/test/API/tools/lldb-vscode/stop-hooks/TestVSCode_stop_hooks.py b/lldb/test/API/tools/lldb-vscode/stop-hooks/TestVSCode_stop_hooks.py +new file mode 100644 +index 000000000000..c6be1a013a80 +--- /dev/null ++++ b/lldb/test/API/tools/lldb-vscode/stop-hooks/TestVSCode_stop_hooks.py +@@ -0,0 +1,35 @@ ++""" ++Test stop hooks ++""" ++ ++ ++from lldbsuite.test.decorators import * ++from lldbsuite.test.lldbtest import * ++import lldbvscode_testcase ++ ++ ++class TestVSCode_stop_hooks(lldbvscode_testcase.VSCodeTestCaseBase): ++ ++ mydir = TestBase.compute_mydir(__file__) ++ ++ @skipIfRemote ++ def test_stop_hooks_before_run(self): ++ ''' ++ Test that there is no race condition between lldb-vscode and ++ stop hooks executor ++ ''' ++ program = self.getBuildArtifact("a.out") ++ preRunCommands = ['target stop-hook add -o help'] ++ self.build_and_launch(program, stopOnEntry=True, preRunCommands=preRunCommands) ++ ++ # The first stop is on entry. ++ self.continue_to_next_stop() ++ ++ breakpoint_ids = self.set_function_breakpoints(['main']) ++ # This request hangs if the race happens, because, in that case, the ++ # command interpreter is in synchr \ No newline at end of file