diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index a3fb3447169e..bf9b64547ed5 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -1,2960 +1,2960 @@ //===-- Process.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_PROCESS_H #define LLDB_TARGET_PROCESS_H #include "lldb/Host/Config.h" #include #include #include #include #include #include #include #include #include "lldb/Breakpoint/BreakpointSiteList.h" #include "lldb/Core/Communication.h" #include "lldb/Core/LoadedModuleInfoList.h" #include "lldb/Core/PluginInterface.h" #include "lldb/Core/ThreadSafeValue.h" #include "lldb/Core/UserSettingsController.h" #include "lldb/Host/HostThread.h" #include "lldb/Host/ProcessLaunchInfo.h" #include "lldb/Host/ProcessRunLock.h" #include "lldb/Interpreter/Options.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/ExecutionContextScope.h" #include "lldb/Target/InstrumentationRuntime.h" #include "lldb/Target/Memory.h" #include "lldb/Target/QueueList.h" #include "lldb/Target/ThreadList.h" #include "lldb/Target/ThreadPlanStack.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/Broadcaster.h" #include "lldb/Utility/Event.h" #include "lldb/Utility/Listener.h" #include "lldb/Utility/NameMatches.h" #include "lldb/Utility/ProcessInfo.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StructuredData.h" #include "lldb/Utility/TraceOptions.h" #include "lldb/Utility/UserIDResolver.h" #include "lldb/lldb-private.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/Threading.h" #include "llvm/Support/VersionTuple.h" namespace lldb_private { template struct Range; class ProcessExperimentalProperties : public Properties { public: ProcessExperimentalProperties(); }; class ProcessProperties : public Properties { public: // Pass nullptr for "process" if the ProcessProperties are to be the global // copy ProcessProperties(lldb_private::Process *process); ~ProcessProperties() override; bool GetDisableMemoryCache() const; uint64_t GetMemoryCacheLineSize() const; Args GetExtraStartupCommands() const; void SetExtraStartupCommands(const Args &args); FileSpec GetPythonOSPluginPath() const; void SetPythonOSPluginPath(const FileSpec &file); bool GetIgnoreBreakpointsInExpressions() const; void SetIgnoreBreakpointsInExpressions(bool ignore); bool GetUnwindOnErrorInExpressions() const; void SetUnwindOnErrorInExpressions(bool ignore); bool GetStopOnSharedLibraryEvents() const; void SetStopOnSharedLibraryEvents(bool stop); bool GetDetachKeepsStopped() const; void SetDetachKeepsStopped(bool keep_stopped); bool GetWarningsOptimization() const; bool GetWarningsUnsupportedLanguage() const; bool GetStopOnExec() const; std::chrono::seconds GetUtilityExpressionTimeout() const; bool GetOSPluginReportsAllThreads() const; void SetOSPluginReportsAllThreads(bool does_report); protected: Process *m_process; // Can be nullptr for global ProcessProperties std::unique_ptr m_experimental_properties_up; }; typedef std::shared_ptr ProcessPropertiesSP; // ProcessAttachInfo // // Describes any information that is required to attach to a process. class ProcessAttachInfo : public ProcessInstanceInfo { public: ProcessAttachInfo() : ProcessInstanceInfo(), m_listener_sp(), m_hijack_listener_sp(), m_plugin_name(), m_resume_count(0), m_wait_for_launch(false), m_ignore_existing(true), m_continue_once_attached(false), m_detach_on_error(true), m_async(false) {} ProcessAttachInfo(const ProcessLaunchInfo &launch_info) : ProcessInstanceInfo(), m_listener_sp(), m_hijack_listener_sp(), m_plugin_name(), m_resume_count(0), m_wait_for_launch(false), m_ignore_existing(true), m_continue_once_attached(false), m_detach_on_error(true), m_async(false) { ProcessInfo::operator=(launch_info); SetProcessPluginName(launch_info.GetProcessPluginName()); SetResumeCount(launch_info.GetResumeCount()); SetListener(launch_info.GetListener()); SetHijackListener(launch_info.GetHijackListener()); m_detach_on_error = launch_info.GetDetachOnError(); } bool GetWaitForLaunch() const { return m_wait_for_launch; } void SetWaitForLaunch(bool b) { m_wait_for_launch = b; } bool GetAsync() const { return m_async; } void SetAsync(bool b) { m_async = b; } bool GetIgnoreExisting() const { return m_ignore_existing; } void SetIgnoreExisting(bool b) { m_ignore_existing = b; } bool GetContinueOnceAttached() const { return m_continue_once_attached; } void SetContinueOnceAttached(bool b) { m_continue_once_attached = b; } uint32_t GetResumeCount() const { return m_resume_count; } void SetResumeCount(uint32_t c) { m_resume_count = c; } const char *GetProcessPluginName() const { return (m_plugin_name.empty() ? nullptr : m_plugin_name.c_str()); } void SetProcessPluginName(llvm::StringRef plugin) { m_plugin_name = std::string(plugin); } void Clear() { ProcessInstanceInfo::Clear(); m_plugin_name.clear(); m_resume_count = 0; m_wait_for_launch = false; m_ignore_existing = true; m_continue_once_attached = false; } bool ProcessInfoSpecified() const { if (GetExecutableFile()) return true; if (GetProcessID() != LLDB_INVALID_PROCESS_ID) return true; if (GetParentProcessID() != LLDB_INVALID_PROCESS_ID) return true; return false; } lldb::ListenerSP GetHijackListener() const { return m_hijack_listener_sp; } void SetHijackListener(const lldb::ListenerSP &listener_sp) { m_hijack_listener_sp = listener_sp; } bool GetDetachOnError() const { return m_detach_on_error; } void SetDetachOnError(bool enable) { m_detach_on_error = enable; } // Get and set the actual listener that will be used for the process events lldb::ListenerSP GetListener() const { return m_listener_sp; } void SetListener(const lldb::ListenerSP &listener_sp) { m_listener_sp = listener_sp; } lldb::ListenerSP GetListenerForProcess(Debugger &debugger); protected: lldb::ListenerSP m_listener_sp; lldb::ListenerSP m_hijack_listener_sp; std::string m_plugin_name; uint32_t m_resume_count; // How many times do we resume after launching bool m_wait_for_launch; bool m_ignore_existing; bool m_continue_once_attached; // Supports the use-case scenario of // immediately continuing the process once // attached. bool m_detach_on_error; // If we are debugging remotely, instruct the stub to // detach rather than killing the target on error. bool m_async; // Use an async attach where we start the attach and return // immediately (used by GUI programs with --waitfor so they can // call SBProcess::Stop() to cancel attach) }; class ProcessLaunchCommandOptions : public Options { public: ProcessLaunchCommandOptions() : Options() { // Keep default values of all options in one place: OptionParsingStarting // () OptionParsingStarting(nullptr); } ~ProcessLaunchCommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override; void OptionParsingStarting(ExecutionContext *execution_context) override { launch_info.Clear(); disable_aslr = eLazyBoolCalculate; } llvm::ArrayRef GetDefinitions() override; // Instance variables to hold the values for command options. ProcessLaunchInfo launch_info; lldb_private::LazyBool disable_aslr; }; // This class tracks the Modification state of the process. Things that can // currently modify the program are running the program (which will up the // StopID) and writing memory (which will up the MemoryID.) // FIXME: Should we also include modification of register states? class ProcessModID { friend bool operator==(const ProcessModID &lhs, const ProcessModID &rhs); public: ProcessModID() : m_stop_id(0), m_last_natural_stop_id(0), m_resume_id(0), m_memory_id(0), m_last_user_expression_resume(0), m_running_user_expression(false), m_running_utility_function(0) {} ProcessModID(const ProcessModID &rhs) : m_stop_id(rhs.m_stop_id), m_memory_id(rhs.m_memory_id) {} const ProcessModID &operator=(const ProcessModID &rhs) { if (this != &rhs) { m_stop_id = rhs.m_stop_id; m_memory_id = rhs.m_memory_id; } return *this; } ~ProcessModID() = default; void BumpStopID() { m_stop_id++; if (!IsLastResumeForUserExpression()) m_last_natural_stop_id++; } void BumpMemoryID() { m_memory_id++; } void BumpResumeID() { m_resume_id++; if (m_running_user_expression > 0) m_last_user_expression_resume = m_resume_id; } bool IsRunningUtilityFunction() const { return m_running_utility_function > 0; } uint32_t GetStopID() const { return m_stop_id; } uint32_t GetLastNaturalStopID() const { return m_last_natural_stop_id; } uint32_t GetMemoryID() const { return m_memory_id; } uint32_t GetResumeID() const { return m_resume_id; } uint32_t GetLastUserExpressionResumeID() const { return m_last_user_expression_resume; } bool MemoryIDEqual(const ProcessModID &compare) const { return m_memory_id == compare.m_memory_id; } bool StopIDEqual(const ProcessModID &compare) const { return m_stop_id == compare.m_stop_id; } void SetInvalid() { m_stop_id = UINT32_MAX; } bool IsValid() const { return m_stop_id != UINT32_MAX; } bool IsLastResumeForUserExpression() const { // If we haven't yet resumed the target, then it can't be for a user // expression... if (m_resume_id == 0) return false; return m_resume_id == m_last_user_expression_resume; } void SetRunningUserExpression(bool on) { if (on) m_running_user_expression++; else m_running_user_expression--; } void SetRunningUtilityFunction(bool on) { if (on) m_running_utility_function++; else { assert(m_running_utility_function > 0 && "Called SetRunningUtilityFunction(false) without calling " "SetRunningUtilityFunction(true) before?"); m_running_utility_function--; } } void SetStopEventForLastNaturalStopID(lldb::EventSP event_sp) { m_last_natural_stop_event = event_sp; } lldb::EventSP GetStopEventForStopID(uint32_t stop_id) const { if (stop_id == m_last_natural_stop_id) return m_last_natural_stop_event; return lldb::EventSP(); } private: uint32_t m_stop_id; uint32_t m_last_natural_stop_id; uint32_t m_resume_id; uint32_t m_memory_id; uint32_t m_last_user_expression_resume; uint32_t m_running_user_expression; uint32_t m_running_utility_function; lldb::EventSP m_last_natural_stop_event; }; inline bool operator==(const ProcessModID &lhs, const ProcessModID &rhs) { if (lhs.StopIDEqual(rhs) && lhs.MemoryIDEqual(rhs)) return true; else return false; } inline bool operator!=(const ProcessModID &lhs, const ProcessModID &rhs) { return (!lhs.StopIDEqual(rhs) || !lhs.MemoryIDEqual(rhs)); } /// \class Process Process.h "lldb/Target/Process.h" /// A plug-in interface definition class for debugging a process. class Process : public std::enable_shared_from_this, public ProcessProperties, public UserID, public Broadcaster, public ExecutionContextScope, public PluginInterface { friend class FunctionCaller; // For WaitForStateChangeEventsPrivate friend class Debugger; // For PopProcessIOHandler and ProcessIOHandlerIsActive friend class DynamicLoader; // For LoadOperatingSystemPlugin friend class ProcessEventData; friend class StopInfo; friend class Target; friend class ThreadList; public: /// Broadcaster event bits definitions. enum { eBroadcastBitStateChanged = (1 << 0), eBroadcastBitInterrupt = (1 << 1), eBroadcastBitSTDOUT = (1 << 2), eBroadcastBitSTDERR = (1 << 3), eBroadcastBitProfileData = (1 << 4), eBroadcastBitStructuredData = (1 << 5), }; enum { eBroadcastInternalStateControlStop = (1 << 0), eBroadcastInternalStateControlPause = (1 << 1), eBroadcastInternalStateControlResume = (1 << 2) }; /// Process warning types. enum Warnings { eWarningsOptimization = 1, eWarningsUnsupportedLanguage = 2 }; typedef Range LoadRange; // We use a read/write lock to allow on or more clients to access the process // state while the process is stopped (reader). We lock the write lock to // control access to the process while it is running (readers, or clients // that want the process stopped can block waiting for the process to stop, // or just try to lock it to see if they can immediately access the stopped // process. If the try read lock fails, then the process is running. typedef ProcessRunLock::ProcessRunLocker StopLocker; // These two functions fill out the Broadcaster interface: static ConstString &GetStaticBroadcasterClass(); ConstString &GetBroadcasterClass() const override { return GetStaticBroadcasterClass(); } /// A notification structure that can be used by clients to listen /// for changes in a process's lifetime. /// /// \see RegisterNotificationCallbacks (const Notifications&) @see /// UnregisterNotificationCallbacks (const Notifications&) typedef struct { void *baton; void (*initialize)(void *baton, Process *process); void (*process_state_changed)(void *baton, Process *process, lldb::StateType state); } Notifications; class ProcessEventData : public EventData { friend class Process; public: ProcessEventData(); ProcessEventData(const lldb::ProcessSP &process, lldb::StateType state); ~ProcessEventData() override; static ConstString GetFlavorString(); ConstString GetFlavor() const override; lldb::ProcessSP GetProcessSP() const { return m_process_wp.lock(); } lldb::StateType GetState() const { return m_state; } bool GetRestarted() const { return m_restarted; } size_t GetNumRestartedReasons() { return m_restarted_reasons.size(); } const char *GetRestartedReasonAtIndex(size_t idx) { return ((idx < m_restarted_reasons.size()) ? m_restarted_reasons[idx].c_str() : nullptr); } bool GetInterrupted() const { return m_interrupted; } void Dump(Stream *s) const override; virtual bool ShouldStop(Event *event_ptr, bool &found_valid_stopinfo); void DoOnRemoval(Event *event_ptr) override; static const Process::ProcessEventData * GetEventDataFromEvent(const Event *event_ptr); static lldb::ProcessSP GetProcessFromEvent(const Event *event_ptr); static lldb::StateType GetStateFromEvent(const Event *event_ptr); static bool GetRestartedFromEvent(const Event *event_ptr); static size_t GetNumRestartedReasons(const Event *event_ptr); static const char *GetRestartedReasonAtIndex(const Event *event_ptr, size_t idx); static void AddRestartedReason(Event *event_ptr, const char *reason); static void SetRestartedInEvent(Event *event_ptr, bool new_value); static bool GetInterruptedFromEvent(const Event *event_ptr); static void SetInterruptedInEvent(Event *event_ptr, bool new_value); static bool SetUpdateStateOnRemoval(Event *event_ptr); private: void SetUpdateStateOnRemoval() { m_update_state++; } void SetRestarted(bool new_value) { m_restarted = new_value; } void SetInterrupted(bool new_value) { m_interrupted = new_value; } void AddRestartedReason(const char *reason) { m_restarted_reasons.push_back(reason); } lldb::ProcessWP m_process_wp; lldb::StateType m_state; std::vector m_restarted_reasons; bool m_restarted; // For "eStateStopped" events, this is true if the target // was automatically restarted. int m_update_state; bool m_interrupted; ProcessEventData(const ProcessEventData &) = delete; const ProcessEventData &operator=(const ProcessEventData &) = delete; }; /// Construct with a shared pointer to a target, and the Process listener. /// Uses the Host UnixSignalsSP by default. Process(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); /// Construct with a shared pointer to a target, the Process listener, and /// the appropriate UnixSignalsSP for the process. Process(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const lldb::UnixSignalsSP &unix_signals_sp); /// Destructor. /// /// The destructor is virtual since this class is designed to be inherited /// from by the plug-in instance. ~Process() override; static void SettingsInitialize(); static void SettingsTerminate(); static const ProcessPropertiesSP &GetGlobalProperties(); /// Find a Process plug-in that can debug \a module using the currently /// selected architecture. /// /// Scans all loaded plug-in interfaces that implement versions of the /// Process plug-in interface and returns the first instance that can debug /// the file. /// /// \see Process::CanDebug () static lldb::ProcessSP FindPlugin(lldb::TargetSP target_sp, llvm::StringRef plugin_name, lldb::ListenerSP listener_sp, const FileSpec *crash_file_path); /// Static function that can be used with the \b host function /// Host::StartMonitoringChildProcess (). /// /// This function can be used by lldb_private::Process subclasses when they /// want to watch for a local process and have its exit status automatically /// set when the host child process exits. Subclasses should call /// Host::StartMonitoringChildProcess () with: /// callback = Process::SetHostProcessExitStatus /// pid = Process::GetID() /// monitor_signals = false static bool SetProcessExitStatus(lldb::pid_t pid, // The process ID we want to monitor bool exited, int signo, // Zero for no signal int status); // Exit value of process if signal is zero lldb::ByteOrder GetByteOrder() const; uint32_t GetAddressByteSize() const; uint32_t GetUniqueID() const { return m_process_unique_id; } /// Check if a plug-in instance can debug the file in \a module. /// /// Each plug-in is given a chance to say whether it can debug the file in /// \a module. If the Process plug-in instance can debug a file on the /// current system, it should return \b true. /// /// \return /// Returns \b true if this Process plug-in instance can /// debug the executable, \b false otherwise. virtual bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) = 0; /// This object is about to be destroyed, do any necessary cleanup. /// /// Subclasses that override this method should always call this superclass /// method. virtual void Finalize(); /// Return whether this object is valid (i.e. has not been finalized.) /// /// \return /// Returns \b true if this Process has not been finalized /// and \b false otherwise. bool IsValid() const { return !m_finalize_called; } /// Return a multi-word command object that can be used to expose plug-in /// specific commands. /// /// This object will be used to resolve plug-in commands and can be /// triggered by a call to: /// /// (lldb) process command /// /// \return /// A CommandObject which can be one of the concrete subclasses /// of CommandObject like CommandObjectRaw, CommandObjectParsed, /// or CommandObjectMultiword. virtual CommandObject *GetPluginCommandObject() { return nullptr; } /// Launch a new process. /// /// Launch a new process by spawning a new process using the target object's /// executable module's file as the file to launch. /// /// This function is not meant to be overridden by Process subclasses. It /// will first call Process::WillLaunch (Module *) and if that returns \b /// true, Process::DoLaunch (Module*, char const *[],char const *[],const /// char *,const char *, const char *) will be called to actually do the /// launching. If DoLaunch returns \b true, then Process::DidLaunch() will /// be called. /// /// \param[in] launch_info /// Details regarding the environment, STDIN/STDOUT/STDERR /// redirection, working path, etc. related to the requested launch. /// /// \return /// An error object. Call GetID() to get the process ID if /// the error object is success. virtual Status Launch(ProcessLaunchInfo &launch_info); virtual Status LoadCore(); virtual Status DoLoadCore() { Status error; error.SetErrorStringWithFormat( "error: %s does not support loading core files.", GetPluginName().GetCString()); return error; } // FUTURE WORK: GetLoadImageUtilityFunction are the first use we've // had of having other plugins cache data in the Process. This is handy for // long-living plugins - like the Platform - which manage interactions whose // lifetime is governed by the Process lifetime. If we find we need to do // this more often, we should construct a general solution to the problem. // The consensus suggestion was that we have a token based registry in the // Process. Some undecided questions are (1) who manages the tokens. It's // probably best that you add the element and get back a token that // represents it. That will avoid collisions. But there may be some utility // in the registerer controlling the token? (2) whether the thing added // should be simply owned by Process, and just go away when it does (3) // whether the registree should be notified of the Process' demise. // // We are postponing designing this till we have at least a second use case. /// Get the cached UtilityFunction that assists in loading binary images /// into the process. /// /// \param[in] platform /// The platform fetching the UtilityFunction. /// \param[in] factory /// A function that will be called only once per-process in a /// thread-safe way to create the UtilityFunction if it has not /// been initialized yet. /// /// \return /// The cached utility function or null if the platform is not the /// same as the target's platform. UtilityFunction *GetLoadImageUtilityFunction( Platform *platform, llvm::function_ref()> factory); /// Get the dynamic loader plug-in for this process. /// /// The default action is to let the DynamicLoader plug-ins check the main /// executable and the DynamicLoader will select itself automatically. /// Subclasses can override this if inspecting the executable is not /// desired, or if Process subclasses can only use a specific DynamicLoader /// plug-in. virtual DynamicLoader *GetDynamicLoader(); // Returns AUXV structure found in many ELF-based environments. // // The default action is to return an empty data buffer. // // \return // A data extractor containing the contents of the AUXV data. virtual DataExtractor GetAuxvData(); /// Sometimes processes know how to retrieve and load shared libraries. This /// is normally done by DynamicLoader plug-ins, but sometimes the connection /// to the process allows retrieving this information. The dynamic loader /// plug-ins can use this function if they can't determine the current /// shared library load state. /// /// \return /// A status object indicating if the operation was sucessful or not. virtual llvm::Error LoadModules() { return llvm::make_error("Not implemented.", llvm::inconvertibleErrorCode()); } /// Query remote GDBServer for a detailed loaded library list /// \return /// The list of modules currently loaded by the process, or an error. virtual llvm::Expected GetLoadedModuleList() { return llvm::createStringError(llvm::inconvertibleErrorCode(), "Not implemented"); } protected: virtual JITLoaderList &GetJITLoaders(); public: /// Get the system runtime plug-in for this process. /// /// \return /// Returns a pointer to the SystemRuntime plugin for this Process /// if one is available. Else returns nullptr. virtual SystemRuntime *GetSystemRuntime(); /// Attach to an existing process using the process attach info. /// /// This function is not meant to be overridden by Process subclasses. It /// will first call WillAttach (lldb::pid_t) or WillAttach (const char *), /// and if that returns \b true, DoAttach (lldb::pid_t) or DoAttach (const /// char *) will be called to actually do the attach. If DoAttach returns \b /// true, then Process::DidAttach() will be called. /// /// \param[in] attach_info /// The process attach info. /// /// \return /// Returns \a pid if attaching was successful, or /// LLDB_INVALID_PROCESS_ID if attaching fails. virtual Status Attach(ProcessAttachInfo &attach_info); /// Attach to a remote system via a URL /// /// \param[in] strm /// A stream where output intended for the user /// (if the driver has a way to display that) generated during /// the connection. This may be nullptr if no output is needed.A /// /// \param[in] remote_url /// The URL format that we are connecting to. /// /// \return /// Returns an error object. - virtual Status ConnectRemote(Stream *strm, llvm::StringRef remote_url); + virtual Status ConnectRemote(llvm::StringRef remote_url); bool GetShouldDetach() const { return m_should_detach; } void SetShouldDetach(bool b) { m_should_detach = b; } /// Get the image information address for the current process. /// /// Some runtimes have system functions that can help dynamic loaders locate /// the dynamic loader information needed to observe shared libraries being /// loaded or unloaded. This function is in the Process interface (as /// opposed to the DynamicLoader interface) to ensure that remote debugging /// can take advantage of this functionality. /// /// \return /// The address of the dynamic loader information, or /// LLDB_INVALID_ADDRESS if this is not supported by this /// interface. virtual lldb::addr_t GetImageInfoAddress(); /// Called when the process is about to broadcast a public stop. /// /// There are public and private stops. Private stops are when the process /// is doing things like stepping and the client doesn't need to know about /// starts and stop that implement a thread plan. Single stepping over a /// source line in code might end up being implemented by one or more /// process starts and stops. Public stops are when clients will be notified /// that the process is stopped. These events typically trigger UI updates /// (thread stack frames to be displayed, variables to be displayed, and /// more). This function can be overriden and allows process subclasses to /// do something before the eBroadcastBitStateChanged event is sent to /// public clients. virtual void WillPublicStop() {} /// Register for process and thread notifications. /// /// Clients can register notification callbacks by filling out a /// Process::Notifications structure and calling this function. /// /// \param[in] callbacks /// A structure that contains the notification baton and /// callback functions. /// /// \see Process::Notifications void RegisterNotificationCallbacks(const Process::Notifications &callbacks); /// Unregister for process and thread notifications. /// /// Clients can unregister notification callbacks by passing a copy of the /// original baton and callbacks in \a callbacks. /// /// \param[in] callbacks /// A structure that contains the notification baton and /// callback functions. /// /// \return /// Returns \b true if the notification callbacks were /// successfully removed from the process, \b false otherwise. /// /// \see Process::Notifications bool UnregisterNotificationCallbacks(const Process::Notifications &callbacks); //================================================================== // Built in Process Control functions //================================================================== /// Resumes all of a process's threads as configured using the Thread run /// control functions. /// /// Threads for a process should be updated with one of the run control /// actions (resume, step, or suspend) that they should take when the /// process is resumed. If no run control action is given to a thread it /// will be resumed by default. /// /// This function is not meant to be overridden by Process subclasses. This /// function will take care of disabling any breakpoints that threads may be /// stopped at, single stepping, and re-enabling breakpoints, and enabling /// the basic flow control that the plug-in instances need not worry about. /// /// N.B. This function also sets the Write side of the Run Lock, which is /// unset when the corresponding stop event is pulled off the Public Event /// Queue. If you need to resume the process without setting the Run Lock, /// use PrivateResume (though you should only do that from inside the /// Process class. /// /// \return /// Returns an error object. /// /// \see Thread:Resume() /// \see Thread:Step() /// \see Thread:Suspend() Status Resume(); Status ResumeSynchronous(Stream *stream); /// Halts a running process. /// /// This function is not meant to be overridden by Process subclasses. If /// the process is successfully halted, a eStateStopped process event with /// GetInterrupted will be broadcast. If false, we will halt the process /// with no events generated by the halt. /// /// \param[in] clear_thread_plans /// If true, when the process stops, clear all thread plans. /// /// \param[in] use_run_lock /// Whether to release the run lock after the stop. /// /// \return /// Returns an error object. If the error is empty, the process is /// halted. /// otherwise the halt has failed. Status Halt(bool clear_thread_plans = false, bool use_run_lock = true); /// Detaches from a running or stopped process. /// /// This function is not meant to be overridden by Process subclasses. /// /// \param[in] keep_stopped /// If true, don't resume the process on detach. /// /// \return /// Returns an error object. Status Detach(bool keep_stopped); /// Kills the process and shuts down all threads that were spawned to track /// and monitor the process. /// /// This function is not meant to be overridden by Process subclasses. /// /// \param[in] force_kill /// Whether lldb should force a kill (instead of a detach) from /// the inferior process. Normally if lldb launched a binary and /// Destory is called, lldb kills it. If lldb attached to a /// running process and Destory is called, lldb detaches. If /// this behavior needs to be over-ridden, this is the bool that /// can be used. /// /// \return /// Returns an error object. Status Destroy(bool force_kill); /// Sends a process a UNIX signal \a signal. /// /// This function is not meant to be overridden by Process subclasses. /// /// \return /// Returns an error object. Status Signal(int signal); void SetUnixSignals(lldb::UnixSignalsSP &&signals_sp); const lldb::UnixSignalsSP &GetUnixSignals(); //================================================================== // Plug-in Process Control Overrides //================================================================== /// Called before attaching to a process. /// /// Allow Process plug-ins to execute some code before attaching a process. /// /// \return /// Returns an error object. virtual Status WillAttachToProcessWithID(lldb::pid_t pid) { return Status(); } /// Called before attaching to a process. /// /// Allow Process plug-ins to execute some code before attaching a process. /// /// \return /// Returns an error object. virtual Status WillAttachToProcessWithName(const char *process_name, bool wait_for_launch) { return Status(); } /// Attach to a remote system via a URL /// /// \param[in] strm /// A stream where output intended for the user /// (if the driver has a way to display that) generated during /// the connection. This may be nullptr if no output is needed.A /// /// \param[in] remote_url /// The URL format that we are connecting to. /// /// \return /// Returns an error object. - virtual Status DoConnectRemote(Stream *strm, llvm::StringRef remote_url) { + virtual Status DoConnectRemote(llvm::StringRef remote_url) { Status error; error.SetErrorString("remote connections are not supported"); return error; } /// Attach to an existing process using a process ID. /// /// \param[in] pid /// The process ID that we should attempt to attach to. /// /// \param[in] attach_info /// Information on how to do the attach. For example, GetUserID() /// will return the uid to attach as. /// /// \return /// Returns a successful Status attaching was successful, or /// an appropriate (possibly platform-specific) error code if /// attaching fails. /// hanming : need flag virtual Status DoAttachToProcessWithID(lldb::pid_t pid, const ProcessAttachInfo &attach_info) { Status error; error.SetErrorStringWithFormat( "error: %s does not support attaching to a process by pid", GetPluginName().GetCString()); return error; } /// Attach to an existing process using a partial process name. /// /// \param[in] process_name /// The name of the process to attach to. /// /// \param[in] attach_info /// Information on how to do the attach. For example, GetUserID() /// will return the uid to attach as. /// /// \return /// Returns a successful Status attaching was successful, or /// an appropriate (possibly platform-specific) error code if /// attaching fails. virtual Status DoAttachToProcessWithName(const char *process_name, const ProcessAttachInfo &attach_info) { Status error; error.SetErrorString("attach by name is not supported"); return error; } /// Called after attaching a process. /// /// \param[in] process_arch /// If you can figure out the process architecture after attach, fill it /// in here. /// /// Allow Process plug-ins to execute some code after attaching to a /// process. virtual void DidAttach(ArchSpec &process_arch) { process_arch.Clear(); } /// Called after a process re-execs itself. /// /// Allow Process plug-ins to execute some code after a process has exec'ed /// itself. Subclasses typically should override DoDidExec() as the /// lldb_private::Process class needs to remove its dynamic loader, runtime, /// ABI and other plug-ins, as well as unload all shared libraries. virtual void DidExec(); /// Subclasses of Process should implement this function if they need to do /// anything after a process exec's itself. virtual void DoDidExec() {} /// Called before launching to a process. /// /// Allow Process plug-ins to execute some code before launching a process. /// /// \return /// Returns an error object. virtual Status WillLaunch(Module *module) { return Status(); } /// Launch a new process. /// /// Launch a new process by spawning a new process using \a exe_module's /// file as the file to launch. Launch details are provided in \a /// launch_info. /// /// \param[in] exe_module /// The module from which to extract the file specification and /// launch. /// /// \param[in] launch_info /// Details (e.g. arguments, stdio redirection, etc.) for the /// requested launch. /// /// \return /// An Status instance indicating success or failure of the /// operation. virtual Status DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) { Status error; error.SetErrorStringWithFormat( "error: %s does not support launching processes", GetPluginName().GetCString()); return error; } /// Called after launching a process. /// /// Allow Process plug-ins to execute some code after launching a process. virtual void DidLaunch() {} /// Called before resuming to a process. /// /// Allow Process plug-ins to execute some code before resuming a process. /// /// \return /// Returns an error object. virtual Status WillResume() { return Status(); } /// Resumes all of a process's threads as configured using the Thread run /// control functions. /// /// Threads for a process should be updated with one of the run control /// actions (resume, step, or suspend) that they should take when the /// process is resumed. If no run control action is given to a thread it /// will be resumed by default. /// /// \return /// Returns \b true if the process successfully resumes using /// the thread run control actions, \b false otherwise. /// /// \see Thread:Resume() /// \see Thread:Step() /// \see Thread:Suspend() virtual Status DoResume() { Status error; error.SetErrorStringWithFormat( "error: %s does not support resuming processes", GetPluginName().GetCString()); return error; } /// Called after resuming a process. /// /// Allow Process plug-ins to execute some code after resuming a process. virtual void DidResume() {} /// Called before halting to a process. /// /// Allow Process plug-ins to execute some code before halting a process. /// /// \return /// Returns an error object. virtual Status WillHalt() { return Status(); } /// Halts a running process. /// /// DoHalt must produce one and only one stop StateChanged event if it /// actually stops the process. If the stop happens through some natural /// event (for instance a SIGSTOP), then forwarding that event will do. /// Otherwise, you must generate the event manually. This function is called /// from the context of the private state thread. /// /// \param[out] caused_stop /// If true, then this Halt caused the stop, otherwise, the /// process was already stopped. /// /// \return /// Returns \b true if the process successfully halts, \b false /// otherwise. virtual Status DoHalt(bool &caused_stop) { Status error; error.SetErrorStringWithFormat( "error: %s does not support halting processes", GetPluginName().GetCString()); return error; } /// Called after halting a process. /// /// Allow Process plug-ins to execute some code after halting a process. virtual void DidHalt() {} /// Called before detaching from a process. /// /// Allow Process plug-ins to execute some code before detaching from a /// process. /// /// \return /// Returns an error object. virtual Status WillDetach() { return Status(); } /// Detaches from a running or stopped process. /// /// \return /// Returns \b true if the process successfully detaches, \b /// false otherwise. virtual Status DoDetach(bool keep_stopped) { Status error; error.SetErrorStringWithFormat( "error: %s does not support detaching from processes", GetPluginName().GetCString()); return error; } /// Called after detaching from a process. /// /// Allow Process plug-ins to execute some code after detaching from a /// process. virtual void DidDetach() {} virtual bool DetachRequiresHalt() { return false; } /// Called before sending a signal to a process. /// /// Allow Process plug-ins to execute some code before sending a signal to a /// process. /// /// \return /// Returns no error if it is safe to proceed with a call to /// Process::DoSignal(int), otherwise an error describing what /// prevents the signal from being sent. virtual Status WillSignal() { return Status(); } /// Sends a process a UNIX signal \a signal. /// /// \return /// Returns an error object. virtual Status DoSignal(int signal) { Status error; error.SetErrorStringWithFormat( "error: %s does not support sending signals to processes", GetPluginName().GetCString()); return error; } virtual Status WillDestroy() { return Status(); } virtual Status DoDestroy() = 0; virtual void DidDestroy() {} virtual bool DestroyRequiresHalt() { return true; } /// Called after sending a signal to a process. /// /// Allow Process plug-ins to execute some code after sending a signal to a /// process. virtual void DidSignal() {} /// Currently called as part of ShouldStop. /// FIXME: Should really happen when the target stops before the /// event is taken from the queue... /// /// This callback is called as the event /// is about to be queued up to allow Process plug-ins to execute some code /// prior to clients being notified that a process was stopped. Common /// operations include updating the thread list, invalidating any thread /// state (registers, stack, etc) prior to letting the notification go out. /// virtual void RefreshStateAfterStop() = 0; /// Sometimes the connection to a process can detect the host OS version /// that the process is running on. The current platform should be checked /// first in case the platform is connected, but clients can fall back onto /// this function if the platform fails to identify the host OS version. The /// platform should be checked first in case you are running a simulator /// platform that might itself be running natively, but have different /// heuristics for figuring out which OS is is emulating. /// /// \return /// Returns the version tuple of the host OS. In case of failure an empty /// VersionTuple is returner. virtual llvm::VersionTuple GetHostOSVersion() { return llvm::VersionTuple(); } /// \return the macCatalyst version of the host OS. virtual llvm::VersionTuple GetHostMacCatalystVersion() { return {}; } /// Get the target object pointer for this module. /// /// \return /// A Target object pointer to the target that owns this /// module. Target &GetTarget() { return *m_target_wp.lock(); } /// Get the const target object pointer for this module. /// /// \return /// A const Target object pointer to the target that owns this /// module. const Target &GetTarget() const { return *m_target_wp.lock(); } /// Flush all data in the process. /// /// Flush the memory caches, all threads, and any other cached data in the /// process. /// /// This function can be called after a world changing event like adding a /// new symbol file, or after the process makes a large context switch (from /// boot ROM to booted into an OS). void Flush(); /// Get accessor for the current process state. /// /// \return /// The current state of the process. /// /// \see lldb::StateType lldb::StateType GetState(); lldb::ExpressionResults RunThreadPlan(ExecutionContext &exe_ctx, lldb::ThreadPlanSP &thread_plan_sp, const EvaluateExpressionOptions &options, DiagnosticManager &diagnostic_manager); static const char *ExecutionResultAsCString(lldb::ExpressionResults result); void GetStatus(Stream &ostrm); size_t GetThreadStatus(Stream &ostrm, bool only_threads_with_stop_reason, uint32_t start_frame, uint32_t num_frames, uint32_t num_frames_with_source, bool stop_format); void SendAsyncInterrupt(); // Notify this process class that modules got loaded. // // If subclasses override this method, they must call this version before // doing anything in the subclass version of the function. virtual void ModulesDidLoad(ModuleList &module_list); /// Retrieve the list of shared libraries that are loaded for this process /// This method is used on pre-macOS 10.12, pre-iOS 10, pre-tvOS 10, pre- /// watchOS 3 systems. The following two methods are for newer versions of /// those OSes. /// /// For certain platforms, the time it takes for the DynamicLoader plugin to /// read all of the shared libraries out of memory over a slow communication /// channel may be too long. In that instance, the gdb-remote stub may be /// able to retrieve the necessary information about the solibs out of /// memory and return a concise summary sufficient for the DynamicLoader /// plugin. /// /// \param [in] image_list_address /// The address where the table of shared libraries is stored in memory, /// if that is appropriate for this platform. Else this may be /// passed as LLDB_INVALID_ADDRESS. /// /// \param [in] image_count /// The number of shared libraries that are present in this process, if /// that is appropriate for this platofrm Else this may be passed as /// LLDB_INVALID_ADDRESS. /// /// \return /// A StructuredDataSP object which, if non-empty, will contain the /// information the DynamicLoader needs to get the initial scan of /// solibs resolved. virtual lldb_private::StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos(lldb::addr_t image_list_address, lldb::addr_t image_count) { return StructuredData::ObjectSP(); } // On macOS 10.12, tvOS 10, iOS 10, watchOS 3 and newer, debugserver can // return the full list of loaded shared libraries without needing any input. virtual lldb_private::StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos() { return StructuredData::ObjectSP(); } // On macOS 10.12, tvOS 10, iOS 10, watchOS 3 and newer, debugserver can // return information about binaries given their load addresses. virtual lldb_private::StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos( const std::vector &load_addresses) { return StructuredData::ObjectSP(); } // Get information about the library shared cache, if that exists // // On macOS 10.12, tvOS 10, iOS 10, watchOS 3 and newer, debugserver can // return information about the library shared cache (a set of standard // libraries that are loaded at the same location for all processes on a // system) in use. virtual lldb_private::StructuredData::ObjectSP GetSharedCacheInfo() { return StructuredData::ObjectSP(); } /// Print a user-visible warning about a module being built with /// optimization /// /// Prints a async warning message to the user one time per Module where a /// function is found that was compiled with optimization, per Process. /// /// \param [in] sc /// A SymbolContext with eSymbolContextFunction and eSymbolContextModule /// pre-computed. void PrintWarningOptimization(const SymbolContext &sc); /// Print a user-visible warning about a function written in a /// language that this version of LLDB doesn't support. /// /// \see PrintWarningOptimization void PrintWarningUnsupportedLanguage(const SymbolContext &sc); virtual bool GetProcessInfo(ProcessInstanceInfo &info); /// Get the exit status for a process. /// /// \return /// The process's return code, or -1 if the current process /// state is not eStateExited. int GetExitStatus(); /// Get a textual description of what the process exited. /// /// \return /// The textual description of why the process exited, or nullptr /// if there is no description available. const char *GetExitDescription(); virtual void DidExit() {} /// Get the Modification ID of the process. /// /// \return /// The modification ID of the process. ProcessModID GetModID() const { return m_mod_id; } const ProcessModID &GetModIDRef() const { return m_mod_id; } uint32_t GetStopID() const { return m_mod_id.GetStopID(); } uint32_t GetResumeID() const { return m_mod_id.GetResumeID(); } uint32_t GetLastUserExpressionResumeID() const { return m_mod_id.GetLastUserExpressionResumeID(); } uint32_t GetLastNaturalStopID() const { return m_mod_id.GetLastNaturalStopID(); } lldb::EventSP GetStopEventForStopID(uint32_t stop_id) const { return m_mod_id.GetStopEventForStopID(stop_id); } /// Set accessor for the process exit status (return code). /// /// Sometimes a child exits and the exit can be detected by global functions /// (signal handler for SIGCHLD for example). This accessor allows the exit /// status to be set from an external source. /// /// Setting this will cause a eStateExited event to be posted to the process /// event queue. /// /// \param[in] exit_status /// The value for the process's return code. /// /// \see lldb::StateType virtual bool SetExitStatus(int exit_status, const char *cstr); /// Check if a process is still alive. /// /// \return /// Returns \b true if the process is still valid, \b false /// otherwise. virtual bool IsAlive(); /// Before lldb detaches from a process, it warns the user that they are /// about to lose their debug session. In some cases, this warning doesn't /// need to be emitted -- for instance, with core file debugging where the /// user can reconstruct the "state" by simply re-running the debugger on /// the core file. /// /// \return /// Returns \b true if the user should be warned about detaching from /// this process. virtual bool WarnBeforeDetach() const { return true; } /// Actually do the reading of memory from a process. /// /// Subclasses must override this function and can return fewer bytes than /// requested when memory requests are too large. This class will break up /// the memory requests and keep advancing the arguments along as needed. /// /// \param[in] vm_addr /// A virtual load address that indicates where to start reading /// memory from. /// /// \param[in] size /// The number of bytes to read. /// /// \param[out] buf /// A byte buffer that is at least \a size bytes long that /// will receive the memory bytes. /// /// \param[out] error /// An error that indicates the success or failure of this /// operation. If error indicates success (error.Success()), /// then the value returned can be trusted, otherwise zero /// will be returned. /// /// \return /// The number of bytes that were actually read into \a buf. /// Zero is returned in the case of an error. virtual size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error) = 0; /// Read of memory from a process. /// /// This function will read memory from the current process's address space /// and remove any traps that may have been inserted into the memory. /// /// This function is not meant to be overridden by Process subclasses, the /// subclasses should implement Process::DoReadMemory (lldb::addr_t, size_t, /// void *). /// /// \param[in] vm_addr /// A virtual load address that indicates where to start reading /// memory from. /// /// \param[out] buf /// A byte buffer that is at least \a size bytes long that /// will receive the memory bytes. /// /// \param[in] size /// The number of bytes to read. /// /// \param[out] error /// An error that indicates the success or failure of this /// operation. If error indicates success (error.Success()), /// then the value returned can be trusted, otherwise zero /// will be returned. /// /// \return /// The number of bytes that were actually read into \a buf. If /// the returned number is greater than zero, yet less than \a /// size, then this function will get called again with \a /// vm_addr, \a buf, and \a size updated appropriately. Zero is /// returned in the case of an error. virtual size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error); /// Read of memory from a process. /// /// This function has the same semantics of ReadMemory except that it /// bypasses caching. /// /// \param[in] vm_addr /// A virtual load address that indicates where to start reading /// memory from. /// /// \param[out] buf /// A byte buffer that is at least \a size bytes long that /// will receive the memory bytes. /// /// \param[in] size /// The number of bytes to read. /// /// \param[out] error /// An error that indicates the success or failure of this /// operation. If error indicates success (error.Success()), /// then the value returned can be trusted, otherwise zero /// will be returned. /// /// \return /// The number of bytes that were actually read into \a buf. If /// the returned number is greater than zero, yet less than \a /// size, then this function will get called again with \a /// vm_addr, \a buf, and \a size updated appropriately. Zero is /// returned in the case of an error. size_t ReadMemoryFromInferior(lldb::addr_t vm_addr, void *buf, size_t size, Status &error); /// 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] vm_addr /// The virtual load address to start the memory read. /// /// \param[in] str /// 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(lldb::addr_t vm_addr, char *str, size_t max_bytes, Status &error, size_t type_width = 1); /// Read a NULL terminated C string from memory /// /// This function will read a cache page at a time until the NULL /// C string terminator is found. It will stop reading if the NULL /// termination byte isn't found before reading \a cstr_max_len bytes, and /// the results are always guaranteed to be NULL terminated (at most /// cstr_max_len - 1 bytes will be read). size_t ReadCStringFromMemory(lldb::addr_t vm_addr, char *cstr, size_t cstr_max_len, Status &error); size_t ReadCStringFromMemory(lldb::addr_t vm_addr, std::string &out_str, Status &error); /// Reads an unsigned integer of the specified byte size from process /// memory. /// /// \param[in] load_addr /// A load address of the integer to read. /// /// \param[in] byte_size /// The size in byte of the integer to read. /// /// \param[in] fail_value /// The value to return if we fail to read an integer. /// /// \param[out] error /// An error that indicates the success or failure of this /// operation. If error indicates success (error.Success()), /// then the value returned can be trusted, otherwise zero /// will be returned. /// /// \return /// The unsigned integer that was read from the process memory /// space. If the integer was smaller than a uint64_t, any /// unused upper bytes will be zero filled. If the process /// byte order differs from the host byte order, the integer /// value will be appropriately byte swapped into host byte /// order. uint64_t ReadUnsignedIntegerFromMemory(lldb::addr_t load_addr, size_t byte_size, uint64_t fail_value, Status &error); int64_t ReadSignedIntegerFromMemory(lldb::addr_t load_addr, size_t byte_size, int64_t fail_value, Status &error); lldb::addr_t ReadPointerFromMemory(lldb::addr_t vm_addr, Status &error); bool WritePointerToMemory(lldb::addr_t vm_addr, lldb::addr_t ptr_value, Status &error); /// Actually do the writing of memory to a process. /// /// \param[in] vm_addr /// A virtual load address that indicates where to start writing /// memory to. /// /// \param[in] buf /// A byte buffer that is at least \a size bytes long that /// contains the data to write. /// /// \param[in] size /// The number of bytes to write. /// /// \param[out] error /// An error value in case the memory write fails. /// /// \return /// The number of bytes that were actually written. virtual size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, Status &error) { error.SetErrorStringWithFormat( "error: %s does not support writing to processes", GetPluginName().GetCString()); return 0; } /// Write all or part of a scalar value to memory. /// /// The value contained in \a scalar will be swapped to match the byte order /// of the process that is being debugged. If \a size is less than the size /// of scalar, the least significant \a size bytes from scalar will be /// written. If \a size is larger than the byte size of scalar, then the /// extra space will be padded with zeros and the scalar value will be /// placed in the least significant bytes in memory. /// /// \param[in] vm_addr /// A virtual load address that indicates where to start writing /// memory to. /// /// \param[in] scalar /// The scalar to write to the debugged process. /// /// \param[in] size /// This value can be smaller or larger than the scalar value /// itself. If \a size is smaller than the size of \a scalar, /// the least significant bytes in \a scalar will be used. If /// \a size is larger than the byte size of \a scalar, then /// the extra space will be padded with zeros. If \a size is /// set to UINT32_MAX, then the size of \a scalar will be used. /// /// \param[out] error /// An error value in case the memory write fails. /// /// \return /// The number of bytes that were actually written. size_t WriteScalarToMemory(lldb::addr_t vm_addr, const Scalar &scalar, size_t size, Status &error); size_t ReadScalarIntegerFromMemory(lldb::addr_t addr, uint32_t byte_size, bool is_signed, Scalar &scalar, Status &error); /// Write memory to a process. /// /// This function will write memory to the current process's address space /// and maintain any traps that might be present due to software /// breakpoints. /// /// This function is not meant to be overridden by Process subclasses, the /// subclasses should implement Process::DoWriteMemory (lldb::addr_t, /// size_t, void *). /// /// \param[in] vm_addr /// A virtual load address that indicates where to start writing /// memory to. /// /// \param[in] buf /// A byte buffer that is at least \a size bytes long that /// contains the data to write. /// /// \param[in] size /// The number of bytes to write. /// /// \return /// The number of bytes that were actually written. // TODO: change this to take an ArrayRef size_t WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, Status &error); /// Actually allocate memory in the process. /// /// This function will allocate memory in the process's address space. This /// can't rely on the generic function calling mechanism, since that /// requires this function. /// /// \param[in] size /// The size of the allocation requested. /// /// \return /// The address of the allocated buffer in the process, or /// LLDB_INVALID_ADDRESS if the allocation failed. virtual lldb::addr_t DoAllocateMemory(size_t size, uint32_t permissions, Status &error) { error.SetErrorStringWithFormat( "error: %s does not support allocating in the debug process", GetPluginName().GetCString()); return LLDB_INVALID_ADDRESS; } virtual Status WriteObjectFile(std::vector entries); /// The public interface to allocating memory in the process. /// /// This function will allocate memory in the process's address space. This /// can't rely on the generic function calling mechanism, since that /// requires this function. /// /// \param[in] size /// The size of the allocation requested. /// /// \param[in] permissions /// Or together any of the lldb::Permissions bits. The permissions on /// a given memory allocation can't be changed after allocation. Note /// that a block that isn't set writable can still be written on from /// lldb, /// just not by the process itself. /// /// \param[in,out] error /// An error object to fill in if things go wrong. /// \return /// The address of the allocated buffer in the process, or /// LLDB_INVALID_ADDRESS if the allocation failed. lldb::addr_t AllocateMemory(size_t size, uint32_t permissions, Status &error); /// The public interface to allocating memory in the process, this also /// clears the allocated memory. /// /// This function will allocate memory in the process's address space. This /// can't rely on the generic function calling mechanism, since that /// requires this function. /// /// \param[in] size /// The size of the allocation requested. /// /// \param[in] permissions /// Or together any of the lldb::Permissions bits. The permissions on /// a given memory allocation can't be changed after allocation. Note /// that a block that isn't set writable can still be written on from /// lldb, /// just not by the process itself. /// /// \param[in,out] error /// An error object to fill in if things go wrong. /// /// \return /// The address of the allocated buffer in the process, or /// LLDB_INVALID_ADDRESS if the allocation failed. lldb::addr_t CallocateMemory(size_t size, uint32_t permissions, Status &error); /// Resolve dynamically loaded indirect functions. /// /// \param[in] address /// The load address of the indirect function to resolve. /// /// \param[out] error /// An error value in case the resolve fails. /// /// \return /// The address of the resolved function. /// LLDB_INVALID_ADDRESS if the resolution failed. virtual lldb::addr_t ResolveIndirectFunction(const Address *address, Status &error); /// Locate the memory region that contains load_addr. /// /// If load_addr is within the address space the process has mapped /// range_info will be filled in with the start and end of that range as /// well as the permissions for that range and range_info.GetMapped will /// return true. /// /// If load_addr is outside any mapped region then range_info will have its /// start address set to load_addr and the end of the range will indicate /// the start of the next mapped range or be set to LLDB_INVALID_ADDRESS if /// there are no valid mapped ranges between load_addr and the end of the /// process address space. /// /// GetMemoryRegionInfo will only return an error if it is unimplemented for /// the current process. /// /// \param[in] load_addr /// The load address to query the range_info for. /// /// \param[out] range_info /// An range_info value containing the details of the range. /// /// \return /// An error value. virtual Status GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info) { Status error; error.SetErrorString("Process::GetMemoryRegionInfo() not supported"); return error; } /// Obtain all the mapped memory regions within this process. /// /// \param[out] region_list /// A vector to contain MemoryRegionInfo objects for all mapped /// ranges. /// /// \return /// An error value. virtual Status GetMemoryRegions(lldb_private::MemoryRegionInfos ®ion_list); virtual Status GetWatchpointSupportInfo(uint32_t &num) { Status error; num = 0; error.SetErrorString("Process::GetWatchpointSupportInfo() not supported"); return error; } virtual Status GetWatchpointSupportInfo(uint32_t &num, bool &after) { Status error; num = 0; after = true; error.SetErrorString("Process::GetWatchpointSupportInfo() not supported"); return error; } lldb::ModuleSP ReadModuleFromMemory(const FileSpec &file_spec, lldb::addr_t header_addr, size_t size_to_read = 512); /// Attempt to get the attributes for a region of memory in the process. /// /// It may be possible for the remote debug server to inspect attributes for /// a region of memory in the process, such as whether there is a valid page /// of memory at a given address or whether that page is /// readable/writable/executable by the process. /// /// \param[in] load_addr /// The address of interest in the process. /// /// \param[out] permissions /// If this call returns successfully, this bitmask will have /// its Permissions bits set to indicate whether the region is /// readable/writable/executable. If this call fails, the /// bitmask values are undefined. /// /// \return /// Returns true if it was able to determine the attributes of the /// memory region. False if not. virtual bool GetLoadAddressPermissions(lldb::addr_t load_addr, uint32_t &permissions); /// Determines whether executing JIT-compiled code in this process is /// possible. /// /// \return /// True if execution of JIT code is possible; false otherwise. bool CanJIT(); /// Sets whether executing JIT-compiled code in this process is possible. /// /// \param[in] can_jit /// True if execution of JIT code is possible; false otherwise. void SetCanJIT(bool can_jit); /// Determines whether executing function calls using the interpreter is /// possible for this process. /// /// \return /// True if possible; false otherwise. bool CanInterpretFunctionCalls() { return m_can_interpret_function_calls; } /// Sets whether executing function calls using the interpreter is possible /// for this process. /// /// \param[in] can_interpret_function_calls /// True if possible; false otherwise. void SetCanInterpretFunctionCalls(bool can_interpret_function_calls) { m_can_interpret_function_calls = can_interpret_function_calls; } /// Sets whether executing code in this process is possible. This could be /// either through JIT or interpreting. /// /// \param[in] can_run_code /// True if execution of code is possible; false otherwise. void SetCanRunCode(bool can_run_code); /// Actually deallocate memory in the process. /// /// This function will deallocate memory in the process's address space that /// was allocated with AllocateMemory. /// /// \param[in] ptr /// A return value from AllocateMemory, pointing to the memory you /// want to deallocate. /// /// \return /// \btrue if the memory was deallocated, \bfalse otherwise. virtual Status DoDeallocateMemory(lldb::addr_t ptr) { Status error; error.SetErrorStringWithFormat( "error: %s does not support deallocating in the debug process", GetPluginName().GetCString()); return error; } /// The public interface to deallocating memory in the process. /// /// This function will deallocate memory in the process's address space that /// was allocated with AllocateMemory. /// /// \param[in] ptr /// A return value from AllocateMemory, pointing to the memory you /// want to deallocate. /// /// \return /// \btrue if the memory was deallocated, \bfalse otherwise. Status DeallocateMemory(lldb::addr_t ptr); /// Get any available STDOUT. /// /// Calling this method is a valid operation only if all of the following /// conditions are true: 1) The process was launched, and not attached to. /// 2) The process was not launched with eLaunchFlagDisableSTDIO. 3) The /// process was launched without supplying a valid file path /// for STDOUT. /// /// Note that the implementation will probably need to start a read thread /// in the background to make sure that the pipe is drained and the STDOUT /// buffered appropriately, to prevent the process from deadlocking trying /// to write to a full buffer. /// /// Events will be queued indicating that there is STDOUT available that can /// be retrieved using this function. /// /// \param[out] buf /// A buffer that will receive any STDOUT bytes that are /// currently available. /// /// \param[in] buf_size /// The size in bytes for the buffer \a buf. /// /// \return /// The number of bytes written into \a buf. If this value is /// equal to \a buf_size, another call to this function should /// be made to retrieve more STDOUT data. virtual size_t GetSTDOUT(char *buf, size_t buf_size, Status &error); /// Get any available STDERR. /// /// Calling this method is a valid operation only if all of the following /// conditions are true: 1) The process was launched, and not attached to. /// 2) The process was not launched with eLaunchFlagDisableSTDIO. 3) The /// process was launched without supplying a valid file path /// for STDERR. /// /// Note that the implementation will probably need to start a read thread /// in the background to make sure that the pipe is drained and the STDERR /// buffered appropriately, to prevent the process from deadlocking trying /// to write to a full buffer. /// /// Events will be queued indicating that there is STDERR available that can /// be retrieved using this function. /// /// \param[in] buf /// A buffer that will receive any STDERR bytes that are /// currently available. /// /// \param[out] buf_size /// The size in bytes for the buffer \a buf. /// /// \return /// The number of bytes written into \a buf. If this value is /// equal to \a buf_size, another call to this function should /// be made to retrieve more STDERR data. virtual size_t GetSTDERR(char *buf, size_t buf_size, Status &error); /// Puts data into this process's STDIN. /// /// Calling this method is a valid operation only if all of the following /// conditions are true: 1) The process was launched, and not attached to. /// 2) The process was not launched with eLaunchFlagDisableSTDIO. 3) The /// process was launched without supplying a valid file path /// for STDIN. /// /// \param[in] buf /// A buffer that contains the data to write to the process's STDIN. /// /// \param[in] buf_size /// The size in bytes for the buffer \a buf. /// /// \return /// The number of bytes written into \a buf. If this value is /// less than \a buf_size, another call to this function should /// be made to write the rest of the data. virtual size_t PutSTDIN(const char *buf, size_t buf_size, Status &error) { error.SetErrorString("stdin unsupported"); return 0; } /// Get any available profile data. /// /// \param[out] buf /// A buffer that will receive any profile data bytes that are /// currently available. /// /// \param[out] buf_size /// The size in bytes for the buffer \a buf. /// /// \return /// The number of bytes written into \a buf. If this value is /// equal to \a buf_size, another call to this function should /// be made to retrieve more profile data. virtual size_t GetAsyncProfileData(char *buf, size_t buf_size, Status &error); // Process Breakpoints size_t GetSoftwareBreakpointTrapOpcode(BreakpointSite *bp_site); virtual Status EnableBreakpointSite(BreakpointSite *bp_site) { Status error; error.SetErrorStringWithFormat( "error: %s does not support enabling breakpoints", GetPluginName().GetCString()); return error; } virtual Status DisableBreakpointSite(BreakpointSite *bp_site) { Status error; error.SetErrorStringWithFormat( "error: %s does not support disabling breakpoints", GetPluginName().GetCString()); return error; } // This is implemented completely using the lldb::Process API. Subclasses // don't need to implement this function unless the standard flow of read // existing opcode, write breakpoint opcode, verify breakpoint opcode doesn't // work for a specific process plug-in. virtual Status EnableSoftwareBreakpoint(BreakpointSite *bp_site); // This is implemented completely using the lldb::Process API. Subclasses // don't need to implement this function unless the standard flow of // restoring original opcode in memory and verifying the restored opcode // doesn't work for a specific process plug-in. virtual Status DisableSoftwareBreakpoint(BreakpointSite *bp_site); BreakpointSiteList &GetBreakpointSiteList(); const BreakpointSiteList &GetBreakpointSiteList() const; void DisableAllBreakpointSites(); Status ClearBreakpointSiteByID(lldb::user_id_t break_id); lldb::break_id_t CreateBreakpointSite(const lldb::BreakpointLocationSP &owner, bool use_hardware); Status DisableBreakpointSiteByID(lldb::user_id_t break_id); Status EnableBreakpointSiteByID(lldb::user_id_t break_id); // BreakpointLocations use RemoveOwnerFromBreakpointSite to remove themselves // from the owner's list of this breakpoint sites. void RemoveOwnerFromBreakpointSite(lldb::user_id_t owner_id, lldb::user_id_t owner_loc_id, lldb::BreakpointSiteSP &bp_site_sp); // Process Watchpoints (optional) virtual Status EnableWatchpoint(Watchpoint *wp, bool notify = true); virtual Status DisableWatchpoint(Watchpoint *wp, bool notify = true); // Thread Queries virtual bool UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) = 0; void UpdateThreadListIfNeeded(); ThreadList &GetThreadList() { return m_thread_list; } // When ExtendedBacktraces are requested, the HistoryThreads that are created // need an owner -- they're saved here in the Process. The threads in this // list are not iterated over - driver programs need to request the extended // backtrace calls starting from a root concrete thread one by one. ThreadList &GetExtendedThreadList() { return m_extended_thread_list; } ThreadList::ThreadIterable Threads() { return m_thread_list.Threads(); } uint32_t GetNextThreadIndexID(uint64_t thread_id); lldb::ThreadSP CreateOSPluginThread(lldb::tid_t tid, lldb::addr_t context); // Returns true if an index id has been assigned to a thread. bool HasAssignedIndexIDToThread(uint64_t sb_thread_id); // Given a thread_id, it will assign a more reasonable index id for display // to the user. If the thread_id has previously been assigned, the same index // id will be used. uint32_t AssignIndexIDToThread(uint64_t thread_id); // Queue Queries void UpdateQueueListIfNeeded(); QueueList &GetQueueList() { UpdateQueueListIfNeeded(); return m_queue_list; } QueueList::QueueIterable Queues() { UpdateQueueListIfNeeded(); return m_queue_list.Queues(); } // Event Handling lldb::StateType GetNextEvent(lldb::EventSP &event_sp); // Returns the process state when it is stopped. If specified, event_sp_ptr // is set to the event which triggered the stop. If wait_always = false, and // the process is already stopped, this function returns immediately. If the // process is hijacked and use_run_lock is true (the default), then this // function releases the run lock after the stop. Setting use_run_lock to // false will avoid this behavior. lldb::StateType WaitForProcessToStop(const Timeout &timeout, lldb::EventSP *event_sp_ptr = nullptr, bool wait_always = true, lldb::ListenerSP hijack_listener = lldb::ListenerSP(), Stream *stream = nullptr, bool use_run_lock = true); uint32_t GetIOHandlerID() const { return m_iohandler_sync.GetValue(); } /// Waits for the process state to be running within a given msec timeout. /// /// The main purpose of this is to implement an interlock waiting for /// HandlePrivateEvent to push an IOHandler. /// /// \param[in] timeout /// The maximum time length to wait for the process to transition to the /// eStateRunning state. void SyncIOHandler(uint32_t iohandler_id, const Timeout &timeout); lldb::StateType GetStateChangedEvents( lldb::EventSP &event_sp, const Timeout &timeout, lldb::ListenerSP hijack_listener); // Pass an empty ListenerSP to use builtin listener /// Centralize the code that handles and prints descriptions for process /// state changes. /// /// \param[in] event_sp /// The process state changed event /// /// \param[in] stream /// The output stream to get the state change description /// /// \param[in,out] pop_process_io_handler /// If this value comes in set to \b true, then pop the Process IOHandler /// if needed. /// Else this variable will be set to \b true or \b false to indicate if /// the process /// needs to have its process IOHandler popped. /// /// \return /// \b true if the event describes a process state changed event, \b false /// otherwise. static bool HandleProcessStateChangedEvent(const lldb::EventSP &event_sp, Stream *stream, bool &pop_process_io_handler); Event *PeekAtStateChangedEvents(); class ProcessEventHijacker { public: ProcessEventHijacker(Process &process, lldb::ListenerSP listener_sp) : m_process(process) { m_process.HijackProcessEvents(listener_sp); } ~ProcessEventHijacker() { m_process.RestoreProcessEvents(); } private: Process &m_process; }; friend class ProcessEventHijacker; friend class ProcessProperties; /// If you need to ensure that you and only you will hear about some public /// event, then make a new listener, set to listen to process events, and /// then call this with that listener. Then you will have to wait on that /// listener explicitly for events (rather than using the GetNextEvent & /// WaitFor* calls above. Be sure to call RestoreProcessEvents when you are /// done. /// /// \param[in] listener_sp /// This is the new listener to whom all process events will be delivered. /// /// \return /// Returns \b true if the new listener could be installed, /// \b false otherwise. bool HijackProcessEvents(lldb::ListenerSP listener_sp); /// Restores the process event broadcasting to its normal state. /// void RestoreProcessEvents(); bool StateChangedIsHijackedForSynchronousResume(); bool StateChangedIsExternallyHijacked(); const lldb::ABISP &GetABI(); OperatingSystem *GetOperatingSystem() { return m_os_up.get(); } std::vector GetLanguageRuntimes(); LanguageRuntime *GetLanguageRuntime(lldb::LanguageType language); bool IsPossibleDynamicValue(ValueObject &in_value); bool IsRunning() const; DynamicCheckerFunctions *GetDynamicCheckers() { return m_dynamic_checkers_up.get(); } void SetDynamicCheckers(DynamicCheckerFunctions *dynamic_checkers); /// Prune ThreadPlanStacks for unreported threads. /// /// \param[in] tid /// The tid whose Plan Stack we are seeking to prune. /// /// \return /// \b true if the TID is found or \b false if not. bool PruneThreadPlansForTID(lldb::tid_t tid); /// Prune ThreadPlanStacks for all unreported threads. void PruneThreadPlans(); /// Find the thread plan stack associated with thread with \a tid. /// /// \param[in] tid /// The tid whose Plan Stack we are seeking. /// /// \return /// Returns a ThreadPlan if the TID is found or nullptr if not. ThreadPlanStack *FindThreadPlans(lldb::tid_t tid); /// Dump the thread plans associated with thread with \a tid. /// /// \param[in/out] strm /// The stream to which to dump the output /// /// \param[in] tid /// The tid whose Plan Stack we are dumping /// /// \param[in] desc_level /// How much detail to dump /// /// \param[in] internal /// If \b true dump all plans, if false only user initiated plans /// /// \param[in] condense_trivial /// If true, only dump a header if the plan stack is just the base plan. /// /// \param[in] skip_unreported_plans /// If true, only dump a plan if it is currently backed by an /// lldb_private::Thread *. /// /// \return /// Returns \b true if TID was found, \b false otherwise bool DumpThreadPlansForTID(Stream &strm, lldb::tid_t tid, lldb::DescriptionLevel desc_level, bool internal, bool condense_trivial, bool skip_unreported_plans); /// Dump all the thread plans for this process. /// /// \param[in/out] strm /// The stream to which to dump the output /// /// \param[in] desc_level /// How much detail to dump /// /// \param[in] internal /// If \b true dump all plans, if false only user initiated plans /// /// \param[in] condense_trivial /// If true, only dump a header if the plan stack is just the base plan. /// /// \param[in] skip_unreported_plans /// If true, skip printing all thread plan stacks that don't currently /// have a backing lldb_private::Thread *. void DumpThreadPlans(Stream &strm, lldb::DescriptionLevel desc_level, bool internal, bool condense_trivial, bool skip_unreported_plans); /// Call this to set the lldb in the mode where it breaks on new thread /// creations, and then auto-restarts. This is useful when you are trying /// to run only one thread, but either that thread or the kernel is creating /// new threads in the process. If you stop when the thread is created, you /// can immediately suspend it, and keep executing only the one thread you /// intend. /// /// \return /// Returns \b true if we were able to start up the notification /// \b false otherwise. virtual bool StartNoticingNewThreads() { return true; } /// Call this to turn off the stop & notice new threads mode. /// /// \return /// Returns \b true if we were able to start up the notification /// \b false otherwise. virtual bool StopNoticingNewThreads() { return true; } void SetRunningUserExpression(bool on); void SetRunningUtilityFunction(bool on); // lldb::ExecutionContextScope pure virtual functions lldb::TargetSP CalculateTarget() override; lldb::ProcessSP CalculateProcess() override { return shared_from_this(); } lldb::ThreadSP CalculateThread() override { return lldb::ThreadSP(); } lldb::StackFrameSP CalculateStackFrame() override { return lldb::StackFrameSP(); } void CalculateExecutionContext(ExecutionContext &exe_ctx) override; void SetSTDIOFileDescriptor(int file_descriptor); // Add a permanent region of memory that should never be read or written to. // This can be used to ensure that memory reads or writes to certain areas of // memory never end up being sent to the DoReadMemory or DoWriteMemory // functions which can improve performance. void AddInvalidMemoryRegion(const LoadRange ®ion); // Remove a permanent region of memory that should never be read or written // to that was previously added with AddInvalidMemoryRegion. bool RemoveInvalidMemoryRange(const LoadRange ®ion); // If the setup code of a thread plan needs to do work that might involve // calling a function in the target, it should not do that work directly in // one of the thread plan functions (DidPush/WillResume) because such work // needs to be handled carefully. Instead, put that work in a // PreResumeAction callback, and register it with the process. It will get // done before the actual "DoResume" gets called. typedef bool(PreResumeActionCallback)(void *); void AddPreResumeAction(PreResumeActionCallback callback, void *baton); bool RunPreResumeActions(); void ClearPreResumeActions(); void ClearPreResumeAction(PreResumeActionCallback callback, void *baton); ProcessRunLock &GetRunLock(); bool CurrentThreadIsPrivateStateThread(); virtual Status SendEventData(const char *data) { Status return_error("Sending an event is not supported for this process."); return return_error; } lldb::ThreadCollectionSP GetHistoryThreads(lldb::addr_t addr); lldb::InstrumentationRuntimeSP GetInstrumentationRuntime(lldb::InstrumentationRuntimeType type); /// Try to fetch the module specification for a module with the given file /// name and architecture. Process sub-classes have to override this method /// if they support platforms where the Platform object can't get the module /// spec for all module. /// /// \param[in] module_file_spec /// The file name of the module to get specification for. /// /// \param[in] arch /// The architecture of the module to get specification for. /// /// \param[out] module_spec /// The fetched module specification if the return value is /// \b true, unchanged otherwise. /// /// \return /// Returns \b true if the module spec fetched successfully, /// \b false otherwise. virtual bool GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, ModuleSpec &module_spec); virtual void PrefetchModuleSpecs(llvm::ArrayRef module_file_specs, const llvm::Triple &triple) {} /// Try to find the load address of a file. /// The load address is defined as the address of the first memory region /// what contains data mapped from the specified file. /// /// \param[in] file /// The name of the file whose load address we are looking for /// /// \param[out] is_loaded /// \b True if the file is loaded into the memory and false /// otherwise. /// /// \param[out] load_addr /// The load address of the file if it is loaded into the /// processes address space, LLDB_INVALID_ADDRESS otherwise. virtual Status GetFileLoadAddress(const FileSpec &file, bool &is_loaded, lldb::addr_t &load_addr) { return Status("Not supported"); } size_t AddImageToken(lldb::addr_t image_ptr); lldb::addr_t GetImagePtrFromToken(size_t token) const; void ResetImageToken(size_t token); /// Find the next branch instruction to set a breakpoint on /// /// When instruction stepping through a source line, instead of stepping /// through each instruction, we can put a breakpoint on the next branch /// instruction (within the range of instructions we are stepping through) /// and continue the process to there, yielding significant performance /// benefits over instruction stepping. /// /// \param[in] default_stop_addr /// The address of the instruction where lldb would put a /// breakpoint normally. /// /// \param[in] range_bounds /// The range which the breakpoint must be contained within. /// Typically a source line. /// /// \return /// The address of the next branch instruction, or the end of /// the range provided in range_bounds. If there are any /// problems with the disassembly or getting the instructions, /// the original default_stop_addr will be returned. Address AdvanceAddressToNextBranchInstruction(Address default_stop_addr, AddressRange range_bounds); /// Configure asynchronous structured data feature. /// /// Each Process type that supports using an asynchronous StructuredData /// feature should implement this to enable/disable/configure the feature. /// The default implementation here will always return an error indiciating /// the feature is unsupported. /// /// StructuredDataPlugin implementations will call this to configure a /// feature that has been reported as being supported. /// /// \param[in] type_name /// The StructuredData type name as previously discovered by /// the Process-derived instance. /// /// \param[in] config_sp /// Configuration data for the feature being enabled. This config /// data, which may be null, will be passed along to the feature /// to process. The feature will dictate whether this is a dictionary, /// an array or some other object. If the feature needs to be /// set up properly before it can be enabled, then the config should /// also take an enable/disable flag. /// /// \return /// Returns the result of attempting to configure the feature. virtual Status ConfigureStructuredData(ConstString type_name, const StructuredData::ObjectSP &config_sp); /// Broadcasts the given structured data object from the given plugin. /// /// StructuredDataPlugin instances can use this to optionally broadcast any /// of their data if they want to make it available for clients. The data /// will come in on the structured data event bit /// (eBroadcastBitStructuredData). /// /// \param[in] object_sp /// The structured data object to broadcast. /// /// \param[in] plugin_sp /// The plugin that will be reported in the event's plugin /// parameter. void BroadcastStructuredData(const StructuredData::ObjectSP &object_sp, const lldb::StructuredDataPluginSP &plugin_sp); /// Returns the StructuredDataPlugin associated with a given type name, if /// there is one. /// /// There will only be a plugin for a given StructuredDataType if the /// debugged process monitor claims that the feature is supported. This is /// one way to tell whether a feature is available. /// /// \return /// The plugin if one is available for the specified feature; /// otherwise, returns an empty shared pointer. lldb::StructuredDataPluginSP GetStructuredDataPlugin(ConstString type_name) const; /// Starts tracing with the configuration provided in options. To enable /// tracing on the complete process the thread_id in the options should be /// set to LLDB_INVALID_THREAD_ID. The API returns a user_id which is needed /// by other API's that manipulate the trace instance. The handling of /// erroneous or unsupported configuration is left to the trace technology /// implementations in the server, as they could be returned as an error, or /// rounded to a valid configuration to start tracing. In the later case the /// GetTraceConfig should supply the actual used trace configuration. virtual lldb::user_id_t StartTrace(const TraceOptions &options, Status &error) { error.SetErrorString("Not implemented"); return LLDB_INVALID_UID; } /// Stops the tracing instance leading to deletion of the trace data. The /// tracing instance is identified by the user_id which is obtained when /// tracing was started from the StartTrace. In case tracing of the complete /// process needs to be stopped the thread_id should be set to /// LLDB_INVALID_THREAD_ID. In the other case that tracing on an individual /// thread needs to be stopped a thread_id can be supplied. virtual Status StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) { return Status("Not implemented"); } /// Provides the trace data as raw bytes. A buffer needs to be supplied to /// copy the trace data. The exact behavior of this API may vary across /// trace technology, as some may support partial reading of the trace data /// from a specified offset while some may not. The thread_id should be used /// to select a particular thread for trace extraction. virtual Status GetData(lldb::user_id_t uid, lldb::tid_t thread_id, llvm::MutableArrayRef &buffer, size_t offset = 0) { return Status("Not implemented"); } /// Similar API as above except for obtaining meta data virtual Status GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id, llvm::MutableArrayRef &buffer, size_t offset = 0) { return Status("Not implemented"); } /// API to obtain the trace configuration used by a trace instance. /// Configurations that may be specific to some trace technology should be /// stored in the custom parameters. The options are transported to the /// server, which shall interpret accordingly. The thread_id can be /// specified in the options to obtain the configuration used by a specific /// thread. The thread_id specified should also match the uid otherwise an /// error will be returned. virtual Status GetTraceConfig(lldb::user_id_t uid, TraceOptions &options) { return Status("Not implemented"); } // This calls a function of the form "void * (*)(void)". bool CallVoidArgVoidPtrReturn(const Address *address, lldb::addr_t &returned_func, bool trap_exceptions = false); protected: void SetState(lldb::EventSP &event_sp); lldb::StateType GetPrivateState(); /// The "private" side of resuming a process. This doesn't alter the state /// of m_run_lock, but just causes the process to resume. /// /// \return /// An Status object describing the success or failure of the resume. Status PrivateResume(); // Called internally void CompleteAttach(); /// Print a user-visible warning one time per Process /// /// A facility for printing a warning to the user once per repeat_key. /// /// warning_type is from the Process::Warnings enums. repeat_key is a /// pointer value that will be used to ensure that the warning message is /// not printed multiple times. For instance, with a warning about a /// function being optimized, you can pass the CompileUnit pointer to have /// the warning issued for only the first function in a CU, or the Function /// pointer to have it issued once for every function, or a Module pointer /// to have it issued once per Module. /// /// Classes outside Process should call a specific PrintWarning method so /// that the warning strings are all centralized in Process, instead of /// calling PrintWarning() directly. /// /// \param [in] warning_type /// One of the types defined in Process::Warnings. /// /// \param [in] repeat_key /// A pointer value used to ensure that the warning is only printed once. /// May be nullptr, indicating that the warning is printed unconditionally /// every time. /// /// \param [in] fmt /// printf style format string void PrintWarning(uint64_t warning_type, const void *repeat_key, const char *fmt, ...) __attribute__((format(printf, 4, 5))); // NextEventAction provides a way to register an action on the next event // that is delivered to this process. There is currently only one next event // action allowed in the process at one time. If a new "NextEventAction" is // added while one is already present, the old action will be discarded (with // HandleBeingUnshipped called after it is discarded.) // // If you want to resume the process as a result of a resume action, call // RequestResume, don't call Resume directly. class NextEventAction { public: enum EventActionResult { eEventActionSuccess, eEventActionRetry, eEventActionExit }; NextEventAction(Process *process) : m_process(process) {} virtual ~NextEventAction() = default; virtual EventActionResult PerformAction(lldb::EventSP &event_sp) = 0; virtual void HandleBeingUnshipped() {} virtual EventActionResult HandleBeingInterrupted() = 0; virtual const char *GetExitString() = 0; void RequestResume() { m_process->m_resume_requested = true; } protected: Process *m_process; }; void SetNextEventAction(Process::NextEventAction *next_event_action) { if (m_next_event_action_up.get()) m_next_event_action_up->HandleBeingUnshipped(); m_next_event_action_up.reset(next_event_action); } // This is the completer for Attaching: class AttachCompletionHandler : public NextEventAction { public: AttachCompletionHandler(Process *process, uint32_t exec_count); ~AttachCompletionHandler() override = default; EventActionResult PerformAction(lldb::EventSP &event_sp) override; EventActionResult HandleBeingInterrupted() override; const char *GetExitString() override; private: uint32_t m_exec_count; std::string m_exit_string; }; bool PrivateStateThreadIsValid() const { lldb::StateType state = m_private_state.GetValue(); return state != lldb::eStateInvalid && state != lldb::eStateDetached && state != lldb::eStateExited && m_private_state_thread.IsJoinable(); } void ForceNextEventDelivery() { m_force_next_event_delivery = true; } /// Loads any plugins associated with asynchronous structured data and maps /// the relevant supported type name to the plugin. /// /// Processes can receive asynchronous structured data from the process /// monitor. This method will load and map any structured data plugins that /// support the given set of supported type names. Later, if any of these /// features are enabled, the process monitor is free to generate /// asynchronous structured data. The data must come in as a single \b /// StructuredData::Dictionary. That dictionary must have a string field /// named 'type', with a value that equals the relevant type name string /// (one of the values in \b supported_type_names). /// /// \param[in] supported_type_names /// An array of zero or more type names. Each must be unique. /// For each entry in the list, a StructuredDataPlugin will be /// searched for that supports the structured data type name. void MapSupportedStructuredDataPlugins( const StructuredData::Array &supported_type_names); /// Route the incoming structured data dictionary to the right plugin. /// /// The incoming structured data must be a dictionary, and it must have a /// key named 'type' that stores a string value. The string value must be /// the name of the structured data feature that knows how to handle it. /// /// \param[in] object_sp /// When non-null and pointing to a dictionary, the 'type' /// key's string value is used to look up the plugin that /// was registered for that structured data type. It then /// calls the following method on the StructuredDataPlugin /// instance: /// /// virtual void /// HandleArrivalOfStructuredData(Process &process, /// ConstString type_name, /// const StructuredData::ObjectSP /// &object_sp) /// /// \return /// True if the structured data was routed to a plugin; otherwise, /// false. bool RouteAsyncStructuredData(const StructuredData::ObjectSP object_sp); // Type definitions typedef std::map LanguageRuntimeCollection; typedef std::unordered_set WarningsPointerSet; typedef std::map WarningsCollection; struct PreResumeCallbackAndBaton { bool (*callback)(void *); void *baton; PreResumeCallbackAndBaton(PreResumeActionCallback in_callback, void *in_baton) : callback(in_callback), baton(in_baton) {} bool operator== (const PreResumeCallbackAndBaton &rhs) { return callback == rhs.callback && baton == rhs.baton; } }; using StructuredDataPluginMap = std::map; // Member variables std::weak_ptr m_target_wp; ///< The target that owns this process. ThreadSafeValue m_public_state; ThreadSafeValue m_private_state; // The actual state of our process Broadcaster m_private_state_broadcaster; // This broadcaster feeds state // changed events into the private // state thread's listener. Broadcaster m_private_state_control_broadcaster; // This is the control // broadcaster, used to // pause, resume & stop the // private state thread. lldb::ListenerSP m_private_state_listener_sp; // This is the listener for the // private state thread. HostThread m_private_state_thread; ///< Thread ID for the thread that watches ///internal state events ProcessModID m_mod_id; ///< Tracks the state of the process over stops and ///other alterations. uint32_t m_process_unique_id; ///< Each lldb_private::Process class that is ///created gets a unique integer ID that ///increments with each new instance uint32_t m_thread_index_id; ///< Each thread is created with a 1 based index ///that won't get re-used. std::map m_thread_id_to_index_id_map; int m_exit_status; ///< The exit status of the process, or -1 if not set. std::string m_exit_string; ///< A textual description of why a process exited. std::mutex m_exit_status_mutex; ///< Mutex so m_exit_status m_exit_string can ///be safely accessed from multiple threads std::recursive_mutex m_thread_mutex; ThreadList m_thread_list_real; ///< The threads for this process as are known ///to the protocol we are debugging with ThreadList m_thread_list; ///< The threads for this process as the user will ///see them. This is usually the same as ///< m_thread_list_real, but might be different if there is an OS plug-in ///creating memory threads ThreadPlanStackMap m_thread_plans; ///< This is the list of thread plans for /// threads in m_thread_list, as well as /// threads we knew existed, but haven't /// determined that they have died yet. ThreadList m_extended_thread_list; ///< Owner for extended threads that may be ///generated, cleared on natural stops uint32_t m_extended_thread_stop_id; ///< The natural stop id when ///extended_thread_list was last updated QueueList m_queue_list; ///< The list of libdispatch queues at a given stop point uint32_t m_queue_list_stop_id; ///< The natural stop id when queue list was ///last fetched std::vector m_notifications; ///< The list of notifications ///that this process can deliver. std::vector m_image_tokens; lldb::ListenerSP m_listener_sp; ///< Shared pointer to the listener used for ///public events. Can not be empty. BreakpointSiteList m_breakpoint_site_list; ///< This is the list of breakpoint ///locations we intend to insert in ///the target. lldb::DynamicLoaderUP m_dyld_up; lldb::JITLoaderListUP m_jit_loaders_up; lldb::DynamicCheckerFunctionsUP m_dynamic_checkers_up; ///< The functions used /// by the expression /// parser to validate /// data that /// expressions use. lldb::OperatingSystemUP m_os_up; lldb::SystemRuntimeUP m_system_runtime_up; lldb::UnixSignalsSP m_unix_signals_sp; /// This is the current signal set for this process. lldb::ABISP m_abi_sp; lldb::IOHandlerSP m_process_input_reader; Communication m_stdio_communication; std::recursive_mutex m_stdio_communication_mutex; bool m_stdin_forward; /// Remember if stdin must be forwarded to remote debug /// server std::string m_stdout_data; std::string m_stderr_data; std::recursive_mutex m_profile_data_comm_mutex; std::vector m_profile_data; Predicate m_iohandler_sync; MemoryCache m_memory_cache; AllocatedMemoryCache m_allocated_memory_cache; bool m_should_detach; /// Should we detach if the process object goes away /// with an explicit call to Kill or Detach? LanguageRuntimeCollection m_language_runtimes; std::recursive_mutex m_language_runtimes_mutex; InstrumentationRuntimeCollection m_instrumentation_runtimes; std::unique_ptr m_next_event_action_up; std::vector m_pre_resume_actions; ProcessRunLock m_public_run_lock; ProcessRunLock m_private_run_lock; bool m_currently_handling_do_on_removals; bool m_resume_requested; // If m_currently_handling_event or // m_currently_handling_do_on_removals are true, // Resume will only request a resume, using this // flag to check. bool m_finalizing; // This is set at the beginning of Process::Finalize() to // stop functions from looking up or creating things // during a finalize call bool m_finalize_called; // This is set at the end of Process::Finalize() bool m_clear_thread_plans_on_stop; bool m_force_next_event_delivery; lldb::StateType m_last_broadcast_state; /// This helps with the Public event /// coalescing in /// ShouldBroadcastEvent. std::map m_resolved_indirect_addresses; bool m_destroy_in_process; bool m_can_interpret_function_calls; // Some targets, e.g the OSX kernel, // don't support the ability to modify // the stack. WarningsCollection m_warnings_issued; // A set of object pointers which have // already had warnings printed std::mutex m_run_thread_plan_lock; StructuredDataPluginMap m_structured_data_plugin_map; enum { eCanJITDontKnow = 0, eCanJITYes, eCanJITNo } m_can_jit; std::unique_ptr m_dlopen_utility_func_up; llvm::once_flag m_dlopen_utility_func_flag_once; size_t RemoveBreakpointOpcodesFromBuffer(lldb::addr_t addr, size_t size, uint8_t *buf) const; void SynchronouslyNotifyStateChanged(lldb::StateType state); void SetPublicState(lldb::StateType new_state, bool restarted); void SetPrivateState(lldb::StateType state); bool StartPrivateStateThread(bool is_secondary_thread = false); void StopPrivateStateThread(); void PausePrivateStateThread(); void ResumePrivateStateThread(); private: struct PrivateStateThreadArgs { PrivateStateThreadArgs(Process *p, bool s) : process(p), is_secondary_thread(s){}; Process *process; bool is_secondary_thread; }; // arg is a pointer to a new'ed PrivateStateThreadArgs structure. // PrivateStateThread will free it for you. static lldb::thread_result_t PrivateStateThread(void *arg); // The starts up the private state thread that will watch for events from the // debugee. Pass true for is_secondary_thread in the case where you have to // temporarily spin up a secondary state thread to handle events from a hand- // called function on the primary private state thread. lldb::thread_result_t RunPrivateStateThread(bool is_secondary_thread); protected: void HandlePrivateEvent(lldb::EventSP &event_sp); Status HaltPrivate(); lldb::StateType WaitForProcessStopPrivate(lldb::EventSP &event_sp, const Timeout &timeout); // This waits for both the state change broadcaster, and the control // broadcaster. If control_only, it only waits for the control broadcaster. bool GetEventsPrivate(lldb::EventSP &event_sp, const Timeout &timeout, bool control_only); lldb::StateType GetStateChangedEventsPrivate(lldb::EventSP &event_sp, const Timeout &timeout); size_t WriteMemoryPrivate(lldb::addr_t addr, const void *buf, size_t size, Status &error); void AppendSTDOUT(const char *s, size_t len); void AppendSTDERR(const char *s, size_t len); void BroadcastAsyncProfileData(const std::string &one_profile_data); static void STDIOReadThreadBytesReceived(void *baton, const void *src, size_t src_len); bool PushProcessIOHandler(); bool PopProcessIOHandler(); bool ProcessIOHandlerIsActive(); bool ProcessIOHandlerExists() const { return static_cast(m_process_input_reader); } Status StopForDestroyOrDetach(lldb::EventSP &exit_event_sp); virtual Status UpdateAutomaticSignalFiltering(); void LoadOperatingSystemPlugin(bool flush); private: /// This is the part of the event handling that for a process event. It /// decides what to do with the event and returns true if the event needs to /// be propagated to the user, and false otherwise. If the event is not /// propagated, this call will most likely set the target to executing /// again. There is only one place where this call should be called, /// HandlePrivateEvent. Don't call it from anywhere else... /// /// \param[in] event_ptr /// This is the event we are handling. /// /// \return /// Returns \b true if the event should be reported to the /// user, \b false otherwise. bool ShouldBroadcastEvent(Event *event_ptr); void ControlPrivateStateThread(uint32_t signal); Process(const Process &) = delete; const Process &operator=(const Process &) = delete; }; /// RAII guard that should be acquired when an utility function is called within /// a given process. class UtilityFunctionScope { Process *m_process; public: UtilityFunctionScope(Process *p) : m_process(p) { if (m_process) m_process->SetRunningUtilityFunction(true); } ~UtilityFunctionScope() { if (m_process) m_process->SetRunningUtilityFunction(false); } }; } // namespace lldb_private #endif // LLDB_TARGET_PROCESS_H diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index ca75e91bd906..b84e9f10fafe 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -1,2661 +1,2661 @@ //===-- SBTarget.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/API/SBTarget.h" #include "SBReproducerPrivate.h" #include "lldb/lldb-public.h" #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBEnvironment.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBExpressionOptions.h" #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBModule.h" #include "lldb/API/SBModuleSpec.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBSourceManager.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" #include "lldb/API/SBStructuredData.h" #include "lldb/API/SBSymbolContextList.h" #include "lldb/Breakpoint/BreakpointID.h" #include "lldb/Breakpoint/BreakpointIDList.h" #include "lldb/Breakpoint/BreakpointList.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Address.h" #include "lldb/Core/AddressResolver.h" #include "lldb/Core/AddressResolverName.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/SearchFilter.h" #include "lldb/Core/Section.h" #include "lldb/Core/StructuredDataImpl.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Core/ValueObjectList.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/Host/Host.h" #include "lldb/Symbol/DeclVendor.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/TypeSystem.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ABI.h" #include "lldb/Target/Language.h" #include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/Process.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" #include "lldb/Target/TargetList.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/ProcessInfo.h" #include "lldb/Utility/RegularExpression.h" #include "Commands/CommandObjectBreakpoint.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Regex.h" using namespace lldb; using namespace lldb_private; #define DEFAULT_DISASM_BYTE_SIZE 32 namespace { Status AttachToProcess(ProcessAttachInfo &attach_info, Target &target) { std::lock_guard guard(target.GetAPIMutex()); auto process_sp = target.GetProcessSP(); if (process_sp) { const auto state = process_sp->GetState(); if (process_sp->IsAlive() && state == eStateConnected) { // If we are already connected, then we have already specified the // listener, so if a valid listener is supplied, we need to error out to // let the client know. if (attach_info.GetListener()) return Status("process is connected and already has a listener, pass " "empty listener"); } } return target.Attach(attach_info, nullptr); } } // namespace // SBTarget constructor SBTarget::SBTarget() : m_opaque_sp() { LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBTarget); } SBTarget::SBTarget(const SBTarget &rhs) : m_opaque_sp(rhs.m_opaque_sp) { LLDB_RECORD_CONSTRUCTOR(SBTarget, (const lldb::SBTarget &), rhs); } SBTarget::SBTarget(const TargetSP &target_sp) : m_opaque_sp(target_sp) { LLDB_RECORD_CONSTRUCTOR(SBTarget, (const lldb::TargetSP &), target_sp); } const SBTarget &SBTarget::operator=(const SBTarget &rhs) { LLDB_RECORD_METHOD(const lldb::SBTarget &, SBTarget, operator=,(const lldb::SBTarget &), rhs); if (this != &rhs) m_opaque_sp = rhs.m_opaque_sp; return LLDB_RECORD_RESULT(*this); } // Destructor SBTarget::~SBTarget() = default; bool SBTarget::EventIsTargetEvent(const SBEvent &event) { LLDB_RECORD_STATIC_METHOD(bool, SBTarget, EventIsTargetEvent, (const lldb::SBEvent &), event); return Target::TargetEventData::GetEventDataFromEvent(event.get()) != nullptr; } SBTarget SBTarget::GetTargetFromEvent(const SBEvent &event) { LLDB_RECORD_STATIC_METHOD(lldb::SBTarget, SBTarget, GetTargetFromEvent, (const lldb::SBEvent &), event); return LLDB_RECORD_RESULT( Target::TargetEventData::GetTargetFromEvent(event.get())); } uint32_t SBTarget::GetNumModulesFromEvent(const SBEvent &event) { LLDB_RECORD_STATIC_METHOD(uint32_t, SBTarget, GetNumModulesFromEvent, (const lldb::SBEvent &), event); const ModuleList module_list = Target::TargetEventData::GetModuleListFromEvent(event.get()); return module_list.GetSize(); } SBModule SBTarget::GetModuleAtIndexFromEvent(const uint32_t idx, const SBEvent &event) { LLDB_RECORD_STATIC_METHOD(lldb::SBModule, SBTarget, GetModuleAtIndexFromEvent, (const uint32_t, const lldb::SBEvent &), idx, event); const ModuleList module_list = Target::TargetEventData::GetModuleListFromEvent(event.get()); return LLDB_RECORD_RESULT(SBModule(module_list.GetModuleAtIndex(idx))); } const char *SBTarget::GetBroadcasterClassName() { LLDB_RECORD_STATIC_METHOD_NO_ARGS(const char *, SBTarget, GetBroadcasterClassName); return Target::GetStaticBroadcasterClass().AsCString(); } bool SBTarget::IsValid() const { LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTarget, IsValid); return this->operator bool(); } SBTarget::operator bool() const { LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBTarget, operator bool); return m_opaque_sp.get() != nullptr && m_opaque_sp->IsValid(); } SBProcess SBTarget::GetProcess() { LLDB_RECORD_METHOD_NO_ARGS(lldb::SBProcess, SBTarget, GetProcess); SBProcess sb_process; ProcessSP process_sp; TargetSP target_sp(GetSP()); if (target_sp) { process_sp = target_sp->GetProcessSP(); sb_process.SetSP(process_sp); } return LLDB_RECORD_RESULT(sb_process); } SBPlatform SBTarget::GetPlatform() { LLDB_RECORD_METHOD_NO_ARGS(lldb::SBPlatform, SBTarget, GetPlatform); TargetSP target_sp(GetSP()); if (!target_sp) return LLDB_RECORD_RESULT(SBPlatform()); SBPlatform platform; platform.m_opaque_sp = target_sp->GetPlatform(); return LLDB_RECORD_RESULT(platform); } SBDebugger SBTarget::GetDebugger() const { LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBDebugger, SBTarget, GetDebugger); SBDebugger debugger; TargetSP target_sp(GetSP()); if (target_sp) debugger.reset(target_sp->GetDebugger().shared_from_this()); return LLDB_RECORD_RESULT(debugger); } SBStructuredData SBTarget::GetStatistics() { LLDB_RECORD_METHOD_NO_ARGS(lldb::SBStructuredData, SBTarget, GetStatistics); SBStructuredData data; TargetSP target_sp(GetSP()); if (!target_sp) return LLDB_RECORD_RESULT(data); auto stats_up = std::make_unique(); int i = 0; for (auto &Entry : target_sp->GetStatistics()) { std::string Desc = lldb_private::GetStatDescription( static_cast(i)); stats_up->AddIntegerItem(Desc, Entry); i += 1; } data.m_impl_up->SetObjectSP(std::move(stats_up)); return LLDB_RECORD_RESULT(data); } void SBTarget::SetCollectingStats(bool v) { LLDB_RECORD_METHOD(void, SBTarget, SetCollectingStats, (bool), v); TargetSP target_sp(GetSP()); if (!target_sp) return; return target_sp->SetCollectingStats(v); } bool SBTarget::GetCollectingStats() { LLDB_RECORD_METHOD_NO_ARGS(bool, SBTarget, GetCollectingStats); TargetSP target_sp(GetSP()); if (!target_sp) return false; return target_sp->GetCollectingStats(); } SBProcess SBTarget::LoadCore(const char *core_file) { LLDB_RECORD_METHOD(lldb::SBProcess, SBTarget, LoadCore, (const char *), core_file); lldb::SBError error; // Ignored return LLDB_RECORD_RESULT(LoadCore(core_file, error)); } SBProcess SBTarget::LoadCore(const char *core_file, lldb::SBError &error) { LLDB_RECORD_METHOD(lldb::SBProcess, SBTarget, LoadCore, (const char *, lldb::SBError &), core_file, error); SBProcess sb_process; TargetSP target_sp(GetSP()); if (target_sp) { FileSpec filespec(core_file); FileSystem::Instance().Resolve(filespec); ProcessSP process_sp(target_sp->CreateProcess( target_sp->GetDebugger().GetListener(), "", &filespec)); if (process_sp) { error.SetError(process_sp->LoadCore()); if (error.Success()) sb_process.SetSP(process_sp); } else { error.SetErrorString("Failed to create the process"); } } else { error.SetErrorString("SBTarget is invalid"); } return LLDB_RECORD_RESULT(sb_process); } SBProcess SBTarget::LaunchSimple(char const **argv, char const **envp, const char *working_directory) { LLDB_RECORD_METHOD(lldb::SBProcess, SBTarget, LaunchSimple, (const char **, const char **, const char *), argv, envp, working_directory); char *stdin_path = nullptr; char *stdout_path = nullptr; char *stderr_path = nullptr; uint32_t launch_flags = 0; bool stop_at_entry = false; SBError error; SBListener listener = GetDebugger().GetListener(); return LLDB_RECORD_RESULT(Launch(listener, argv, envp, stdin_path, stdout_path, stderr_path, working_directory, launch_flags, stop_at_entry, error)); } SBError SBTarget::Install() { LLDB_RECORD_METHOD_NO_ARGS(lldb::SBError, SBTarget, Install); SBError sb_error; TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); sb_error.ref() = target_sp->Install(nullptr); } return LLDB_RECORD_RESULT(sb_error); } SBProcess SBTarget::Launch(SBListener &listener, char const **argv, char const **envp, const char *stdin_path, const char *stdout_path, const char *stderr_path, const char *working_directory, uint32_t launch_flags, // See LaunchFlags bool stop_at_entry, lldb::SBError &error) { LLDB_RECORD_METHOD(lldb::SBProcess, SBTarget, Launch, (lldb::SBListener &, const char **, const char **, const char *, const char *, const char *, const char *, uint32_t, bool, lldb::SBError &), listener, argv, envp, stdin_path, stdout_path, stderr_path, working_directory, launch_flags, stop_at_entry, error); SBProcess sb_process; ProcessSP process_sp; TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); if (stop_at_entry) launch_flags |= eLaunchFlagStopAtEntry; if (getenv("LLDB_LAUNCH_FLAG_DISABLE_ASLR")) launch_flags |= eLaunchFlagDisableASLR; StateType state = eStateInvalid; process_sp = target_sp->GetProcessSP(); if (process_sp) { state = process_sp->GetState(); if (process_sp->IsAlive() && state != eStateConnected) { if (state == eStateAttaching) error.SetErrorString("process attach is in progress"); else error.SetErrorString("a process is already being debugged"); return LLDB_RECORD_RESULT(sb_process); } } if (state == eStateConnected) { // If we are already connected, then we have already specified the // listener, so if a valid listener is supplied, we need to error out to // let the client know. if (listener.IsValid()) { error.SetErrorString("process is connected and already has a listener, " "pass empty listener"); return LLDB_RECORD_RESULT(sb_process); } } if (getenv("LLDB_LAUNCH_FLAG_DISABLE_STDIO")) launch_flags |= eLaunchFlagDisableSTDIO; ProcessLaunchInfo launch_info(FileSpec(stdin_path), FileSpec(stdout_path), FileSpec(stderr_path), FileSpec(working_directory), launch_flags); Module *exe_module = target_sp->GetExecutableModulePointer(); if (exe_module) launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); if (argv) { launch_info.GetArguments().AppendArguments(argv); } else { auto default_launch_info = target_sp->GetProcessLaunchInfo(); launch_info.GetArguments().AppendArguments( default_launch_info.GetArguments()); } if (envp) { launch_info.GetEnvironment() = Environment(envp); } else { auto default_launch_info = target_sp->GetProcessLaunchInfo(); launch_info.GetEnvironment() = default_launch_info.GetEnvironment(); } if (listener.IsValid()) launch_info.SetListener(listener.GetSP()); error.SetError(target_sp->Launch(launch_info, nullptr)); sb_process.SetSP(target_sp->GetProcessSP()); } else { error.SetErrorString("SBTarget is invalid"); } return LLDB_RECORD_RESULT(sb_process); } SBProcess SBTarget::Launch(SBLaunchInfo &sb_launch_info, SBError &error) { LLDB_RECORD_METHOD(lldb::SBProcess, SBTarget, Launch, (lldb::SBLaunchInfo &, lldb::SBError &), sb_launch_info, error); SBProcess sb_process; TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); StateType state = eStateInvalid; { ProcessSP process_sp = target_sp->GetProcessSP(); if (process_sp) { state = process_sp->GetState(); if (process_sp->IsAlive() && state != eStateConnected) { if (state == eStateAttaching) error.SetErrorString("process attach is in progress"); else error.SetErrorString("a process is already being debugged"); return LLDB_RECORD_RESULT(sb_process); } } } lldb_private::ProcessLaunchInfo launch_info = sb_launch_info.ref(); if (!launch_info.GetExecutableFile()) { Module *exe_module = target_sp->GetExecutableModulePointer(); if (exe_module) launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); } const ArchSpec &arch_spec = target_sp->GetArchitecture(); if (arch_spec.IsValid()) launch_info.GetArchitecture() = arch_spec; error.SetError(target_sp->Launch(launch_info, nullptr)); sb_launch_info.set_ref(launch_info); sb_process.SetSP(target_sp->GetProcessSP()); } else { error.SetErrorString("SBTarget is invalid"); } return LLDB_RECORD_RESULT(sb_process); } lldb::SBProcess SBTarget::Attach(SBAttachInfo &sb_attach_info, SBError &error) { LLDB_RECORD_METHOD(lldb::SBProcess, SBTarget, Attach, (lldb::SBAttachInfo &, lldb::SBError &), sb_attach_info, error); SBProcess sb_process; TargetSP target_sp(GetSP()); if (target_sp) { ProcessAttachInfo &attach_info = sb_attach_info.ref(); if (attach_info.ProcessIDIsValid() && !attach_info.UserIDIsValid()) { PlatformSP platform_sp = target_sp->GetPlatform(); // See if we can pre-verify if a process exists or not if (platform_sp && platform_sp->IsConnected()) { lldb::pid_t attach_pid = attach_info.GetProcessID(); ProcessInstanceInfo instance_info; if (platform_sp->GetProcessInfo(attach_pid, instance_info)) { attach_info.SetUserID(instance_info.GetEffectiveUserID()); } else { error.ref().SetErrorStringWithFormat( "no process found with process ID %" PRIu64, attach_pid); return LLDB_RECORD_RESULT(sb_process); } } } error.SetError(AttachToProcess(attach_info, *target_sp)); if (error.Success()) sb_process.SetSP(target_sp->GetProcessSP()); } else { error.SetErrorString("SBTarget is invalid"); } return LLDB_RECORD_RESULT(sb_process); } lldb::SBProcess SBTarget::AttachToProcessWithID( SBListener &listener, lldb::pid_t pid, // The process ID to attach to SBError &error // An error explaining what went wrong if attach fails ) { LLDB_RECORD_METHOD(lldb::SBProcess, SBTarget, AttachToProcessWithID, (lldb::SBListener &, lldb::pid_t, lldb::SBError &), listener, pid, error); SBProcess sb_process; TargetSP target_sp(GetSP()); if (target_sp) { ProcessAttachInfo attach_info; attach_info.SetProcessID(pid); if (listener.IsValid()) attach_info.SetListener(listener.GetSP()); ProcessInstanceInfo instance_info; if (target_sp->GetPlatform()->GetProcessInfo(pid, instance_info)) attach_info.SetUserID(instance_info.GetEffectiveUserID()); error.SetError(AttachToProcess(attach_info, *target_sp)); if (error.Success()) sb_process.SetSP(target_sp->GetProcessSP()); } else error.SetErrorString("SBTarget is invalid"); return LLDB_RECORD_RESULT(sb_process); } lldb::SBProcess SBTarget::AttachToProcessWithName( SBListener &listener, const char *name, // basename of process to attach to bool wait_for, // if true wait for a new instance of "name" to be launched SBError &error // An error explaining what went wrong if attach fails ) { LLDB_RECORD_METHOD(lldb::SBProcess, SBTarget, AttachToProcessWithName, (lldb::SBListener &, const char *, bool, lldb::SBError &), listener, name, wait_for, error); SBProcess sb_process; TargetSP target_sp(GetSP()); if (name && target_sp) { ProcessAttachInfo attach_info; attach_info.GetExecutableFile().SetFile(name, FileSpec::Style::native); attach_info.SetWaitForLaunch(wait_for); if (listener.IsValid()) attach_info.SetListener(listener.GetSP()); error.SetError(AttachToProcess(attach_info, *target_sp)); if (error.Success()) sb_process.SetSP(target_sp->GetProcessSP()); } else error.SetErrorString("SBTarget is invalid"); return LLDB_RECORD_RESULT(sb_process); } lldb::SBProcess SBTarget::ConnectRemote(SBListener &listener, const char *url, const char *plugin_name, SBError &error) { LLDB_RECORD_METHOD( lldb::SBProcess, SBTarget, ConnectRemote, (lldb::SBListener &, const char *, const char *, lldb::SBError &), listener, url, plugin_name, error); SBProcess sb_process; ProcessSP process_sp; TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); if (listener.IsValid()) process_sp = target_sp->CreateProcess(listener.m_opaque_sp, plugin_name, nullptr); else process_sp = target_sp->CreateProcess( target_sp->GetDebugger().GetListener(), plugin_name, nullptr); if (process_sp) { sb_process.SetSP(process_sp); - error.SetError(process_sp->ConnectRemote(nullptr, url)); + error.SetError(process_sp->ConnectRemote(url)); } else { error.SetErrorString("unable to create lldb_private::Process"); } } else { error.SetErrorString("SBTarget is invalid"); } return LLDB_RECORD_RESULT(sb_process); } SBFileSpec SBTarget::GetExecutable() { LLDB_RECORD_METHOD_NO_ARGS(lldb::SBFileSpec, SBTarget, GetExecutable); SBFileSpec exe_file_spec; TargetSP target_sp(GetSP()); if (target_sp) { Module *exe_module = target_sp->GetExecutableModulePointer(); if (exe_module) exe_file_spec.SetFileSpec(exe_module->GetFileSpec()); } return LLDB_RECORD_RESULT(exe_file_spec); } bool SBTarget::operator==(const SBTarget &rhs) const { LLDB_RECORD_METHOD_CONST(bool, SBTarget, operator==,(const lldb::SBTarget &), rhs); return m_opaque_sp.get() == rhs.m_opaque_sp.get(); } bool SBTarget::operator!=(const SBTarget &rhs) const { LLDB_RECORD_METHOD_CONST(bool, SBTarget, operator!=,(const lldb::SBTarget &), rhs); return m_opaque_sp.get() != rhs.m_opaque_sp.get(); } lldb::TargetSP SBTarget::GetSP() const { return m_opaque_sp; } void SBTarget::SetSP(const lldb::TargetSP &target_sp) { m_opaque_sp = target_sp; } lldb::SBAddress SBTarget::ResolveLoadAddress(lldb::addr_t vm_addr) { LLDB_RECORD_METHOD(lldb::SBAddress, SBTarget, ResolveLoadAddress, (lldb::addr_t), vm_addr); lldb::SBAddress sb_addr; Address &addr = sb_addr.ref(); TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); if (target_sp->ResolveLoadAddress(vm_addr, addr)) return LLDB_RECORD_RESULT(sb_addr); } // We have a load address that isn't in a section, just return an address // with the offset filled in (the address) and the section set to NULL addr.SetRawAddress(vm_addr); return LLDB_RECORD_RESULT(sb_addr); } lldb::SBAddress SBTarget::ResolveFileAddress(lldb::addr_t file_addr) { LLDB_RECORD_METHOD(lldb::SBAddress, SBTarget, ResolveFileAddress, (lldb::addr_t), file_addr); lldb::SBAddress sb_addr; Address &addr = sb_addr.ref(); TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); if (target_sp->ResolveFileAddress(file_addr, addr)) return LLDB_RECORD_RESULT(sb_addr); } addr.SetRawAddress(file_addr); return LLDB_RECORD_RESULT(sb_addr); } lldb::SBAddress SBTarget::ResolvePastLoadAddress(uint32_t stop_id, lldb::addr_t vm_addr) { LLDB_RECORD_METHOD(lldb::SBAddress, SBTarget, ResolvePastLoadAddress, (uint32_t, lldb::addr_t), stop_id, vm_addr); lldb::SBAddress sb_addr; Address &addr = sb_addr.ref(); TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); if (target_sp->ResolveLoadAddress(vm_addr, addr)) return LLDB_RECORD_RESULT(sb_addr); } // We have a load address that isn't in a section, just return an address // with the offset filled in (the address) and the section set to NULL addr.SetRawAddress(vm_addr); return LLDB_RECORD_RESULT(sb_addr); } SBSymbolContext SBTarget::ResolveSymbolContextForAddress(const SBAddress &addr, uint32_t resolve_scope) { LLDB_RECORD_METHOD(lldb::SBSymbolContext, SBTarget, ResolveSymbolContextForAddress, (const lldb::SBAddress &, uint32_t), addr, resolve_scope); SBSymbolContext sc; SymbolContextItem scope = static_cast(resolve_scope); if (addr.IsValid()) { TargetSP target_sp(GetSP()); if (target_sp) target_sp->GetImages().ResolveSymbolContextForAddress(addr.ref(), scope, sc.ref()); } return LLDB_RECORD_RESULT(sc); } size_t SBTarget::ReadMemory(const SBAddress addr, void *buf, size_t size, lldb::SBError &error) { LLDB_RECORD_METHOD(size_t, SBTarget, ReadMemory, (const lldb::SBAddress, void *, size_t, lldb::SBError &), addr, buf, size, error); SBError sb_error; size_t bytes_read = 0; TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); bytes_read = target_sp->ReadMemory(addr.ref(), false, buf, size, sb_error.ref()); } else { sb_error.SetErrorString("invalid target"); } return bytes_read; } SBBreakpoint SBTarget::BreakpointCreateByLocation(const char *file, uint32_t line) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByLocation, (const char *, uint32_t), file, line); return LLDB_RECORD_RESULT( SBBreakpoint(BreakpointCreateByLocation(SBFileSpec(file, false), line))); } SBBreakpoint SBTarget::BreakpointCreateByLocation(const SBFileSpec &sb_file_spec, uint32_t line) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByLocation, (const lldb::SBFileSpec &, uint32_t), sb_file_spec, line); return LLDB_RECORD_RESULT(BreakpointCreateByLocation(sb_file_spec, line, 0)); } SBBreakpoint SBTarget::BreakpointCreateByLocation(const SBFileSpec &sb_file_spec, uint32_t line, lldb::addr_t offset) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByLocation, (const lldb::SBFileSpec &, uint32_t, lldb::addr_t), sb_file_spec, line, offset); SBFileSpecList empty_list; return LLDB_RECORD_RESULT( BreakpointCreateByLocation(sb_file_spec, line, offset, empty_list)); } SBBreakpoint SBTarget::BreakpointCreateByLocation(const SBFileSpec &sb_file_spec, uint32_t line, lldb::addr_t offset, SBFileSpecList &sb_module_list) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByLocation, (const lldb::SBFileSpec &, uint32_t, lldb::addr_t, lldb::SBFileSpecList &), sb_file_spec, line, offset, sb_module_list); return LLDB_RECORD_RESULT(BreakpointCreateByLocation(sb_file_spec, line, 0, offset, sb_module_list)); } SBBreakpoint SBTarget::BreakpointCreateByLocation( const SBFileSpec &sb_file_spec, uint32_t line, uint32_t column, lldb::addr_t offset, SBFileSpecList &sb_module_list) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByLocation, (const lldb::SBFileSpec &, uint32_t, uint32_t, lldb::addr_t, lldb::SBFileSpecList &), sb_file_spec, line, column, offset, sb_module_list); SBBreakpoint sb_bp; TargetSP target_sp(GetSP()); if (target_sp && line != 0) { std::lock_guard guard(target_sp->GetAPIMutex()); const LazyBool check_inlines = eLazyBoolCalculate; const LazyBool skip_prologue = eLazyBoolCalculate; const bool internal = false; const bool hardware = false; const LazyBool move_to_nearest_code = eLazyBoolCalculate; const FileSpecList *module_list = nullptr; if (sb_module_list.GetSize() > 0) { module_list = sb_module_list.get(); } sb_bp = target_sp->CreateBreakpoint( module_list, *sb_file_spec, line, column, offset, check_inlines, skip_prologue, internal, hardware, move_to_nearest_code); } return LLDB_RECORD_RESULT(sb_bp); } SBBreakpoint SBTarget::BreakpointCreateByName(const char *symbol_name, const char *module_name) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByName, (const char *, const char *), symbol_name, module_name); SBBreakpoint sb_bp; TargetSP target_sp(GetSP()); if (target_sp.get()) { std::lock_guard guard(target_sp->GetAPIMutex()); const bool internal = false; const bool hardware = false; const LazyBool skip_prologue = eLazyBoolCalculate; const lldb::addr_t offset = 0; if (module_name && module_name[0]) { FileSpecList module_spec_list; module_spec_list.Append(FileSpec(module_name)); sb_bp = target_sp->CreateBreakpoint( &module_spec_list, nullptr, symbol_name, eFunctionNameTypeAuto, eLanguageTypeUnknown, offset, skip_prologue, internal, hardware); } else { sb_bp = target_sp->CreateBreakpoint( nullptr, nullptr, symbol_name, eFunctionNameTypeAuto, eLanguageTypeUnknown, offset, skip_prologue, internal, hardware); } } return LLDB_RECORD_RESULT(sb_bp); } lldb::SBBreakpoint SBTarget::BreakpointCreateByName(const char *symbol_name, const SBFileSpecList &module_list, const SBFileSpecList &comp_unit_list) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByName, (const char *, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &), symbol_name, module_list, comp_unit_list); lldb::FunctionNameType name_type_mask = eFunctionNameTypeAuto; return LLDB_RECORD_RESULT( BreakpointCreateByName(symbol_name, name_type_mask, eLanguageTypeUnknown, module_list, comp_unit_list)); } lldb::SBBreakpoint SBTarget::BreakpointCreateByName( const char *symbol_name, uint32_t name_type_mask, const SBFileSpecList &module_list, const SBFileSpecList &comp_unit_list) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByName, (const char *, uint32_t, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &), symbol_name, name_type_mask, module_list, comp_unit_list); return LLDB_RECORD_RESULT( BreakpointCreateByName(symbol_name, name_type_mask, eLanguageTypeUnknown, module_list, comp_unit_list)); } lldb::SBBreakpoint SBTarget::BreakpointCreateByName( const char *symbol_name, uint32_t name_type_mask, LanguageType symbol_language, const SBFileSpecList &module_list, const SBFileSpecList &comp_unit_list) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByName, (const char *, uint32_t, lldb::LanguageType, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &), symbol_name, name_type_mask, symbol_language, module_list, comp_unit_list); SBBreakpoint sb_bp; TargetSP target_sp(GetSP()); if (target_sp && symbol_name && symbol_name[0]) { const bool internal = false; const bool hardware = false; const LazyBool skip_prologue = eLazyBoolCalculate; std::lock_guard guard(target_sp->GetAPIMutex()); FunctionNameType mask = static_cast(name_type_mask); sb_bp = target_sp->CreateBreakpoint(module_list.get(), comp_unit_list.get(), symbol_name, mask, symbol_language, 0, skip_prologue, internal, hardware); } return LLDB_RECORD_RESULT(sb_bp); } lldb::SBBreakpoint SBTarget::BreakpointCreateByNames( const char *symbol_names[], uint32_t num_names, uint32_t name_type_mask, const SBFileSpecList &module_list, const SBFileSpecList &comp_unit_list) { LLDB_RECORD_METHOD( lldb::SBBreakpoint, SBTarget, BreakpointCreateByNames, (const char **, uint32_t, uint32_t, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &), symbol_names, num_names, name_type_mask, module_list, comp_unit_list); return LLDB_RECORD_RESULT(BreakpointCreateByNames( symbol_names, num_names, name_type_mask, eLanguageTypeUnknown, module_list, comp_unit_list)); } lldb::SBBreakpoint SBTarget::BreakpointCreateByNames( const char *symbol_names[], uint32_t num_names, uint32_t name_type_mask, LanguageType symbol_language, const SBFileSpecList &module_list, const SBFileSpecList &comp_unit_list) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByNames, (const char **, uint32_t, uint32_t, lldb::LanguageType, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &), symbol_names, num_names, name_type_mask, symbol_language, module_list, comp_unit_list); return LLDB_RECORD_RESULT(BreakpointCreateByNames( symbol_names, num_names, name_type_mask, eLanguageTypeUnknown, 0, module_list, comp_unit_list)); } lldb::SBBreakpoint SBTarget::BreakpointCreateByNames( const char *symbol_names[], uint32_t num_names, uint32_t name_type_mask, LanguageType symbol_language, lldb::addr_t offset, const SBFileSpecList &module_list, const SBFileSpecList &comp_unit_list) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByNames, (const char **, uint32_t, uint32_t, lldb::LanguageType, lldb::addr_t, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &), symbol_names, num_names, name_type_mask, symbol_language, offset, module_list, comp_unit_list); SBBreakpoint sb_bp; TargetSP target_sp(GetSP()); if (target_sp && num_names > 0) { std::lock_guard guard(target_sp->GetAPIMutex()); const bool internal = false; const bool hardware = false; FunctionNameType mask = static_cast(name_type_mask); const LazyBool skip_prologue = eLazyBoolCalculate; sb_bp = target_sp->CreateBreakpoint( module_list.get(), comp_unit_list.get(), symbol_names, num_names, mask, symbol_language, offset, skip_prologue, internal, hardware); } return LLDB_RECORD_RESULT(sb_bp); } SBBreakpoint SBTarget::BreakpointCreateByRegex(const char *symbol_name_regex, const char *module_name) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByRegex, (const char *, const char *), symbol_name_regex, module_name); SBFileSpecList module_spec_list; SBFileSpecList comp_unit_list; if (module_name && module_name[0]) { module_spec_list.Append(FileSpec(module_name)); } return LLDB_RECORD_RESULT( BreakpointCreateByRegex(symbol_name_regex, eLanguageTypeUnknown, module_spec_list, comp_unit_list)); } lldb::SBBreakpoint SBTarget::BreakpointCreateByRegex(const char *symbol_name_regex, const SBFileSpecList &module_list, const SBFileSpecList &comp_unit_list) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByRegex, (const char *, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &), symbol_name_regex, module_list, comp_unit_list); return LLDB_RECORD_RESULT(BreakpointCreateByRegex( symbol_name_regex, eLanguageTypeUnknown, module_list, comp_unit_list)); } lldb::SBBreakpoint SBTarget::BreakpointCreateByRegex( const char *symbol_name_regex, LanguageType symbol_language, const SBFileSpecList &module_list, const SBFileSpecList &comp_unit_list) { LLDB_RECORD_METHOD( lldb::SBBreakpoint, SBTarget, BreakpointCreateByRegex, (const char *, lldb::LanguageType, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &), symbol_name_regex, symbol_language, module_list, comp_unit_list); SBBreakpoint sb_bp; TargetSP target_sp(GetSP()); if (target_sp && symbol_name_regex && symbol_name_regex[0]) { std::lock_guard guard(target_sp->GetAPIMutex()); RegularExpression regexp((llvm::StringRef(symbol_name_regex))); const bool internal = false; const bool hardware = false; const LazyBool skip_prologue = eLazyBoolCalculate; sb_bp = target_sp->CreateFuncRegexBreakpoint( module_list.get(), comp_unit_list.get(), std::move(regexp), symbol_language, skip_prologue, internal, hardware); } return LLDB_RECORD_RESULT(sb_bp); } SBBreakpoint SBTarget::BreakpointCreateByAddress(addr_t address) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByAddress, (lldb::addr_t), address); SBBreakpoint sb_bp; TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); const bool hardware = false; sb_bp = target_sp->CreateBreakpoint(address, false, hardware); } return LLDB_RECORD_RESULT(sb_bp); } SBBreakpoint SBTarget::BreakpointCreateBySBAddress(SBAddress &sb_address) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateBySBAddress, (lldb::SBAddress &), sb_address); SBBreakpoint sb_bp; TargetSP target_sp(GetSP()); if (!sb_address.IsValid()) { return LLDB_RECORD_RESULT(sb_bp); } if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); const bool hardware = false; sb_bp = target_sp->CreateBreakpoint(sb_address.ref(), false, hardware); } return LLDB_RECORD_RESULT(sb_bp); } lldb::SBBreakpoint SBTarget::BreakpointCreateBySourceRegex(const char *source_regex, const lldb::SBFileSpec &source_file, const char *module_name) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateBySourceRegex, (const char *, const lldb::SBFileSpec &, const char *), source_regex, source_file, module_name); SBFileSpecList module_spec_list; if (module_name && module_name[0]) { module_spec_list.Append(FileSpec(module_name)); } SBFileSpecList source_file_list; if (source_file.IsValid()) { source_file_list.Append(source_file); } return LLDB_RECORD_RESULT(BreakpointCreateBySourceRegex( source_regex, module_spec_list, source_file_list)); } lldb::SBBreakpoint SBTarget::BreakpointCreateBySourceRegex( const char *source_regex, const SBFileSpecList &module_list, const lldb::SBFileSpecList &source_file_list) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateBySourceRegex, (const char *, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &), source_regex, module_list, source_file_list); return LLDB_RECORD_RESULT(BreakpointCreateBySourceRegex( source_regex, module_list, source_file_list, SBStringList())); } lldb::SBBreakpoint SBTarget::BreakpointCreateBySourceRegex( const char *source_regex, const SBFileSpecList &module_list, const lldb::SBFileSpecList &source_file_list, const SBStringList &func_names) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateBySourceRegex, (const char *, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &, const lldb::SBStringList &), source_regex, module_list, source_file_list, func_names); SBBreakpoint sb_bp; TargetSP target_sp(GetSP()); if (target_sp && source_regex && source_regex[0]) { std::lock_guard guard(target_sp->GetAPIMutex()); const bool hardware = false; const LazyBool move_to_nearest_code = eLazyBoolCalculate; RegularExpression regexp((llvm::StringRef(source_regex))); std::unordered_set func_names_set; for (size_t i = 0; i < func_names.GetSize(); i++) { func_names_set.insert(func_names.GetStringAtIndex(i)); } sb_bp = target_sp->CreateSourceRegexBreakpoint( module_list.get(), source_file_list.get(), func_names_set, std::move(regexp), false, hardware, move_to_nearest_code); } return LLDB_RECORD_RESULT(sb_bp); } lldb::SBBreakpoint SBTarget::BreakpointCreateForException(lldb::LanguageType language, bool catch_bp, bool throw_bp) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateForException, (lldb::LanguageType, bool, bool), language, catch_bp, throw_bp); SBBreakpoint sb_bp; TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); const bool hardware = false; sb_bp = target_sp->CreateExceptionBreakpoint(language, catch_bp, throw_bp, hardware); } return LLDB_RECORD_RESULT(sb_bp); } lldb::SBBreakpoint SBTarget::BreakpointCreateFromScript( const char *class_name, SBStructuredData &extra_args, const SBFileSpecList &module_list, const SBFileSpecList &file_list, bool request_hardware) { LLDB_RECORD_METHOD( lldb::SBBreakpoint, SBTarget, BreakpointCreateFromScript, (const char *, lldb::SBStructuredData &, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &, bool), class_name, extra_args, module_list, file_list, request_hardware); SBBreakpoint sb_bp; TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); Status error; StructuredData::ObjectSP obj_sp = extra_args.m_impl_up->GetObjectSP(); sb_bp = target_sp->CreateScriptedBreakpoint(class_name, module_list.get(), file_list.get(), false, /* internal */ request_hardware, obj_sp, &error); } return LLDB_RECORD_RESULT(sb_bp); } uint32_t SBTarget::GetNumBreakpoints() const { LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBTarget, GetNumBreakpoints); TargetSP target_sp(GetSP()); if (target_sp) { // The breakpoint list is thread safe, no need to lock return target_sp->GetBreakpointList().GetSize(); } return 0; } SBBreakpoint SBTarget::GetBreakpointAtIndex(uint32_t idx) const { LLDB_RECORD_METHOD_CONST(lldb::SBBreakpoint, SBTarget, GetBreakpointAtIndex, (uint32_t), idx); SBBreakpoint sb_breakpoint; TargetSP target_sp(GetSP()); if (target_sp) { // The breakpoint list is thread safe, no need to lock sb_breakpoint = target_sp->GetBreakpointList().GetBreakpointAtIndex(idx); } return LLDB_RECORD_RESULT(sb_breakpoint); } bool SBTarget::BreakpointDelete(break_id_t bp_id) { LLDB_RECORD_METHOD(bool, SBTarget, BreakpointDelete, (lldb::break_id_t), bp_id); bool result = false; TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); result = target_sp->RemoveBreakpointByID(bp_id); } return result; } SBBreakpoint SBTarget::FindBreakpointByID(break_id_t bp_id) { LLDB_RECORD_METHOD(lldb::SBBreakpoint, SBTarget, FindBreakpointByID, (lldb::break_id_t), bp_id); SBBreakpoint sb_breakpoint; TargetSP target_sp(GetSP()); if (target_sp && bp_id != LLDB_INVALID_BREAK_ID) { std::lock_guard guard(target_sp->GetAPIMutex()); sb_breakpoint = target_sp->GetBreakpointByID(bp_id); } return LLDB_RECORD_RESULT(sb_breakpoint); } bool SBTarget::FindBreakpointsByName(const char *name, SBBreakpointList &bkpts) { LLDB_RECORD_METHOD(bool, SBTarget, FindBreakpointsByName, (const char *, lldb::SBBreakpointList &), name, bkpts); TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); llvm::Expected> expected_vector = target_sp->GetBreakpointList().FindBreakpointsByName(name); if (!expected_vector) { LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS), "invalid breakpoint name: {}", llvm::toString(expected_vector.takeError())); return false; } for (BreakpointSP bkpt_sp : *expected_vector) { bkpts.AppendByID(bkpt_sp->GetID()); } } return true; } void SBTarget::GetBreakpointNames(SBStringList &names) { LLDB_RECORD_METHOD(void, SBTarget, GetBreakpointNames, (lldb::SBStringList &), names); names.Clear(); TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); std::vector name_vec; target_sp->GetBreakpointNames(name_vec); for (auto name : name_vec) names.AppendString(name.c_str()); } } void SBTarget::DeleteBreakpointName(const char *name) { LLDB_RECORD_METHOD(void, SBTarget, DeleteBreakpointName, (const char *), name); TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); target_sp->DeleteBreakpointName(ConstString(name)); } } bool SBTarget::EnableAllBreakpoints() { LLDB_RECORD_METHOD_NO_ARGS(bool, SBTarget, EnableAllBreakpoints); TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); target_sp->EnableAllowedBreakpoints(); return true; } return false; } bool SBTarget::DisableAllBreakpoints() { LLDB_RECORD_METHOD_NO_ARGS(bool, SBTarget, DisableAllBreakpoints); TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); target_sp->DisableAllowedBreakpoints(); return true; } return false; } bool SBTarget::DeleteAllBreakpoints() { LLDB_RECORD_METHOD_NO_ARGS(bool, SBTarget, DeleteAllBreakpoints); TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); target_sp->RemoveAllowedBreakpoints(); return true; } return false; } lldb::SBError SBTarget::BreakpointsCreateFromFile(SBFileSpec &source_file, SBBreakpointList &new_bps) { LLDB_RECORD_METHOD(lldb::SBError, SBTarget, BreakpointsCreateFromFile, (lldb::SBFileSpec &, lldb::SBBreakpointList &), source_file, new_bps); SBStringList empty_name_list; return LLDB_RECORD_RESULT( BreakpointsCreateFromFile(source_file, empty_name_list, new_bps)); } lldb::SBError SBTarget::BreakpointsCreateFromFile(SBFileSpec &source_file, SBStringList &matching_names, SBBreakpointList &new_bps) { LLDB_RECORD_METHOD( lldb::SBError, SBTarget, BreakpointsCreateFromFile, (lldb::SBFileSpec &, lldb::SBStringList &, lldb::SBBreakpointList &), source_file, matching_names, new_bps); SBError sberr; TargetSP target_sp(GetSP()); if (!target_sp) { sberr.SetErrorString( "BreakpointCreateFromFile called with invalid target."); return LLDB_RECORD_RESULT(sberr); } std::lock_guard guard(target_sp->GetAPIMutex()); BreakpointIDList bp_ids; std::vector name_vector; size_t num_names = matching_names.GetSize(); for (size_t i = 0; i < num_names; i++) name_vector.push_back(matching_names.GetStringAtIndex(i)); sberr.ref() = target_sp->CreateBreakpointsFromFile(source_file.ref(), name_vector, bp_ids); if (sberr.Fail()) return LLDB_RECORD_RESULT(sberr); size_t num_bkpts = bp_ids.GetSize(); for (size_t i = 0; i < num_bkpts; i++) { BreakpointID bp_id = bp_ids.GetBreakpointIDAtIndex(i); new_bps.AppendByID(bp_id.GetBreakpointID()); } return LLDB_RECORD_RESULT(sberr); } lldb::SBError SBTarget::BreakpointsWriteToFile(SBFileSpec &dest_file) { LLDB_RECORD_METHOD(lldb::SBError, SBTarget, BreakpointsWriteToFile, (lldb::SBFileSpec &), dest_file); SBError sberr; TargetSP target_sp(GetSP()); if (!target_sp) { sberr.SetErrorString("BreakpointWriteToFile called with invalid target."); return LLDB_RECORD_RESULT(sberr); } SBBreakpointList bkpt_list(*this); return LLDB_RECORD_RESULT(BreakpointsWriteToFile(dest_file, bkpt_list)); } lldb::SBError SBTarget::BreakpointsWriteToFile(SBFileSpec &dest_file, SBBreakpointList &bkpt_list, bool append) { LLDB_RECORD_METHOD(lldb::SBError, SBTarget, BreakpointsWriteToFile, (lldb::SBFileSpec &, lldb::SBBreakpointList &, bool), dest_file, bkpt_list, append); SBError sberr; TargetSP target_sp(GetSP()); if (!target_sp) { sberr.SetErrorString("BreakpointWriteToFile called with invalid target."); return LLDB_RECORD_RESULT(sberr); } std::lock_guard guard(target_sp->GetAPIMutex()); BreakpointIDList bp_id_list; bkpt_list.CopyToBreakpointIDList(bp_id_list); sberr.ref() = target_sp->SerializeBreakpointsToFile(dest_file.ref(), bp_id_list, append); return LLDB_RECORD_RESULT(sberr); } uint32_t SBTarget::GetNumWatchpoints() const { LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBTarget, GetNumWatchpoints); TargetSP target_sp(GetSP()); if (target_sp) { // The watchpoint list is thread safe, no need to lock return target_sp->GetWatchpointList().GetSize(); } return 0; } SBWatchpoint SBTarget::GetWatchpointAtIndex(uint32_t idx) const { LLDB_RECORD_METHOD_CONST(lldb::SBWatchpoint, SBTarget, GetWatchpointAtIndex, (uint32_t), idx); SBWatchpoint sb_watchpoint; TargetSP target_sp(GetSP()); if (target_sp) { // The watchpoint list is thread safe, no need to lock sb_watchpoint.SetSP(target_sp->GetWatchpointList().GetByIndex(idx)); } return LLDB_RECORD_RESULT(sb_watchpoint); } bool SBTarget::DeleteWatchpoint(watch_id_t wp_id) { LLDB_RECORD_METHOD(bool, SBTarget, DeleteWatchpoint, (lldb::watch_id_t), wp_id); bool result = false; TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); std::unique_lock lock; target_sp->GetWatchpointList().GetListMutex(lock); result = target_sp->RemoveWatchpointByID(wp_id); } return result; } SBWatchpoint SBTarget::FindWatchpointByID(lldb::watch_id_t wp_id) { LLDB_RECORD_METHOD(lldb::SBWatchpoint, SBTarget, FindWatchpointByID, (lldb::watch_id_t), wp_id); SBWatchpoint sb_watchpoint; lldb::WatchpointSP watchpoint_sp; TargetSP target_sp(GetSP()); if (target_sp && wp_id != LLDB_INVALID_WATCH_ID) { std::lock_guard guard(target_sp->GetAPIMutex()); std::unique_lock lock; target_sp->GetWatchpointList().GetListMutex(lock); watchpoint_sp = target_sp->GetWatchpointList().FindByID(wp_id); sb_watchpoint.SetSP(watchpoint_sp); } return LLDB_RECORD_RESULT(sb_watchpoint); } lldb::SBWatchpoint SBTarget::WatchAddress(lldb::addr_t addr, size_t size, bool read, bool write, SBError &error) { LLDB_RECORD_METHOD(lldb::SBWatchpoint, SBTarget, WatchAddress, (lldb::addr_t, size_t, bool, bool, lldb::SBError &), addr, size, read, write, error); SBWatchpoint sb_watchpoint; lldb::WatchpointSP watchpoint_sp; TargetSP target_sp(GetSP()); if (target_sp && (read || write) && addr != LLDB_INVALID_ADDRESS && size > 0) { std::lock_guard guard(target_sp->GetAPIMutex()); uint32_t watch_type = 0; if (read) watch_type |= LLDB_WATCH_TYPE_READ; if (write) watch_type |= LLDB_WATCH_TYPE_WRITE; if (watch_type == 0) { error.SetErrorString( "Can't create a watchpoint that is neither read nor write."); return LLDB_RECORD_RESULT(sb_watchpoint); } // Target::CreateWatchpoint() is thread safe. Status cw_error; // This API doesn't take in a type, so we can't figure out what it is. CompilerType *type = nullptr; watchpoint_sp = target_sp->CreateWatchpoint(addr, size, type, watch_type, cw_error); error.SetError(cw_error); sb_watchpoint.SetSP(watchpoint_sp); } return LLDB_RECORD_RESULT(sb_watchpoint); } bool SBTarget::EnableAllWatchpoints() { LLDB_RECORD_METHOD_NO_ARGS(bool, SBTarget, EnableAllWatchpoints); TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); std::unique_lock lock; target_sp->GetWatchpointList().GetListMutex(lock); target_sp->EnableAllWatchpoints(); return true; } return false; } bool SBTarget::DisableAllWatchpoints() { LLDB_RECORD_METHOD_NO_ARGS(bool, SBTarget, DisableAllWatchpoints); TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); std::unique_lock lock; target_sp->GetWatchpointList().GetListMutex(lock); target_sp->DisableAllWatchpoints(); return true; } return false; } SBValue SBTarget::CreateValueFromAddress(const char *name, SBAddress addr, SBType type) { LLDB_RECORD_METHOD(lldb::SBValue, SBTarget, CreateValueFromAddress, (const char *, lldb::SBAddress, lldb::SBType), name, addr, type); SBValue sb_value; lldb::ValueObjectSP new_value_sp; if (IsValid() && name && *name && addr.IsValid() && type.IsValid()) { lldb::addr_t load_addr(addr.GetLoadAddress(*this)); ExecutionContext exe_ctx( ExecutionContextRef(ExecutionContext(m_opaque_sp.get(), false))); CompilerType ast_type(type.GetSP()->GetCompilerType(true)); new_value_sp = ValueObject::CreateValueObjectFromAddress(name, load_addr, exe_ctx, ast_type); } sb_value.SetSP(new_value_sp); return LLDB_RECORD_RESULT(sb_value); } lldb::SBValue SBTarget::CreateValueFromData(const char *name, lldb::SBData data, lldb::SBType type) { LLDB_RECORD_METHOD(lldb::SBValue, SBTarget, CreateValueFromData, (const char *, lldb::SBData, lldb::SBType), name, data, type); SBValue sb_value; lldb::ValueObjectSP new_value_sp; if (IsValid() && name && *name && data.IsValid() && type.IsValid()) { DataExtractorSP extractor(*data); ExecutionContext exe_ctx( ExecutionContextRef(ExecutionContext(m_opaque_sp.get(), false))); CompilerType ast_type(type.GetSP()->GetCompilerType(true)); new_value_sp = ValueObject::CreateValueObjectFromData(name, *extractor, exe_ctx, ast_type); } sb_value.SetSP(new_value_sp); return LLDB_RECORD_RESULT(sb_value); } lldb::SBValue SBTarget::CreateValueFromExpression(const char *name, const char *expr) { LLDB_RECORD_METHOD(lldb::SBValue, SBTarget, CreateValueFromExpression, (const char *, const char *), name, expr); SBValue sb_value; lldb::ValueObjectSP new_value_sp; if (IsValid() && name && *name && expr && *expr) { ExecutionContext exe_ctx( ExecutionContextRef(ExecutionContext(m_opaque_sp.get(), false))); new_value_sp = ValueObject::CreateValueObjectFromExpression(name, expr, exe_ctx); } sb_value.SetSP(new_value_sp); return LLDB_RECORD_RESULT(sb_value); } bool SBTarget::DeleteAllWatchpoints() { LLDB_RECORD_METHOD_NO_ARGS(bool, SBTarget, DeleteAllWatchpoints); TargetSP target_sp(GetSP()); if (target_sp) { std::lock_guard guard(target_sp->GetAPIMutex()); std::unique_lock lock; target_sp->GetWatchpointList().GetListMutex(lock); target_sp->RemoveAllWatchpoints(); return true; } return false; } void SBTarget::AppendImageSearchPath(const char *from, const char *to, lldb::SBError &error) { LLDB_RECORD_METHOD(void, SBTarget, AppendImageSearchPath, (const char *, const char *, lldb::SBError &), from, to, error); TargetSP target_sp(GetSP()); if (!target_sp) return error.SetErrorString("invalid target"); const ConstString csFrom(from), csTo(to); if (!csFrom) return error.SetErrorString(" path can't be empty"); if (!csTo) return error.SetErrorString(" path can't be empty"); target_sp->GetImageSearchPathList().Append(csFrom, csTo, true); } lldb::SBModule SBTarget::AddModule(const char *path, const char *triple, const char *uuid_cstr) { LLDB_RECORD_METHOD(lldb::SBModule, SBTarget, AddModule, (const char *, const char *, const char *), path, triple, uuid_cstr); return LLDB_RECORD_RESULT(AddModule(path, triple, uuid_cstr, nullptr)); } lldb::SBModule SBTarget::AddModule(const char *path, const char *triple, const char *uuid_cstr, const char *symfile) { LLDB_RECORD_METHOD(lldb::SBModule, SBTarget, AddModule, (const char *, const char *, const char *, const char *), path, triple, uuid_cstr, symfile); lldb::SBModule sb_module; TargetSP target_sp(GetSP()); if (target_sp) { ModuleSpec module_spec; if (path) module_spec.GetFileSpec().SetFile(path, FileSpec::Style::native); if (uuid_cstr) module_spec.GetUUID().SetFromStringRef(uuid_cstr); if (triple) module_spec.GetArchitecture() = Platform::GetAugmentedArchSpec( target_sp->GetPlatform().get(), triple); else module_spec.GetArchitecture() = target_sp->GetArchitecture(); if (symfile) module_spec.GetSymbolFileSpec().SetFile(symfile, FileSpec::Style::native); sb_module.SetSP(target_sp->GetOrCreateModule(module_spec, true /* notify */)); } return LLDB_RECORD_RESULT(sb_module); } lldb::SBModule SBTarget::AddModule(const SBModuleSpec &module_spec) { LLDB_RECORD_METHOD(lldb::SBModule, SBTarget, AddModule, (const lldb::SBModuleSpec &), module_spec); lldb::SBModule sb_module; TargetSP target_sp(GetSP()); if (target_sp) sb_module.SetSP(target_sp->GetOrCreateModule(*module_spec.m_opaque_up, true /* notify */)); return LLDB_RECORD_RESULT(sb_module); } bool SBTarget::AddModule(lldb::SBModule &module) { LLDB_RECORD_METHOD(bool, SBTarget, AddModule, (lldb::SBModule &), module); TargetSP target_sp(GetSP()); if (target_sp) { target_sp->GetImages().AppendIfNeeded(module.GetSP()); return true; } return false; } uint32_t SBTarget::GetNumModules() const { LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBTarget, GetNumModules); uint32_t num = 0; TargetSP target_sp(GetSP()); if (target_sp) { // The module list is thread safe, no need to lock num = target_sp->GetImages().GetSize(); } return num; } void SBTarget::Clear() { LLDB_RECORD_METHOD_NO_ARGS(void, SBTarget, Clear); m_opaque_sp.reset(); } SBModule SBTarget::FindModule(const SBFileSpec &sb_file_spec) { LLDB_RECORD_METHOD(lldb::SBModule, SBTarget, FindModule, (const lldb::SBFileSpec &), sb_file_spec); SBModule sb_module; TargetSP target_sp(GetSP()); if (target_sp && sb_file_spec.IsValid()) { ModuleSpec module_spec(*sb_file_spec); // The module list is thread safe, no need to lock sb_module.SetSP(target_sp->GetImages().FindFirstModule(module_spec)); } return LLDB_RECORD_RESULT(sb_module); } SBSymbolContextList SBTarget::FindCompileUnits(const SBFileSpec &sb_file_spec) { LLDB_RECORD_METHOD(lldb::SBSymbolContextList, SBTarget, FindCompileUnits, (const lldb::SBFileSpec &), sb_file_spec); SBSymbolContextList sb_sc_list; const TargetSP target_sp(GetSP()); if (target_sp && sb_file_spec.IsValid()) target_sp->GetImages().FindCompileUnits(*sb_file_spec, *sb_sc_list); return LLDB_RECORD_RESULT(sb_sc_list); } lldb::ByteOrder SBTarget::GetByteOrder() { LLDB_RECORD_METHOD_NO_ARGS(lldb::ByteOrder, SBTarget, GetByteOrder); TargetSP target_sp(GetSP()); if (target_sp) return target_sp->GetArchitecture().GetByteOrder(); return eByteOrderInvalid; } const char *SBTarget::GetTriple() { LLDB_RECORD_METHOD_NO_ARGS(const char *, SBTarget, GetTriple); TargetSP target_sp(GetSP()); if (target_sp) { std::string triple(target_sp->GetArchitecture().GetTriple().str()); // Unique the string so we don't run into ownership issues since the const // strings put the string into the string pool once and the strings never // comes out ConstString const_triple(triple.c_str()); return const_triple.GetCString(); } return nullptr; } uint32_t SBTarget::GetDataByteSize() { LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTarget, GetDataByteSize); TargetSP target_sp(GetSP()); if (target_sp) { return target_sp->GetArchitecture().GetDataByteSize(); } return 0; } uint32_t SBTarget::GetCodeByteSize() { LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTarget, GetCodeByteSize); TargetSP target_sp(GetSP()); if (target_sp) { return target_sp->GetArchitecture().GetCodeByteSize(); } return 0; } uint32_t SBTarget::GetAddressByteSize() { LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTarget, GetAddressByteSize); TargetSP target_sp(GetSP()); if (target_sp) return target_sp->GetArchitecture().GetAddressByteSize(); return sizeof(void *); } SBModule SBTarget::GetModuleAtIndex(uint32_t idx) { LLDB_RECORD_METHOD(lldb::SBModule, SBTarget, GetModuleAtIndex, (uint32_t), idx); SBModule sb_module; ModuleSP module_sp; TargetSP target_sp(GetSP()); if (target_sp) { // The module list is thread safe, no need to lock module_sp = target_sp->GetImages().GetModuleAtIndex(idx); sb_module.SetSP(module_sp); } return LLDB_RECORD_RESULT(sb_module); } bool SBTarget::RemoveModule(lldb::SBModule module) { LLDB_RECORD_METHOD(bool, SBTarget, RemoveModule, (lldb::SBModule), module); TargetSP target_sp(GetSP()); if (target_sp) return target_sp->GetImages().Remove(module.GetSP()); return false; } SBBroadcaster SBTarget::GetBroadcaster() const { LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBBroadcaster, SBTarget, GetBroadcaster); TargetSP target_sp(GetSP()); SBBroadcaster broadcaster(target_sp.get(), false); return LLDB_RECORD_RESULT(broadcaster); } bool SBTarget::GetDescription(SBStream &description, lldb::DescriptionLevel description_level) { LLDB_RECORD_METHOD(bool, SBTarget, GetDescription, (lldb::SBStream &, lldb::DescriptionLevel), description, description_level); Stream &strm = description.ref(); TargetSP target_sp(GetSP()); if (target_sp) { target_sp->Dump(&strm, description_level); } else strm.PutCString("No value"); return true; } lldb::SBSymbolContextList SBTarget::FindFunctions(const char *name, uint32_t name_type_mask) { LLDB_RECORD_METHOD(lldb::SBSymbolContextList, SBTarget, FindFunctions, (const char *, uint32_t), name, name_type_mask); lldb::SBSymbolContextList sb_sc_list; if (!name | !name[0]) return LLDB_RECORD_RESULT(sb_sc_list); TargetSP target_sp(GetSP()); if (!target_sp) return LLDB_RECORD_RESULT(sb_sc_list); const bool symbols_ok = true; const bool inlines_ok = true; FunctionNameType mask = static_cast(name_type_mask); target_sp->GetImages().FindFunctions(ConstString(name), mask, symbols_ok, inlines_ok, *sb_sc_list); return LLDB_RECORD_RESULT(sb_sc_list); } lldb::SBSymbolContextList SBTarget::FindGlobalFunctions(const char *name, uint32_t max_matches, MatchType matchtype) { LLDB_RECORD_METHOD(lldb::SBSymbolContextList, SBTarget, FindGlobalFunctions, (const char *, uint32_t, lldb::MatchType), name, max_matches, matchtype); lldb::SBSymbolContextList sb_sc_list; if (name && name[0]) { llvm::StringRef name_ref(name); TargetSP target_sp(GetSP()); if (target_sp) { std::string regexstr; switch (matchtype) { case eMatchTypeRegex: target_sp->GetImages().FindFunctions(RegularExpression(name_ref), true, true, *sb_sc_list); break; case eMatchTypeStartsWith: regexstr = llvm::Regex::escape(name) + ".*"; target_sp->GetImages().FindFunctions(RegularExpression(regexstr), true, true, *sb_sc_list); break; default: target_sp->GetImages().FindFunctions( ConstString(name), eFunctionNameTypeAny, true, true, *sb_sc_list); break; } } } return LLDB_RECORD_RESULT(sb_sc_list); } lldb::SBType SBTarget::FindFirstType(const char *typename_cstr) { LLDB_RECORD_METHOD(lldb::SBType, SBTarget, FindFirstType, (const char *), typename_cstr); TargetSP target_sp(GetSP()); if (typename_cstr && typename_cstr[0] && target_sp) { ConstString const_typename(typename_cstr); SymbolContext sc; const bool exact_match = false; const ModuleList &module_list = target_sp->GetImages(); size_t count = module_list.GetSize(); for (size_t idx = 0; idx < count; idx++) { ModuleSP module_sp(module_list.GetModuleAtIndex(idx)); if (module_sp) { TypeSP type_sp( module_sp->FindFirstType(sc, const_typename, exact_match)); if (type_sp) return LLDB_RECORD_RESULT(SBType(type_sp)); } } // Didn't find the type in the symbols; Try the loaded language runtimes if (auto process_sp = target_sp->GetProcessSP()) { for (auto *runtime : process_sp->GetLanguageRuntimes()) { if (auto vendor = runtime->GetDeclVendor()) { auto types = vendor->FindTypes(const_typename, /*max_matches*/ 1); if (!types.empty()) return LLDB_RECORD_RESULT(SBType(types.front())); } } } // No matches, search for basic typename matches for (auto *type_system : target_sp->GetScratchTypeSystems()) if (auto type = type_system->GetBuiltinTypeByName(const_typename)) return LLDB_RECORD_RESULT(SBType(type)); } return LLDB_RECORD_RESULT(SBType()); } SBType SBTarget::GetBasicType(lldb::BasicType type) { LLDB_RECORD_METHOD(lldb::SBType, SBTarget, GetBasicType, (lldb::BasicType), type); TargetSP target_sp(GetSP()); if (target_sp) { for (auto *type_system : target_sp->GetScratchTypeSystems()) if (auto compiler_type = type_system->GetBasicTypeFromAST(type)) return LLDB_RECORD_RESULT(SBType(compiler_type)); } return LLDB_RECORD_RESULT(SBType()); } lldb::SBTypeList SBTarget::FindTypes(const char *typename_cstr) { LLDB_RECORD_METHOD(lldb::SBTypeList, SBTarget, FindTypes, (const char *), typename_cstr); SBTypeList sb_type_list; TargetSP target_sp(GetSP()); if (typename_cstr && typename_cstr[0] && target_sp) { ModuleList &images = target_sp->GetImages(); ConstString const_typename(typename_cstr); bool exact_match = false; TypeList type_list; llvm::DenseSet searched_symbol_files; images.FindTypes(nullptr, const_typename, exact_match, UINT32_MAX, searched_symbol_files, type_list); for (size_t idx = 0; idx < type_list.GetSize(); idx++) { TypeSP type_sp(type_list.GetTypeAtIndex(idx)); if (type_sp) sb_type_list.Append(SBType(type_sp)); } // Try the loaded language runtimes if (auto process_sp = target_sp->GetProcessSP()) { for (auto *runtime : process_sp->GetLanguageRuntimes()) { if (auto *vendor = runtime->GetDeclVendor()) { auto types = vendor->FindTypes(const_typename, /*max_matches*/ UINT32_MAX); for (auto type : types) sb_type_list.Append(SBType(type)); } } } if (sb_type_list.GetSize() == 0) { // No matches, search for basic typename matches for (auto *type_system : target_sp->GetScratchTypeSystems()) if (auto compiler_type = type_system->GetBuiltinTypeByName(const_typename)) sb_type_list.Append(SBType(compiler_type)); } } return LLDB_RECORD_RESULT(sb_type_list); } SBValueList SBTarget::FindGlobalVariables(const char *name, uint32_t max_matches) { LLDB_RECORD_METHOD(lldb::SBValueList, SBTarget, FindGlobalVariables, (const char *, uint32_t), name, max_matches); SBValueList sb_value_list; TargetSP target_sp(GetSP()); if (name && target_sp) { VariableList variable_list; target_sp->GetImages().FindGlobalVariables(ConstString(name), max_matches, variable_list); if (!variable_list.Empty()) { ExecutionContextScope *exe_scope = target_sp->GetProcessSP().get(); if (exe_scope == nullptr) exe_scope = target_sp.get(); for (const VariableSP &var_sp : variable_list) { lldb::ValueObjectSP valobj_sp( ValueObjectVariable::Create(exe_scope, var_sp)); if (valobj_sp) sb_value_list.Append(SBValue(valobj_sp)); } } } return LLDB_RECORD_RESULT(sb_value_list); } SBValueList SBTarget::FindGlobalVariables(const char *name, uint32_t max_matches, MatchType matchtype) { LLDB_RECORD_METHOD(lldb::SBValueList, SBTarget, FindGlobalVariables, (const char *, uint32_t, lldb::MatchType), name, max_matches, matchtype); SBValueList sb_value_list; TargetSP target_sp(GetSP()); if (name && target_sp) { llvm::StringRef name_ref(name); VariableList variable_list; std::string regexstr; switch (matchtype) { case eMatchTypeNormal: target_sp->GetImages().FindGlobalVariables(ConstString(name), max_matches, variable_list); break; case eMatchTypeRegex: target_sp->GetImages().FindGlobalVariables(RegularExpression(name_ref), max_matches, variable_list); break; case eMatchTypeStartsWith: regexstr = llvm::Regex::escape(name) + ".*"; target_sp->GetImages().FindGlobalVariables(RegularExpression(regexstr), max_matches, variable_list); break; } if (!variable_list.Empty()) { ExecutionContextScope *exe_scope = target_sp->GetProcessSP().get(); if (exe_scope == nullptr) exe_scope = target_sp.get(); for (const VariableSP &var_sp : variable_list) { lldb::ValueObjectSP valobj_sp( ValueObjectVariable::Create(exe_scope, var_sp)); if (valobj_sp) sb_value_list.Append(SBValue(valobj_sp)); } } } return LLDB_RECORD_RESULT(sb_value_list); } lldb::SBValue SBTarget::FindFirstGlobalVariable(const char *name) { LLDB_RECORD_METHOD(lldb::SBValue, SBTarget, FindFirstGlobalVariable, (const char *), name); SBValueList sb_value_list(FindGlobalVariables(name, 1)); if (sb_value_list.IsValid() && sb_value_list.GetSize() > 0) return LLDB_RECORD_RESULT(sb_value_list.GetValueAtIndex(0)); return LLDB_RECORD_RESULT(SBValue()); } SBSourceManager SBTarget::GetSourceManager() { LLDB_RECORD_METHOD_NO_ARGS(lldb::SBSourceManager, SBTarget, GetSourceManager); SBSourceManager source_manager(*this); return LLDB_RECORD_RESULT(source_manager); } lldb::SBInstructionList SBTarget::ReadInstructions(lldb::SBAddress base_addr, uint32_t count) { LLDB_RECORD_METHOD(lldb::SBInstructionList, SBTarget, ReadInstructions, (lldb::SBAddress, uint32_t), base_addr, count); return LLDB_RECORD_RESULT(ReadInstructions(base_addr, count, nullptr)); } lldb::SBInstructionList SBTarget::ReadInstructions(lldb::SBAddress base_addr, uint32_t count, const char *flavor_string) { LLDB_RECORD_METHOD(lldb::SBInstructionList, SBTarget, ReadInstructions, (lldb::SBAddress, uint32_t, const char *), base_addr, count, flavor_string); SBInstructionList sb_instructions; TargetSP target_sp(GetSP()); if (target_sp) { Address *addr_ptr = base_addr.get(); if (addr_ptr) { DataBufferHeap data( target_sp->GetArchitecture().GetMaximumOpcodeByteSize() * count, 0); bool prefer_file_cache = false; lldb_private::Status error; lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; const size_t bytes_read = target_sp->ReadMemory(*addr_ptr, prefer_file_cache, data.GetBytes(), data.GetByteSize(), error, &load_addr); const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS; sb_instructions.SetDisassembler(Disassembler::DisassembleBytes( target_sp->GetArchitecture(), nullptr, flavor_string, *addr_ptr, data.GetBytes(), bytes_read, count, data_from_file)); } } return LLDB_RECORD_RESULT(sb_instructions); } lldb::SBInstructionList SBTarget::GetInstructions(lldb::SBAddress base_addr, const void *buf, size_t size) { LLDB_RECORD_METHOD(lldb::SBInstructionList, SBTarget, GetInstructions, (lldb::SBAddress, const void *, size_t), base_addr, buf, size); return LLDB_RECORD_RESULT( GetInstructionsWithFlavor(base_addr, nullptr, buf, size)); } lldb::SBInstructionList SBTarget::GetInstructionsWithFlavor(lldb::SBAddress base_addr, const char *flavor_string, const void *buf, size_t size) { LLDB_RECORD_METHOD(lldb::SBInstructionList, SBTarget, GetInstructionsWithFlavor, (lldb::SBAddress, const char *, const void *, size_t), base_addr, flavor_string, buf, size); SBInstructionList sb_instructions; TargetSP target_sp(GetSP()); if (target_sp) { Address addr; if (base_addr.get()) addr = *base_addr.get(); const bool data_from_file = true; sb_instructions.SetDisassembler(Disassembler::DisassembleBytes( target_sp->GetArchitecture(), nullptr, flavor_string, addr, buf, size, UINT32_MAX, data_from_file)); } return LLDB_RECORD_RESULT(sb_instructions); } lldb::SBInstructionList SBTarget::GetInstructions(lldb::addr_t base_addr, const void *buf, size_t size) { LLDB_RECORD_METHOD(lldb::SBInstructionList, SBTarget, GetInstructions, (lldb::addr_t, const void *, size_t), base_addr, buf, size); return LLDB_RECORD_RESULT(GetInstructionsWithFlavor( ResolveLoadAddress(base_addr), nullptr, buf, size)); } lldb::SBInstructionList SBTarget::GetInstructionsWithFlavor(lldb::addr_t base_addr, const char *flavor_string, const void *buf, size_t size) { LLDB_RECORD_METHOD(lldb::SBInstructionList, SBTarget, GetInstructionsWithFlavor, (lldb::addr_t, const char *, const void *, size_t), base_addr, flavor_string, buf, size); return LLDB_RECORD_RESULT(GetInstructionsWithFlavor( ResolveLoadAddress(base_addr), flavor_string, buf, size)); } SBError SBTarget::SetSectionLoadAddress(lldb::SBSection section, lldb::addr_t section_base_addr) { LLDB_RECORD_METHOD(lldb::SBError, SBTarget, SetSectionLoadAddress, (lldb::SBSection, lldb::addr_t), section, section_base_addr); SBError sb_error; TargetSP target_sp(GetSP()); if (target_sp) { if (!section.IsValid()) { sb_error.SetErrorStringWithFormat("invalid section"); } else { SectionSP section_sp(section.GetSP()); if (section_sp) { if (section_sp->IsThreadSpecific()) { sb_error.SetErrorString( "thread specific sections are not yet supported"); } else { ProcessSP process_sp(target_sp->GetProcessSP()); if (target_sp->SetSectionLoadAddress(section_sp, section_base_addr)) { ModuleSP module_sp(section_sp->GetModule()); if (module_sp) { ModuleList module_list; module_list.Append(module_sp); target_sp->ModulesDidLoad(module_list); } // Flush info in the process (stack frames, etc) if (process_sp) process_sp->Flush(); } } } } } else { sb_error.SetErrorString("invalid target"); } return LLDB_RECORD_RESULT(sb_error); } SBError SBTarget::ClearSectionLoadAddress(lldb::SBSection section) { LLDB_RECORD_METHOD(lldb::SBError, SBTarget, ClearSectionLoadAddress, (lldb::SBSection), section); SBError sb_error; TargetSP target_sp(GetSP()); if (target_sp) { if (!section.IsValid()) { sb_error.SetErrorStringWithFormat("invalid section"); } else { SectionSP section_sp(section.GetSP()); if (section_sp) { ProcessSP process_sp(target_sp->GetProcessSP()); if (target_sp->SetSectionUnloaded(section_sp)) { ModuleSP module_sp(section_sp->GetModule()); if (module_sp) { ModuleList module_list; module_list.Append(module_sp); target_sp->ModulesDidUnload(module_list, false); } // Flush info in the process (stack frames, etc) if (process_sp) process_sp->Flush(); } } else { sb_error.SetErrorStringWithFormat("invalid section"); } } } else { sb_error.SetErrorStringWithFormat("invalid target"); } return LLDB_RECORD_RESULT(sb_error); } SBError SBTarget::SetModuleLoadAddress(lldb::SBModule module, int64_t slide_offset) { LLDB_RECORD_METHOD(lldb::SBError, SBTarget, SetModuleLoadAddress, (lldb::SBModule, int64_t), module, slide_offset); SBError sb_error; TargetSP target_sp(GetSP()); if (target_sp) { ModuleSP module_sp(module.GetSP()); if (module_sp) { bool changed = false; if (module_sp->SetLoadAddress(*target_sp, slide_offset, true, changed)) { // The load was successful, make sure that at least some sections // changed before we notify that our module was loaded. if (changed) { ModuleList module_list; module_list.Append(module_sp); target_sp->ModulesDidLoad(module_list); // Flush info in the process (stack frames, etc) ProcessSP process_sp(target_sp->GetProcessSP()); if (process_sp) process_sp->Flush(); } } } else { sb_error.SetErrorStringWithFormat("invalid module"); } } else { sb_error.SetErrorStringWithFormat("invalid target"); } return LLDB_RECORD_RESULT(sb_error); } SBError SBTarget::ClearModuleLoadAddress(lldb::SBModule module) { LLDB_RECORD_METHOD(lldb::SBError, SBTarget, ClearModuleLoadAddress, (lldb::SBModule), module); SBError sb_error; char path[PATH_MAX]; TargetSP target_sp(GetSP()); if (target_sp) { ModuleSP module_sp(module.GetSP()); if (module_sp) { ObjectFile *objfile = module_sp->GetObjectFile(); if (objfile) { SectionList *section_list = objfile->GetSectionList(); if (section_list) { ProcessSP process_sp(target_sp->GetProcessSP()); bool changed = false; const size_t num_sections = section_list->GetSize(); for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) { SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); if (section_sp) changed |= target_sp->SetSectionUnloaded(section_sp); } if (changed) { ModuleList module_list; module_list.Append(module_sp); target_sp->ModulesDidUnload(module_list, false); // Flush info in the process (stack frames, etc) ProcessSP process_sp(target_sp->GetProcessSP()); if (process_sp) process_sp->Flush(); } } else { module_sp->GetFileSpec().GetPath(path, sizeof(path)); sb_error.SetErrorStringWithFormat("no sections in object file '%s'", path); } } else { module_sp->GetFileSpec().GetPath(path, sizeof(path)); sb_error.SetErrorStringWithFormat("no object file for module '%s'", path); } } else { sb_error.SetErrorStringWithFormat("invalid module"); } } else { sb_error.SetErrorStringWithFormat("invalid target"); } return LLDB_RECORD_RESULT(sb_error); } lldb::SBSymbolContextList SBTarget::FindSymbols(const char *name, lldb::SymbolType symbol_type) { LLDB_RECORD_METHOD(lldb::SBSymbolContextList, SBTarget, FindSymbols, (const char *, lldb::SymbolType), name, symbol_type); SBSymbolContextList sb_sc_list; if (name && name[0]) { TargetSP target_sp(GetSP()); if (target_sp) target_sp->GetImages().FindSymbolsWithNameAndType( ConstString(name), symbol_type, *sb_sc_list); } return LLDB_RECORD_RESULT(sb_sc_list); } lldb::SBValue SBTarget::EvaluateExpression(const char *expr) { LLDB_RECORD_METHOD(lldb::SBValue, SBTarget, EvaluateExpression, (const char *), expr); TargetSP target_sp(GetSP()); if (!target_sp) return LLDB_RECORD_RESULT(SBValue()); SBExpressionOptions options; lldb::DynamicValueType fetch_dynamic_value = target_sp->GetPreferDynamicValue(); options.SetFetchDynamicValue(fetch_dynamic_value); options.SetUnwindOnError(true); return LLDB_RECORD_RESULT(EvaluateExpression(expr, options)); } lldb::SBValue SBTarget::EvaluateExpression(const char *expr, const SBExpressionOptions &options) { LLDB_RECORD_METHOD(lldb::SBValue, SBTarget, EvaluateExpression, (const char *, const lldb::SBExpressionOptions &), expr, options); Log *expr_log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); SBValue expr_result; ValueObjectSP expr_value_sp; TargetSP target_sp(GetSP()); StackFrame *frame = nullptr; if (target_sp) { if (expr == nullptr || expr[0] == '\0') { return LLDB_RECORD_RESULT(expr_result); } std::lock_guard guard(target_sp->GetAPIMutex()); ExecutionContext exe_ctx(m_opaque_sp.get()); frame = exe_ctx.GetFramePtr(); Target *target = exe_ctx.GetTargetPtr(); if (target) { target->EvaluateExpression(expr, frame, expr_value_sp, options.ref()); expr_result.SetSP(expr_value_sp, options.GetFetchDynamicValue()); } } LLDB_LOGF(expr_log, "** [SBTarget::EvaluateExpression] Expression result is " "%s, summary %s **", expr_result.GetValue(), expr_result.GetSummary()); return LLDB_RECORD_RESULT(expr_result); } lldb::addr_t SBTarget::GetStackRedZoneSize() { LLDB_RECORD_METHOD_NO_ARGS(lldb::addr_t, SBTarget, GetStackRedZoneSize); TargetSP target_sp(GetSP()); if (target_sp) { ABISP abi_sp; ProcessSP process_sp(target_sp->GetProcessSP()); if (process_sp) abi_sp = process_sp->GetABI(); else abi_sp = ABI::FindPlugin(ProcessSP(), target_sp->GetArchitecture()); if (abi_sp) return abi_sp->GetRedZoneSize(); } return 0; } lldb::SBLaunchInfo SBTarget::GetLaunchInfo() const { LLDB_RECORD_METHOD_CONST_NO_ARGS(lldb::SBLaunchInfo, SBTarget, GetLaunchInfo); lldb::SBLaunchInfo launch_info(nullptr); TargetSP target_sp(GetSP()); if (target_sp) launch_info.set_ref(m_opaque_sp->GetProcessLaunchInfo()); return LLDB_RECORD_RESULT(launch_info); } void SBTarget::SetLaunchInfo(const lldb::SBLaunchInfo &launch_info) { LLDB_RECORD_METHOD(void, SBTarget, SetLaunchInfo, (const lldb::SBLaunchInfo &), launch_info); TargetSP target_sp(GetSP()); if (target_sp) m_opaque_sp->SetProcessLaunchInfo(launch_info.ref()); } SBEnvironment SBTarget::GetEnvironment() { LLDB_RECORD_METHOD_NO_ARGS(lldb::SBEnvironment, SBTarget, GetEnvironment); TargetSP target_sp(GetSP()); if (target_sp) { return LLDB_RECORD_RESULT(SBEnvironment(target_sp->GetEnvironment())); } return LLDB_RECORD_RESULT(SBEnvironment()); } namespace lldb_private { namespace repro { template <> void RegisterMethods(Registry &R) { LLDB_REGISTER_CONSTRUCTOR(SBTarget, ()); LLDB_REGISTER_CONSTRUCTOR(SBTarget, (const lldb::SBTarget &)); LLDB_REGISTER_CONSTRUCTOR(SBTarget, (const lldb::TargetSP &)); LLDB_REGISTER_METHOD(const lldb::SBTarget &, SBTarget, operator=,(const lldb::SBTarget &)); LLDB_REGISTER_STATIC_METHOD(bool, SBTarget, EventIsTargetEvent, (const lldb::SBEvent &)); LLDB_REGISTER_STATIC_METHOD(lldb::SBTarget, SBTarget, GetTargetFromEvent, (const lldb::SBEvent &)); LLDB_REGISTER_STATIC_METHOD(uint32_t, SBTarget, GetNumModulesFromEvent, (const lldb::SBEvent &)); LLDB_REGISTER_STATIC_METHOD(lldb::SBModule, SBTarget, GetModuleAtIndexFromEvent, (const uint32_t, const lldb::SBEvent &)); LLDB_REGISTER_STATIC_METHOD(const char *, SBTarget, GetBroadcasterClassName, ()); LLDB_REGISTER_METHOD_CONST(bool, SBTarget, IsValid, ()); LLDB_REGISTER_METHOD_CONST(bool, SBTarget, operator bool, ()); LLDB_REGISTER_METHOD(lldb::SBProcess, SBTarget, GetProcess, ()); LLDB_REGISTER_METHOD(lldb::SBPlatform, SBTarget, GetPlatform, ()); LLDB_REGISTER_METHOD_CONST(lldb::SBDebugger, SBTarget, GetDebugger, ()); LLDB_REGISTER_METHOD(lldb::SBStructuredData, SBTarget, GetStatistics, ()); LLDB_REGISTER_METHOD(void, SBTarget, SetCollectingStats, (bool)); LLDB_REGISTER_METHOD(bool, SBTarget, GetCollectingStats, ()); LLDB_REGISTER_METHOD(lldb::SBProcess, SBTarget, LoadCore, (const char *)); LLDB_REGISTER_METHOD(lldb::SBProcess, SBTarget, LoadCore, (const char *, lldb::SBError &)); LLDB_REGISTER_METHOD(lldb::SBProcess, SBTarget, LaunchSimple, (const char **, const char **, const char *)); LLDB_REGISTER_METHOD(lldb::SBError, SBTarget, Install, ()); LLDB_REGISTER_METHOD(lldb::SBProcess, SBTarget, Launch, (lldb::SBListener &, const char **, const char **, const char *, const char *, const char *, const char *, uint32_t, bool, lldb::SBError &)); LLDB_REGISTER_METHOD(lldb::SBProcess, SBTarget, Launch, (lldb::SBLaunchInfo &, lldb::SBError &)); LLDB_REGISTER_METHOD(lldb::SBProcess, SBTarget, Attach, (lldb::SBAttachInfo &, lldb::SBError &)); LLDB_REGISTER_METHOD(lldb::SBProcess, SBTarget, AttachToProcessWithID, (lldb::SBListener &, lldb::pid_t, lldb::SBError &)); LLDB_REGISTER_METHOD( lldb::SBProcess, SBTarget, AttachToProcessWithName, (lldb::SBListener &, const char *, bool, lldb::SBError &)); LLDB_REGISTER_METHOD( lldb::SBProcess, SBTarget, ConnectRemote, (lldb::SBListener &, const char *, const char *, lldb::SBError &)); LLDB_REGISTER_METHOD(lldb::SBFileSpec, SBTarget, GetExecutable, ()); LLDB_REGISTER_METHOD_CONST(bool, SBTarget, operator==,(const lldb::SBTarget &)); LLDB_REGISTER_METHOD_CONST(bool, SBTarget, operator!=,(const lldb::SBTarget &)); LLDB_REGISTER_METHOD(lldb::SBAddress, SBTarget, ResolveLoadAddress, (lldb::addr_t)); LLDB_REGISTER_METHOD(lldb::SBAddress, SBTarget, ResolveFileAddress, (lldb::addr_t)); LLDB_REGISTER_METHOD(lldb::SBAddress, SBTarget, ResolvePastLoadAddress, (uint32_t, lldb::addr_t)); LLDB_REGISTER_METHOD(lldb::SBSymbolContext, SBTarget, ResolveSymbolContextForAddress, (const lldb::SBAddress &, uint32_t)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByLocation, (const char *, uint32_t)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByLocation, (const lldb::SBFileSpec &, uint32_t)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByLocation, (const lldb::SBFileSpec &, uint32_t, lldb::addr_t)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByLocation, (const lldb::SBFileSpec &, uint32_t, lldb::addr_t, lldb::SBFileSpecList &)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByLocation, (const lldb::SBFileSpec &, uint32_t, uint32_t, lldb::addr_t, lldb::SBFileSpecList &)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByName, (const char *, const char *)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByName, (const char *, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByName, (const char *, uint32_t, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByName, (const char *, uint32_t, lldb::LanguageType, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByNames, (const char **, uint32_t, uint32_t, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByNames, (const char **, uint32_t, uint32_t, lldb::LanguageType, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByNames, (const char **, uint32_t, uint32_t, lldb::LanguageType, lldb::addr_t, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByRegex, (const char *, const char *)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByRegex, (const char *, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByRegex, (const char *, lldb::LanguageType, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateByAddress, (lldb::addr_t)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateBySBAddress, (lldb::SBAddress &)); LLDB_REGISTER_METHOD( lldb::SBBreakpoint, SBTarget, BreakpointCreateBySourceRegex, (const char *, const lldb::SBFileSpec &, const char *)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateBySourceRegex, (const char *, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &)); LLDB_REGISTER_METHOD( lldb::SBBreakpoint, SBTarget, BreakpointCreateBySourceRegex, (const char *, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &, const lldb::SBStringList &)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, BreakpointCreateForException, (lldb::LanguageType, bool, bool)); LLDB_REGISTER_METHOD( lldb::SBBreakpoint, SBTarget, BreakpointCreateFromScript, (const char *, lldb::SBStructuredData &, const lldb::SBFileSpecList &, const lldb::SBFileSpecList &, bool)); LLDB_REGISTER_METHOD_CONST(uint32_t, SBTarget, GetNumBreakpoints, ()); LLDB_REGISTER_METHOD_CONST(lldb::SBBreakpoint, SBTarget, GetBreakpointAtIndex, (uint32_t)); LLDB_REGISTER_METHOD(bool, SBTarget, BreakpointDelete, (lldb::break_id_t)); LLDB_REGISTER_METHOD(lldb::SBBreakpoint, SBTarget, FindBreakpointByID, (lldb::break_id_t)); LLDB_REGISTER_METHOD(bool, SBTarget, FindBreakpointsByName, (const char *, lldb::SBBreakpointList &)); LLDB_REGISTER_METHOD(void, SBTarget, GetBreakpointNames, (lldb::SBStringList &)); LLDB_REGISTER_METHOD(void, SBTarget, DeleteBreakpointName, (const char *)); LLDB_REGISTER_METHOD(bool, SBTarget, EnableAllBreakpoints, ()); LLDB_REGISTER_METHOD(bool, SBTarget, DisableAllBreakpoints, ()); LLDB_REGISTER_METHOD(bool, SBTarget, DeleteAllBreakpoints, ()); LLDB_REGISTER_METHOD(lldb::SBError, SBTarget, BreakpointsCreateFromFile, (lldb::SBFileSpec &, lldb::SBBreakpointList &)); LLDB_REGISTER_METHOD( lldb::SBError, SBTarget, BreakpointsCreateFromFile, (lldb::SBFileSpec &, lldb::SBStringList &, lldb::SBBreakpointList &)); LLDB_REGISTER_METHOD(lldb::SBError, SBTarget, BreakpointsWriteToFile, (lldb::SBFileSpec &)); LLDB_REGISTER_METHOD(lldb::SBError, SBTarget, BreakpointsWriteToFile, (lldb::SBFileSpec &, lldb::SBBreakpointList &, bool)); LLDB_REGISTER_METHOD_CONST(uint32_t, SBTarget, GetNumWatchpoints, ()); LLDB_REGISTER_METHOD_CONST(lldb::SBWatchpoint, SBTarget, GetWatchpointAtIndex, (uint32_t)); LLDB_REGISTER_METHOD(bool, SBTarget, DeleteWatchpoint, (lldb::watch_id_t)); LLDB_REGISTER_METHOD(lldb::SBWatchpoint, SBTarget, FindWatchpointByID, (lldb::watch_id_t)); LLDB_REGISTER_METHOD(lldb::SBWatchpoint, SBTarget, WatchAddress, (lldb::addr_t, size_t, bool, bool, lldb::SBError &)); LLDB_REGISTER_METHOD(bool, SBTarget, EnableAllWatchpoints, ()); LLDB_REGISTER_METHOD(bool, SBTarget, DisableAllWatchpoints, ()); LLDB_REGISTER_METHOD(lldb::SBValue, SBTarget, CreateValueFromAddress, (const char *, lldb::SBAddress, lldb::SBType)); LLDB_REGISTER_METHOD(lldb::SBValue, SBTarget, CreateValueFromData, (const char *, lldb::SBData, lldb::SBType)); LLDB_REGISTER_METHOD(lldb::SBValue, SBTarget, CreateValueFromExpression, (const char *, const char *)); LLDB_REGISTER_METHOD(bool, SBTarget, DeleteAllWatchpoints, ()); LLDB_REGISTER_METHOD(void, SBTarget, AppendImageSearchPath, (const char *, const char *, lldb::SBError &)); LLDB_REGISTER_METHOD(lldb::SBModule, SBTarget, AddModule, (const char *, const char *, const char *)); LLDB_REGISTER_METHOD( lldb::SBModule, SBTarget, AddModule, (const char *, const char *, const char *, const char *)); LLDB_REGISTER_METHOD(lldb::SBModule, SBTarget, AddModule, (const lldb::SBModuleSpec &)); LLDB_REGISTER_METHOD(bool, SBTarget, AddModule, (lldb::SBModule &)); LLDB_REGISTER_METHOD_CONST(uint32_t, SBTarget, GetNumModules, ()); LLDB_REGISTER_METHOD(void, SBTarget, Clear, ()); LLDB_REGISTER_METHOD(lldb::SBModule, SBTarget, FindModule, (const lldb::SBFileSpec &)); LLDB_REGISTER_METHOD(lldb::SBSymbolContextList, SBTarget, FindCompileUnits, (const lldb::SBFileSpec &)); LLDB_REGISTER_METHOD(lldb::ByteOrder, SBTarget, GetByteOrder, ()); LLDB_REGISTER_METHOD(const char *, SBTarget, GetTriple, ()); LLDB_REGISTER_METHOD(uint32_t, SBTarget, GetDataByteSize, ()); LLDB_REGISTER_METHOD(uint32_t, SBTarget, GetCodeByteSize, ()); LLDB_REGISTER_METHOD(uint32_t, SBTarget, GetAddressByteSize, ()); LLDB_REGISTER_METHOD(lldb::SBModule, SBTarget, GetModuleAtIndex, (uint32_t)); LLDB_REGISTER_METHOD(bool, SBTarget, RemoveModule, (lldb::SBModule)); LLDB_REGISTER_METHOD_CONST(lldb::SBBroadcaster, SBTarget, GetBroadcaster, ()); LLDB_REGISTER_METHOD(bool, SBTarget, GetDescription, (lldb::SBStream &, lldb::DescriptionLevel)); LLDB_REGISTER_METHOD(lldb::SBSymbolContextList, SBTarget, FindFunctions, (const char *, uint32_t)); LLDB_REGISTER_METHOD(lldb::SBSymbolContextList, SBTarget, FindGlobalFunctions, (const char *, uint32_t, lldb::MatchType)); LLDB_REGISTER_METHOD(lldb::SBType, SBTarget, FindFirstType, (const char *)); LLDB_REGISTER_METHOD(lldb::SBType, SBTarget, GetBasicType, (lldb::BasicType)); LLDB_REGISTER_METHOD(lldb::SBTypeList, SBTarget, FindTypes, (const char *)); LLDB_REGISTER_METHOD(lldb::SBValueList, SBTarget, FindGlobalVariables, (const char *, uint32_t)); LLDB_REGISTER_METHOD(lldb::SBValueList, SBTarget, FindGlobalVariables, (const char *, uint32_t, lldb::MatchType)); LLDB_REGISTER_METHOD(lldb::SBValue, SBTarget, FindFirstGlobalVariable, (const char *)); LLDB_REGISTER_METHOD(lldb::SBSourceManager, SBTarget, GetSourceManager, ()); LLDB_REGISTER_METHOD(lldb::SBInstructionList, SBTarget, ReadInstructions, (lldb::SBAddress, uint32_t)); LLDB_REGISTER_METHOD(lldb::SBInstructionList, SBTarget, ReadInstructions, (lldb::SBAddress, uint32_t, const char *)); LLDB_REGISTER_METHOD(lldb::SBError, SBTarget, SetSectionLoadAddress, (lldb::SBSection, lldb::addr_t)); LLDB_REGISTER_METHOD(lldb::SBError, SBTarget, ClearSectionLoadAddress, (lldb::SBSection)); LLDB_REGISTER_METHOD(lldb::SBError, SBTarget, SetModuleLoadAddress, (lldb::SBModule, int64_t)); LLDB_REGISTER_METHOD(lldb::SBError, SBTarget, ClearModuleLoadAddress, (lldb::SBModule)); LLDB_REGISTER_METHOD(lldb::SBSymbolContextList, SBTarget, FindSymbols, (const char *, lldb::SymbolType)); LLDB_REGISTER_METHOD(lldb::SBValue, SBTarget, EvaluateExpression, (const char *)); LLDB_REGISTER_METHOD(lldb::SBValue, SBTarget, EvaluateExpression, (const char *, const lldb::SBExpressionOptions &)); LLDB_REGISTER_METHOD(lldb::addr_t, SBTarget, GetStackRedZoneSize, ()); LLDB_REGISTER_METHOD_CONST(lldb::SBLaunchInfo, SBTarget, GetLaunchInfo, ()); LLDB_REGISTER_METHOD(void, SBTarget, SetLaunchInfo, (const lldb::SBLaunchInfo &)); LLDB_REGISTER_METHOD( size_t, SBTarget, ReadMemory, (const lldb::SBAddress, void *, size_t, lldb::SBError &)); LLDB_REGISTER_METHOD(lldb::SBInstructionList, SBTarget, GetInstructions, (lldb::SBAddress, const void *, size_t)); LLDB_REGISTER_METHOD(lldb::SBInstructionList, SBTarget, GetInstructionsWithFlavor, (lldb::SBAddress, const char *, const void *, size_t)); LLDB_REGISTER_METHOD(lldb::SBInstructionList, SBTarget, GetInstructions, (lldb::addr_t, const void *, size_t)); LLDB_REGISTER_METHOD(lldb::SBInstructionList, SBTarget, GetInstructionsWithFlavor, (lldb::addr_t, const char *, const void *, size_t)); LLDB_REGISTER_METHOD(lldb::SBEnvironment, SBTarget, GetEnvironment, ()); } } } diff --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp index 18631a0c5315..21bf7f4ac46d 100644 --- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp +++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -1,872 +1,872 @@ //===-- PlatformRemoteGDBServer.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 "PlatformRemoteGDBServer.h" #include "lldb/Host/Config.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/StreamFile.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/PosixApi.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/ProcessInfo.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/UriParser.h" #include "Plugins/Process/Utility/GDBRemoteSignals.h" using namespace lldb; using namespace lldb_private; using namespace lldb_private::platform_gdb_server; LLDB_PLUGIN_DEFINE_ADV(PlatformRemoteGDBServer, PlatformGDB) static bool g_initialized = false; void PlatformRemoteGDBServer::Initialize() { Platform::Initialize(); if (!g_initialized) { g_initialized = true; PluginManager::RegisterPlugin( PlatformRemoteGDBServer::GetPluginNameStatic(), PlatformRemoteGDBServer::GetDescriptionStatic(), PlatformRemoteGDBServer::CreateInstance); } } void PlatformRemoteGDBServer::Terminate() { if (g_initialized) { g_initialized = false; PluginManager::UnregisterPlugin(PlatformRemoteGDBServer::CreateInstance); } Platform::Terminate(); } PlatformSP PlatformRemoteGDBServer::CreateInstance(bool force, const ArchSpec *arch) { bool create = force; if (!create) { create = !arch->TripleVendorWasSpecified() && !arch->TripleOSWasSpecified(); } if (create) return PlatformSP(new PlatformRemoteGDBServer()); return PlatformSP(); } ConstString PlatformRemoteGDBServer::GetPluginNameStatic() { static ConstString g_name("remote-gdb-server"); return g_name; } const char *PlatformRemoteGDBServer::GetDescriptionStatic() { return "A platform that uses the GDB remote protocol as the communication " "transport."; } const char *PlatformRemoteGDBServer::GetDescription() { if (m_platform_description.empty()) { if (IsConnected()) { // Send the get description packet } } if (!m_platform_description.empty()) return m_platform_description.c_str(); return GetDescriptionStatic(); } Status PlatformRemoteGDBServer::ResolveExecutable( const ModuleSpec &module_spec, lldb::ModuleSP &exe_module_sp, const FileSpecList *module_search_paths_ptr) { // copied from PlatformRemoteiOS Status error; // Nothing special to do here, just use the actual file and architecture ModuleSpec resolved_module_spec(module_spec); // Resolve any executable within an apk on Android? // Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec()); if (FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec()) || module_spec.GetUUID().IsValid()) { if (resolved_module_spec.GetArchitecture().IsValid() || resolved_module_spec.GetUUID().IsValid()) { error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, module_search_paths_ptr, nullptr, nullptr); if (exe_module_sp && exe_module_sp->GetObjectFile()) return error; exe_module_sp.reset(); } // No valid architecture was specified or the exact arch wasn't found so // ask the platform for the architectures that we should be using (in the // correct order) and see if we can find a match that way StreamString arch_names; for (uint32_t idx = 0; GetSupportedArchitectureAtIndex( idx, resolved_module_spec.GetArchitecture()); ++idx) { error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, module_search_paths_ptr, nullptr, nullptr); // Did we find an executable using one of the if (error.Success()) { if (exe_module_sp && exe_module_sp->GetObjectFile()) break; else error.SetErrorToGenericError(); } if (idx > 0) arch_names.PutCString(", "); arch_names.PutCString( resolved_module_spec.GetArchitecture().GetArchitectureName()); } if (error.Fail() || !exe_module_sp) { if (FileSystem::Instance().Readable(resolved_module_spec.GetFileSpec())) { error.SetErrorStringWithFormat( "'%s' doesn't contain any '%s' platform architectures: %s", resolved_module_spec.GetFileSpec().GetPath().c_str(), GetPluginName().GetCString(), arch_names.GetData()); } else { error.SetErrorStringWithFormat( "'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); } } } else { error.SetErrorStringWithFormat( "'%s' does not exist", resolved_module_spec.GetFileSpec().GetPath().c_str()); } return error; } bool PlatformRemoteGDBServer::GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, ModuleSpec &module_spec) { Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); const auto module_path = module_file_spec.GetPath(false); if (!m_gdb_client.GetModuleInfo(module_file_spec, arch, module_spec)) { LLDB_LOGF( log, "PlatformRemoteGDBServer::%s - failed to get module info for %s:%s", __FUNCTION__, module_path.c_str(), arch.GetTriple().getTriple().c_str()); return false; } if (log) { StreamString stream; module_spec.Dump(stream); LLDB_LOGF(log, "PlatformRemoteGDBServer::%s - got module info for (%s:%s) : %s", __FUNCTION__, module_path.c_str(), arch.GetTriple().getTriple().c_str(), stream.GetData()); } return true; } Status PlatformRemoteGDBServer::GetFileWithUUID(const FileSpec &platform_file, const UUID *uuid_ptr, FileSpec &local_file) { // Default to the local case local_file = platform_file; return Status(); } /// Default Constructor PlatformRemoteGDBServer::PlatformRemoteGDBServer() : Platform(false), // This is a remote platform m_gdb_client() {} /// Destructor. /// /// The destructor is virtual since this class is designed to be /// inherited from by the plug-in instance. PlatformRemoteGDBServer::~PlatformRemoteGDBServer() {} bool PlatformRemoteGDBServer::GetSupportedArchitectureAtIndex(uint32_t idx, ArchSpec &arch) { ArchSpec remote_arch = m_gdb_client.GetSystemArchitecture(); if (idx == 0) { arch = remote_arch; return arch.IsValid(); } else if (idx == 1 && remote_arch.IsValid() && remote_arch.GetTriple().isArch64Bit()) { arch.SetTriple(remote_arch.GetTriple().get32BitArchVariant()); return arch.IsValid(); } return false; } size_t PlatformRemoteGDBServer::GetSoftwareBreakpointTrapOpcode( Target &target, BreakpointSite *bp_site) { // This isn't needed if the z/Z packets are supported in the GDB remote // server. But we might need a packet to detect this. return 0; } bool PlatformRemoteGDBServer::GetRemoteOSVersion() { m_os_version = m_gdb_client.GetOSVersion(); return !m_os_version.empty(); } bool PlatformRemoteGDBServer::GetRemoteOSBuildString(std::string &s) { return m_gdb_client.GetOSBuildString(s); } bool PlatformRemoteGDBServer::GetRemoteOSKernelDescription(std::string &s) { return m_gdb_client.GetOSKernelDescription(s); } // Remote Platform subclasses need to override this function ArchSpec PlatformRemoteGDBServer::GetRemoteSystemArchitecture() { return m_gdb_client.GetSystemArchitecture(); } FileSpec PlatformRemoteGDBServer::GetRemoteWorkingDirectory() { if (IsConnected()) { Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); FileSpec working_dir; if (m_gdb_client.GetWorkingDir(working_dir) && log) LLDB_LOGF(log, "PlatformRemoteGDBServer::GetRemoteWorkingDirectory() -> '%s'", working_dir.GetCString()); return working_dir; } else { return Platform::GetRemoteWorkingDirectory(); } } bool PlatformRemoteGDBServer::SetRemoteWorkingDirectory( const FileSpec &working_dir) { if (IsConnected()) { // Clear the working directory it case it doesn't get set correctly. This // will for use to re-read it Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); LLDB_LOGF(log, "PlatformRemoteGDBServer::SetRemoteWorkingDirectory('%s')", working_dir.GetCString()); return m_gdb_client.SetWorkingDir(working_dir) == 0; } else return Platform::SetRemoteWorkingDirectory(working_dir); } bool PlatformRemoteGDBServer::IsConnected() const { return m_gdb_client.IsConnected(); } Status PlatformRemoteGDBServer::ConnectRemote(Args &args) { Status error; if (IsConnected()) { error.SetErrorStringWithFormat("the platform is already connected to '%s', " "execute 'platform disconnect' to close the " "current connection", GetHostname()); return error; } if (args.GetArgumentCount() != 1) { error.SetErrorString( "\"platform connect\" takes a single argument: "); return error; } const char *url = args.GetArgumentAtIndex(0); if (!url) return Status("URL is null."); int port; llvm::StringRef scheme, hostname, pathname; if (!UriParser::Parse(url, scheme, hostname, port, pathname)) return Status("Invalid URL: %s", url); // We're going to reuse the hostname when we connect to the debugserver. m_platform_scheme = std::string(scheme); m_platform_hostname = std::string(hostname); m_gdb_client.SetConnection(std::make_unique()); if (repro::Reproducer::Instance().IsReplaying()) { error = m_gdb_replay_server.Connect(m_gdb_client); if (error.Success()) m_gdb_replay_server.StartAsyncThread(); } else { if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) { repro::GDBRemoteProvider &provider = g->GetOrCreate(); m_gdb_client.SetPacketRecorder(provider.GetNewPacketRecorder()); } m_gdb_client.Connect(url, &error); } if (error.Fail()) return error; if (m_gdb_client.HandshakeWithServer(&error)) { m_gdb_client.GetHostInfo(); // If a working directory was set prior to connecting, send it down // now. if (m_working_dir) m_gdb_client.SetWorkingDir(m_working_dir); } else { m_gdb_client.Disconnect(); if (error.Success()) error.SetErrorString("handshake failed"); } return error; } Status PlatformRemoteGDBServer::DisconnectRemote() { Status error; m_gdb_client.Disconnect(&error); m_remote_signals_sp.reset(); return error; } const char *PlatformRemoteGDBServer::GetHostname() { m_gdb_client.GetHostname(m_name); if (m_name.empty()) return nullptr; return m_name.c_str(); } llvm::Optional PlatformRemoteGDBServer::DoGetUserName(UserIDResolver::id_t uid) { std::string name; if (m_gdb_client.GetUserName(uid, name)) return std::move(name); return llvm::None; } llvm::Optional PlatformRemoteGDBServer::DoGetGroupName(UserIDResolver::id_t gid) { std::string name; if (m_gdb_client.GetGroupName(gid, name)) return std::move(name); return llvm::None; } uint32_t PlatformRemoteGDBServer::FindProcesses( const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) { return m_gdb_client.FindProcesses(match_info, process_infos); } bool PlatformRemoteGDBServer::GetProcessInfo( lldb::pid_t pid, ProcessInstanceInfo &process_info) { return m_gdb_client.GetProcessInfo(pid, process_info); } Status PlatformRemoteGDBServer::LaunchProcess(ProcessLaunchInfo &launch_info) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); Status error; LLDB_LOGF(log, "PlatformRemoteGDBServer::%s() called", __FUNCTION__); auto num_file_actions = launch_info.GetNumFileActions(); for (decltype(num_file_actions) i = 0; i < num_file_actions; ++i) { const auto file_action = launch_info.GetFileActionAtIndex(i); if (file_action->GetAction() != FileAction::eFileActionOpen) continue; switch (file_action->GetFD()) { case STDIN_FILENO: m_gdb_client.SetSTDIN(file_action->GetFileSpec()); break; case STDOUT_FILENO: m_gdb_client.SetSTDOUT(file_action->GetFileSpec()); break; case STDERR_FILENO: m_gdb_client.SetSTDERR(file_action->GetFileSpec()); break; } } m_gdb_client.SetDisableASLR( launch_info.GetFlags().Test(eLaunchFlagDisableASLR)); m_gdb_client.SetDetachOnError( launch_info.GetFlags().Test(eLaunchFlagDetachOnError)); FileSpec working_dir = launch_info.GetWorkingDirectory(); if (working_dir) { m_gdb_client.SetWorkingDir(working_dir); } // Send the environment and the program + arguments after we connect m_gdb_client.SendEnvironment(launch_info.GetEnvironment()); ArchSpec arch_spec = launch_info.GetArchitecture(); const char *arch_triple = arch_spec.GetTriple().str().c_str(); m_gdb_client.SendLaunchArchPacket(arch_triple); LLDB_LOGF( log, "PlatformRemoteGDBServer::%s() set launch architecture triple to '%s'", __FUNCTION__, arch_triple ? arch_triple : ""); int arg_packet_err; { // Scope for the scoped timeout object process_gdb_remote::GDBRemoteCommunication::ScopedTimeout timeout( m_gdb_client, std::chrono::seconds(5)); arg_packet_err = m_gdb_client.SendArgumentsPacket(launch_info); } if (arg_packet_err == 0) { std::string error_str; if (m_gdb_client.GetLaunchSuccess(error_str)) { const auto pid = m_gdb_client.GetCurrentProcessID(false); if (pid != LLDB_INVALID_PROCESS_ID) { launch_info.SetProcessID(pid); LLDB_LOGF(log, "PlatformRemoteGDBServer::%s() pid %" PRIu64 " launched successfully", __FUNCTION__, pid); } else { LLDB_LOGF(log, "PlatformRemoteGDBServer::%s() launch succeeded but we " "didn't get a valid process id back!", __FUNCTION__); error.SetErrorString("failed to get PID"); } } else { error.SetErrorString(error_str.c_str()); LLDB_LOGF(log, "PlatformRemoteGDBServer::%s() launch failed: %s", __FUNCTION__, error.AsCString()); } } else { error.SetErrorStringWithFormat("'A' packet returned an error: %i", arg_packet_err); } return error; } Status PlatformRemoteGDBServer::KillProcess(const lldb::pid_t pid) { if (!KillSpawnedProcess(pid)) return Status("failed to kill remote spawned process"); return Status(); } lldb::ProcessSP PlatformRemoteGDBServer::DebugProcess( ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, // Can be NULL, if NULL create a new target, else use // existing one Status &error) { lldb::ProcessSP process_sp; if (IsRemote()) { if (IsConnected()) { lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; std::string connect_url; if (!LaunchGDBServer(debugserver_pid, connect_url)) { error.SetErrorStringWithFormat("unable to launch a GDB server on '%s'", GetHostname()); } else { if (target == nullptr) { TargetSP new_target_sp; error = debugger.GetTargetList().CreateTarget( debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp); target = new_target_sp.get(); } else error.Clear(); if (target && error.Success()) { debugger.GetTargetList().SetSelectedTarget(target); // The darwin always currently uses the GDB remote debugger plug-in // so even when debugging locally we are debugging remotely! process_sp = target->CreateProcess(launch_info.GetListener(), "gdb-remote", nullptr); if (process_sp) { - error = process_sp->ConnectRemote(nullptr, connect_url.c_str()); + error = process_sp->ConnectRemote(connect_url.c_str()); // Retry the connect remote one time... if (error.Fail()) - error = process_sp->ConnectRemote(nullptr, connect_url.c_str()); + error = process_sp->ConnectRemote(connect_url.c_str()); if (error.Success()) error = process_sp->Launch(launch_info); else if (debugserver_pid != LLDB_INVALID_PROCESS_ID) { printf("error: connect remote failed (%s)\n", error.AsCString()); KillSpawnedProcess(debugserver_pid); } } } } } else { error.SetErrorString("not connected to remote gdb server"); } } return process_sp; } bool PlatformRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid, std::string &connect_url) { ArchSpec remote_arch = GetRemoteSystemArchitecture(); llvm::Triple &remote_triple = remote_arch.GetTriple(); uint16_t port = 0; std::string socket_name; bool launch_result = false; if (remote_triple.getVendor() == llvm::Triple::Apple && remote_triple.getOS() == llvm::Triple::IOS) { // When remote debugging to iOS, we use a USB mux that always talks to // localhost, so we will need the remote debugserver to accept connections // only from localhost, no matter what our current hostname is launch_result = m_gdb_client.LaunchGDBServer("127.0.0.1", pid, port, socket_name); } else { // All other hosts should use their actual hostname launch_result = m_gdb_client.LaunchGDBServer(nullptr, pid, port, socket_name); } if (!launch_result) return false; connect_url = MakeGdbServerUrl(m_platform_scheme, m_platform_hostname, port, (socket_name.empty()) ? nullptr : socket_name.c_str()); return true; } bool PlatformRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) { return m_gdb_client.KillSpawnedProcess(pid); } lldb::ProcessSP PlatformRemoteGDBServer::Attach( ProcessAttachInfo &attach_info, Debugger &debugger, Target *target, // Can be NULL, if NULL create a new target, else use // existing one Status &error) { lldb::ProcessSP process_sp; if (IsRemote()) { if (IsConnected()) { lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; std::string connect_url; if (!LaunchGDBServer(debugserver_pid, connect_url)) { error.SetErrorStringWithFormat("unable to launch a GDB server on '%s'", GetHostname()); } else { if (target == nullptr) { TargetSP new_target_sp; error = debugger.GetTargetList().CreateTarget( debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp); target = new_target_sp.get(); } else error.Clear(); if (target && error.Success()) { debugger.GetTargetList().SetSelectedTarget(target); // The darwin always currently uses the GDB remote debugger plug-in // so even when debugging locally we are debugging remotely! process_sp = target->CreateProcess(attach_info.GetListenerForProcess(debugger), "gdb-remote", nullptr); if (process_sp) { - error = process_sp->ConnectRemote(nullptr, connect_url.c_str()); + error = process_sp->ConnectRemote(connect_url.c_str()); if (error.Success()) { ListenerSP listener_sp = attach_info.GetHijackListener(); if (listener_sp) process_sp->HijackProcessEvents(listener_sp); error = process_sp->Attach(attach_info); } if (error.Fail() && debugserver_pid != LLDB_INVALID_PROCESS_ID) { KillSpawnedProcess(debugserver_pid); } } } } } else { error.SetErrorString("not connected to remote gdb server"); } } return process_sp; } Status PlatformRemoteGDBServer::MakeDirectory(const FileSpec &file_spec, uint32_t mode) { Status error = m_gdb_client.MakeDirectory(file_spec, mode); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); LLDB_LOGF(log, "PlatformRemoteGDBServer::MakeDirectory(path='%s', mode=%o) " "error = %u (%s)", file_spec.GetCString(), mode, error.GetError(), error.AsCString()); return error; } Status PlatformRemoteGDBServer::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions) { Status error = m_gdb_client.GetFilePermissions(file_spec, file_permissions); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); LLDB_LOGF(log, "PlatformRemoteGDBServer::GetFilePermissions(path='%s', " "file_permissions=%o) error = %u (%s)", file_spec.GetCString(), file_permissions, error.GetError(), error.AsCString()); return error; } Status PlatformRemoteGDBServer::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions) { Status error = m_gdb_client.SetFilePermissions(file_spec, file_permissions); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); LLDB_LOGF(log, "PlatformRemoteGDBServer::SetFilePermissions(path='%s', " "file_permissions=%o) error = %u (%s)", file_spec.GetCString(), file_permissions, error.GetError(), error.AsCString()); return error; } lldb::user_id_t PlatformRemoteGDBServer::OpenFile(const FileSpec &file_spec, File::OpenOptions flags, uint32_t mode, Status &error) { return m_gdb_client.OpenFile(file_spec, flags, mode, error); } bool PlatformRemoteGDBServer::CloseFile(lldb::user_id_t fd, Status &error) { return m_gdb_client.CloseFile(fd, error); } lldb::user_id_t PlatformRemoteGDBServer::GetFileSize(const FileSpec &file_spec) { return m_gdb_client.GetFileSize(file_spec); } uint64_t PlatformRemoteGDBServer::ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst, uint64_t dst_len, Status &error) { return m_gdb_client.ReadFile(fd, offset, dst, dst_len, error); } uint64_t PlatformRemoteGDBServer::WriteFile(lldb::user_id_t fd, uint64_t offset, const void *src, uint64_t src_len, Status &error) { return m_gdb_client.WriteFile(fd, offset, src, src_len, error); } Status PlatformRemoteGDBServer::PutFile(const FileSpec &source, const FileSpec &destination, uint32_t uid, uint32_t gid) { return Platform::PutFile(source, destination, uid, gid); } Status PlatformRemoteGDBServer::CreateSymlink( const FileSpec &src, // The name of the link is in src const FileSpec &dst) // The symlink points to dst { Status error = m_gdb_client.CreateSymlink(src, dst); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); LLDB_LOGF(log, "PlatformRemoteGDBServer::CreateSymlink(src='%s', dst='%s') " "error = %u (%s)", src.GetCString(), dst.GetCString(), error.GetError(), error.AsCString()); return error; } Status PlatformRemoteGDBServer::Unlink(const FileSpec &file_spec) { Status error = m_gdb_client.Unlink(file_spec); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); LLDB_LOGF(log, "PlatformRemoteGDBServer::Unlink(path='%s') error = %u (%s)", file_spec.GetCString(), error.GetError(), error.AsCString()); return error; } bool PlatformRemoteGDBServer::GetFileExists(const FileSpec &file_spec) { return m_gdb_client.GetFileExists(file_spec); } Status PlatformRemoteGDBServer::RunShellCommand( const char *command, // Shouldn't be NULL const FileSpec & working_dir, // Pass empty FileSpec to use the current working directory int *status_ptr, // Pass NULL if you don't want the process exit status int *signo_ptr, // Pass NULL if you don't want the signal that caused the // process to exit std::string *command_output, // Pass NULL if you don't want the command output const Timeout &timeout) { return m_gdb_client.RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout); } void PlatformRemoteGDBServer::CalculateTrapHandlerSymbolNames() { m_trap_handlers.push_back(ConstString("_sigtramp")); } const UnixSignalsSP &PlatformRemoteGDBServer::GetRemoteUnixSignals() { if (!IsConnected()) return Platform::GetRemoteUnixSignals(); if (m_remote_signals_sp) return m_remote_signals_sp; // If packet not implemented or JSON failed to parse, we'll guess the signal // set based on the remote architecture. m_remote_signals_sp = UnixSignals::Create(GetRemoteSystemArchitecture()); StringExtractorGDBRemote response; auto result = m_gdb_client.SendPacketAndWaitForResponse("jSignalsInfo", response, false); if (result != decltype(result)::Success || response.GetResponseType() != response.eResponse) return m_remote_signals_sp; auto object_sp = StructuredData::ParseJSON(std::string(response.GetStringRef())); if (!object_sp || !object_sp->IsValid()) return m_remote_signals_sp; auto array_sp = object_sp->GetAsArray(); if (!array_sp || !array_sp->IsValid()) return m_remote_signals_sp; auto remote_signals_sp = std::make_shared(); bool done = array_sp->ForEach( [&remote_signals_sp](StructuredData::Object *object) -> bool { if (!object || !object->IsValid()) return false; auto dict = object->GetAsDictionary(); if (!dict || !dict->IsValid()) return false; // Signal number and signal name are required. int signo; if (!dict->GetValueForKeyAsInteger("signo", signo)) return false; llvm::StringRef name; if (!dict->GetValueForKeyAsString("name", name)) return false; // We can live without short_name, description, etc. bool suppress{false}; auto object_sp = dict->GetValueForKey("suppress"); if (object_sp && object_sp->IsValid()) suppress = object_sp->GetBooleanValue(); bool stop{false}; object_sp = dict->GetValueForKey("stop"); if (object_sp && object_sp->IsValid()) stop = object_sp->GetBooleanValue(); bool notify{false}; object_sp = dict->GetValueForKey("notify"); if (object_sp && object_sp->IsValid()) notify = object_sp->GetBooleanValue(); std::string description{""}; object_sp = dict->GetValueForKey("description"); if (object_sp && object_sp->IsValid()) description = std::string(object_sp->GetStringValue()); remote_signals_sp->AddSignal(signo, name.str().c_str(), suppress, stop, notify, description.c_str()); return true; }); if (done) m_remote_signals_sp = std::move(remote_signals_sp); return m_remote_signals_sp; } std::string PlatformRemoteGDBServer::MakeGdbServerUrl( const std::string &platform_scheme, const std::string &platform_hostname, uint16_t port, const char *socket_name) { const char *override_scheme = getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_SCHEME"); const char *override_hostname = getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_HOSTNAME"); const char *port_offset_c_str = getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_PORT_OFFSET"); int port_offset = port_offset_c_str ? ::atoi(port_offset_c_str) : 0; return MakeUrl(override_scheme ? override_scheme : platform_scheme.c_str(), override_hostname ? override_hostname : platform_hostname.c_str(), port + port_offset, socket_name); } std::string PlatformRemoteGDBServer::MakeUrl(const char *scheme, const char *hostname, uint16_t port, const char *path) { StreamString result; result.Printf("%s://%s", scheme, hostname); if (port != 0) result.Printf(":%u", port); if (path) result.Write(path, strlen(path)); return std::string(result.GetString()); } lldb::ProcessSP PlatformRemoteGDBServer::ConnectProcess( llvm::StringRef connect_url, llvm::StringRef plugin_name, lldb_private::Debugger &debugger, lldb_private::Target *target, lldb_private::Status &error) { if (!IsRemote() || !IsConnected()) { error.SetErrorString("Not connected to remote gdb server"); return nullptr; } return Platform::ConnectProcess(connect_url, plugin_name, debugger, target, error); } size_t PlatformRemoteGDBServer::ConnectToWaitingProcesses(Debugger &debugger, Status &error) { std::vector connection_urls; GetPendingGdbServerList(connection_urls); for (size_t i = 0; i < connection_urls.size(); ++i) { ConnectProcess(connection_urls[i].c_str(), "", debugger, nullptr, error); if (error.Fail()) return i; // We already connected to i process succsessfully } return connection_urls.size(); } size_t PlatformRemoteGDBServer::GetPendingGdbServerList( std::vector &connection_urls) { std::vector> remote_servers; m_gdb_client.QueryGDBServer(remote_servers); for (const auto &gdbserver : remote_servers) { const char *socket_name_cstr = gdbserver.second.empty() ? nullptr : gdbserver.second.c_str(); connection_urls.emplace_back( MakeGdbServerUrl(m_platform_scheme, m_platform_hostname, gdbserver.first, socket_name_cstr)); } return connection_urls.size(); } diff --git a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp index 5b728a5f2960..2f4a8917a78a 100644 --- a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp +++ b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp @@ -1,1032 +1,1032 @@ //===-- ProcessKDP.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 #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Host.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/common/TCPSocket.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionGroupString.h" #include "lldb/Interpreter/OptionGroupUInt64.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/LocateSymbolFile.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/State.h" #include "lldb/Utility/StringExtractor.h" #include "lldb/Utility/UUID.h" #include "llvm/Support/Threading.h" #define USEC_PER_SEC 1000000 #include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h" #include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" #include "ProcessKDP.h" #include "ProcessKDPLog.h" #include "ThreadKDP.h" using namespace lldb; using namespace lldb_private; LLDB_PLUGIN_DEFINE_ADV(ProcessKDP, ProcessMacOSXKernel) namespace { #define LLDB_PROPERTIES_processkdp #include "ProcessKDPProperties.inc" enum { #define LLDB_PROPERTIES_processkdp #include "ProcessKDPPropertiesEnum.inc" }; class PluginProperties : public Properties { public: static ConstString GetSettingName() { return ProcessKDP::GetPluginNameStatic(); } PluginProperties() : Properties() { m_collection_sp = std::make_shared(GetSettingName()); m_collection_sp->Initialize(g_processkdp_properties); } virtual ~PluginProperties() {} uint64_t GetPacketTimeout() { const uint32_t idx = ePropertyKDPPacketTimeout; return m_collection_sp->GetPropertyAtIndexAsUInt64( NULL, idx, g_processkdp_properties[idx].default_uint_value); } }; typedef std::shared_ptr ProcessKDPPropertiesSP; static const ProcessKDPPropertiesSP &GetGlobalPluginProperties() { static ProcessKDPPropertiesSP g_settings_sp; if (!g_settings_sp) g_settings_sp = std::make_shared(); return g_settings_sp; } } // anonymous namespace end static const lldb::tid_t g_kernel_tid = 1; ConstString ProcessKDP::GetPluginNameStatic() { static ConstString g_name("kdp-remote"); return g_name; } const char *ProcessKDP::GetPluginDescriptionStatic() { return "KDP Remote protocol based debugging plug-in for darwin kernel " "debugging."; } void ProcessKDP::Terminate() { PluginManager::UnregisterPlugin(ProcessKDP::CreateInstance); } lldb::ProcessSP ProcessKDP::CreateInstance(TargetSP target_sp, ListenerSP listener_sp, const FileSpec *crash_file_path) { lldb::ProcessSP process_sp; if (crash_file_path == NULL) process_sp = std::make_shared(target_sp, listener_sp); return process_sp; } bool ProcessKDP::CanDebug(TargetSP target_sp, bool plugin_specified_by_name) { if (plugin_specified_by_name) return true; // For now we are just making sure the file exists for a given module Module *exe_module = target_sp->GetExecutableModulePointer(); if (exe_module) { const llvm::Triple &triple_ref = target_sp->GetArchitecture().GetTriple(); switch (triple_ref.getOS()) { case llvm::Triple::Darwin: // Should use "macosx" for desktop and "ios" for // iOS, but accept darwin just in case case llvm::Triple::MacOSX: // For desktop targets case llvm::Triple::IOS: // For arm targets case llvm::Triple::TvOS: case llvm::Triple::WatchOS: if (triple_ref.getVendor() == llvm::Triple::Apple) { ObjectFile *exe_objfile = exe_module->GetObjectFile(); if (exe_objfile->GetType() == ObjectFile::eTypeExecutable && exe_objfile->GetStrata() == ObjectFile::eStrataKernel) return true; } break; default: break; } } return false; } // ProcessKDP constructor ProcessKDP::ProcessKDP(TargetSP target_sp, ListenerSP listener_sp) : Process(target_sp, listener_sp), m_comm("lldb.process.kdp-remote.communication"), m_async_broadcaster(NULL, "lldb.process.kdp-remote.async-broadcaster"), m_dyld_plugin_name(), m_kernel_load_addr(LLDB_INVALID_ADDRESS), m_command_sp(), m_kernel_thread_wp() { m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue, "async thread continue"); const uint64_t timeout_seconds = GetGlobalPluginProperties()->GetPacketTimeout(); if (timeout_seconds > 0) m_comm.SetPacketTimeout(std::chrono::seconds(timeout_seconds)); } // Destructor ProcessKDP::~ProcessKDP() { Clear(); // We need to call finalize on the process before destroying ourselves to // make sure all of the broadcaster cleanup goes as planned. If we destruct // this class, then Process::~Process() might have problems trying to fully // destroy the broadcaster. Finalize(); } // PluginInterface lldb_private::ConstString ProcessKDP::GetPluginName() { return GetPluginNameStatic(); } uint32_t ProcessKDP::GetPluginVersion() { return 1; } Status ProcessKDP::WillLaunch(Module *module) { Status error; error.SetErrorString("launching not supported in kdp-remote plug-in"); return error; } Status ProcessKDP::WillAttachToProcessWithID(lldb::pid_t pid) { Status error; error.SetErrorString( "attaching to a by process ID not supported in kdp-remote plug-in"); return error; } Status ProcessKDP::WillAttachToProcessWithName(const char *process_name, bool wait_for_launch) { Status error; error.SetErrorString( "attaching to a by process name not supported in kdp-remote plug-in"); return error; } bool ProcessKDP::GetHostArchitecture(ArchSpec &arch) { uint32_t cpu = m_comm.GetCPUType(); if (cpu) { uint32_t sub = m_comm.GetCPUSubtype(); arch.SetArchitecture(eArchTypeMachO, cpu, sub); // Leave architecture vendor as unspecified unknown arch.GetTriple().setVendor(llvm::Triple::UnknownVendor); arch.GetTriple().setVendorName(llvm::StringRef()); return true; } arch.Clear(); return false; } -Status ProcessKDP::DoConnectRemote(Stream *strm, llvm::StringRef remote_url) { +Status ProcessKDP::DoConnectRemote(llvm::StringRef remote_url) { Status error; // Don't let any JIT happen when doing KDP as we can't allocate memory and we // don't want to be mucking with threads that might already be handling // exceptions SetCanJIT(false); if (remote_url.empty()) { error.SetErrorStringWithFormat("empty connection URL"); return error; } std::unique_ptr conn_up( new ConnectionFileDescriptor()); if (conn_up) { // Only try once for now. // TODO: check if we should be retrying? const uint32_t max_retry_count = 1; for (uint32_t retry_count = 0; retry_count < max_retry_count; ++retry_count) { if (conn_up->Connect(remote_url, &error) == eConnectionStatusSuccess) break; usleep(100000); } } if (conn_up->IsConnected()) { const TCPSocket &socket = static_cast(*conn_up->GetReadObject()); const uint16_t reply_port = socket.GetLocalPortNumber(); if (reply_port != 0) { m_comm.SetConnection(std::move(conn_up)); if (m_comm.SendRequestReattach(reply_port)) { if (m_comm.SendRequestConnect(reply_port, reply_port, "Greetings from LLDB...")) { m_comm.GetVersion(); Target &target = GetTarget(); ArchSpec kernel_arch; // The host architecture GetHostArchitecture(kernel_arch); ArchSpec target_arch = target.GetArchitecture(); // Merge in any unspecified stuff into the target architecture in // case the target arch isn't set at all or incompletely. target_arch.MergeFrom(kernel_arch); target.SetArchitecture(target_arch); /* Get the kernel's UUID and load address via KDP_KERNELVERSION * packet. */ /* An EFI kdp session has neither UUID nor load address. */ UUID kernel_uuid = m_comm.GetUUID(); addr_t kernel_load_addr = m_comm.GetLoadAddress(); if (m_comm.RemoteIsEFI()) { // Select an invalid plugin name for the dynamic loader so one // doesn't get used since EFI does its own manual loading via // python scripting static ConstString g_none_dynamic_loader("none"); m_dyld_plugin_name = g_none_dynamic_loader; if (kernel_uuid.IsValid()) { // If EFI passed in a UUID= try to lookup UUID The slide will not // be provided. But the UUID lookup will be used to launch EFI // debug scripts from the dSYM, that can load all of the symbols. ModuleSpec module_spec; module_spec.GetUUID() = kernel_uuid; module_spec.GetArchitecture() = target.GetArchitecture(); // Lookup UUID locally, before attempting dsymForUUID like action FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths(); module_spec.GetSymbolFileSpec() = Symbols::LocateExecutableSymbolFile(module_spec, search_paths); if (module_spec.GetSymbolFileSpec()) { ModuleSpec executable_module_spec = Symbols::LocateExecutableObjectFile(module_spec); if (FileSystem::Instance().Exists( executable_module_spec.GetFileSpec())) { module_spec.GetFileSpec() = executable_module_spec.GetFileSpec(); } } if (!module_spec.GetSymbolFileSpec() || !module_spec.GetSymbolFileSpec()) Symbols::DownloadObjectAndSymbolFile(module_spec, true); if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) { ModuleSP module_sp(new Module(module_spec)); if (module_sp.get() && module_sp->GetObjectFile()) { // Get the current target executable ModuleSP exe_module_sp(target.GetExecutableModule()); // Make sure you don't already have the right module loaded // and they will be uniqued if (exe_module_sp.get() != module_sp.get()) target.SetExecutableModule(module_sp, eLoadDependentsNo); } } } } else if (m_comm.RemoteIsDarwinKernel()) { m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); if (kernel_load_addr != LLDB_INVALID_ADDRESS) { m_kernel_load_addr = kernel_load_addr; } } // Set the thread ID UpdateThreadListIfNeeded(); SetID(1); GetThreadList(); SetPrivateState(eStateStopped); StreamSP async_strm_sp(target.GetDebugger().GetAsyncOutputStream()); if (async_strm_sp) { const char *cstr; if ((cstr = m_comm.GetKernelVersion()) != NULL) { async_strm_sp->Printf("Version: %s\n", cstr); async_strm_sp->Flush(); } // if ((cstr = m_comm.GetImagePath ()) != NULL) // { // async_strm_sp->Printf ("Image Path: // %s\n", cstr); // async_strm_sp->Flush(); // } } } else { error.SetErrorString("KDP_REATTACH failed"); } } else { error.SetErrorString("KDP_REATTACH failed"); } } else { error.SetErrorString("invalid reply port from UDP connection"); } } else { if (error.Success()) error.SetErrorStringWithFormat("failed to connect to '%s'", remote_url.str().c_str()); } if (error.Fail()) m_comm.Disconnect(); return error; } // Process Control Status ProcessKDP::DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) { Status error; error.SetErrorString("launching not supported in kdp-remote plug-in"); return error; } Status ProcessKDP::DoAttachToProcessWithID(lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) { Status error; error.SetErrorString( "attach to process by ID is not supported in kdp remote debugging"); return error; } Status ProcessKDP::DoAttachToProcessWithName(const char *process_name, const ProcessAttachInfo &attach_info) { Status error; error.SetErrorString( "attach to process by name is not supported in kdp remote debugging"); return error; } void ProcessKDP::DidAttach(ArchSpec &process_arch) { Process::DidAttach(process_arch); Log *log(ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); LLDB_LOGF(log, "ProcessKDP::DidAttach()"); if (GetID() != LLDB_INVALID_PROCESS_ID) { GetHostArchitecture(process_arch); } } addr_t ProcessKDP::GetImageInfoAddress() { return m_kernel_load_addr; } lldb_private::DynamicLoader *ProcessKDP::GetDynamicLoader() { if (m_dyld_up.get() == NULL) m_dyld_up.reset(DynamicLoader::FindPlugin( this, m_dyld_plugin_name.IsEmpty() ? NULL : m_dyld_plugin_name.GetCString())); return m_dyld_up.get(); } Status ProcessKDP::WillResume() { return Status(); } Status ProcessKDP::DoResume() { Status error; Log *log(ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); // Only start the async thread if we try to do any process control if (!m_async_thread.IsJoinable()) StartAsyncThread(); bool resume = false; // With KDP there is only one thread we can tell what to do ThreadSP kernel_thread_sp(m_thread_list.FindThreadByProtocolID(g_kernel_tid)); if (kernel_thread_sp) { const StateType thread_resume_state = kernel_thread_sp->GetTemporaryResumeState(); LLDB_LOGF(log, "ProcessKDP::DoResume() thread_resume_state = %s", StateAsCString(thread_resume_state)); switch (thread_resume_state) { case eStateSuspended: // Nothing to do here when a thread will stay suspended we just leave the // CPU mask bit set to zero for the thread LLDB_LOGF(log, "ProcessKDP::DoResume() = suspended???"); break; case eStateStepping: { lldb::RegisterContextSP reg_ctx_sp( kernel_thread_sp->GetRegisterContext()); if (reg_ctx_sp) { LLDB_LOGF( log, "ProcessKDP::DoResume () reg_ctx_sp->HardwareSingleStep (true);"); reg_ctx_sp->HardwareSingleStep(true); resume = true; } else { error.SetErrorStringWithFormat( "KDP thread 0x%llx has no register context", kernel_thread_sp->GetID()); } } break; case eStateRunning: { lldb::RegisterContextSP reg_ctx_sp( kernel_thread_sp->GetRegisterContext()); if (reg_ctx_sp) { LLDB_LOGF(log, "ProcessKDP::DoResume () reg_ctx_sp->HardwareSingleStep " "(false);"); reg_ctx_sp->HardwareSingleStep(false); resume = true; } else { error.SetErrorStringWithFormat( "KDP thread 0x%llx has no register context", kernel_thread_sp->GetID()); } } break; default: // The only valid thread resume states are listed above llvm_unreachable("invalid thread resume state"); } } if (resume) { LLDB_LOGF(log, "ProcessKDP::DoResume () sending resume"); if (m_comm.SendRequestResume()) { m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue); SetPrivateState(eStateRunning); } else error.SetErrorString("KDP resume failed"); } else { error.SetErrorString("kernel thread is suspended"); } return error; } lldb::ThreadSP ProcessKDP::GetKernelThread() { // KDP only tells us about one thread/core. Any other threads will usually // be the ones that are read from memory by the OS plug-ins. ThreadSP thread_sp(m_kernel_thread_wp.lock()); if (!thread_sp) { thread_sp = std::make_shared(*this, g_kernel_tid); m_kernel_thread_wp = thread_sp; } return thread_sp; } bool ProcessKDP::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) { // locker will keep a mutex locked until it goes out of scope Log *log(ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_THREAD)); LLDB_LOGV(log, "pid = {0}", GetID()); // Even though there is a CPU mask, it doesn't mean we can see each CPU // individually, there is really only one. Lets call this thread 1. ThreadSP thread_sp( old_thread_list.FindThreadByProtocolID(g_kernel_tid, false)); if (!thread_sp) thread_sp = GetKernelThread(); new_thread_list.AddThread(thread_sp); return new_thread_list.GetSize(false) > 0; } void ProcessKDP::RefreshStateAfterStop() { // Let all threads recover from stopping and do any clean up based on the // previous thread state (if any). m_thread_list.RefreshStateAfterStop(); } Status ProcessKDP::DoHalt(bool &caused_stop) { Status error; if (m_comm.IsRunning()) { if (m_destroy_in_process) { // If we are attempting to destroy, we need to not return an error to Halt // or DoDestroy won't get called. We are also currently running, so send // a process stopped event SetPrivateState(eStateStopped); } else { error.SetErrorString("KDP cannot interrupt a running kernel"); } } return error; } Status ProcessKDP::DoDetach(bool keep_stopped) { Status error; Log *log(ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); LLDB_LOGF(log, "ProcessKDP::DoDetach(keep_stopped = %i)", keep_stopped); if (m_comm.IsRunning()) { // We are running and we can't interrupt a running kernel, so we need to // just close the connection to the kernel and hope for the best } else { // If we are going to keep the target stopped, then don't send the // disconnect message. if (!keep_stopped && m_comm.IsConnected()) { const bool success = m_comm.SendRequestDisconnect(); if (log) { if (success) log->PutCString( "ProcessKDP::DoDetach() detach packet sent successfully"); else log->PutCString( "ProcessKDP::DoDetach() connection channel shutdown failed"); } m_comm.Disconnect(); } } StopAsyncThread(); m_comm.Clear(); SetPrivateState(eStateDetached); ResumePrivateStateThread(); // KillDebugserverProcess (); return error; } Status ProcessKDP::DoDestroy() { // For KDP there really is no difference between destroy and detach bool keep_stopped = false; return DoDetach(keep_stopped); } // Process Queries bool ProcessKDP::IsAlive() { return m_comm.IsConnected() && Process::IsAlive(); } // Process Memory size_t ProcessKDP::DoReadMemory(addr_t addr, void *buf, size_t size, Status &error) { uint8_t *data_buffer = (uint8_t *)buf; if (m_comm.IsConnected()) { const size_t max_read_size = 512; size_t total_bytes_read = 0; // Read the requested amount of memory in 512 byte chunks while (total_bytes_read < size) { size_t bytes_to_read_this_request = size - total_bytes_read; if (bytes_to_read_this_request > max_read_size) { bytes_to_read_this_request = max_read_size; } size_t bytes_read = m_comm.SendRequestReadMemory( addr + total_bytes_read, data_buffer + total_bytes_read, bytes_to_read_this_request, error); total_bytes_read += bytes_read; if (error.Fail() || bytes_read == 0) { return total_bytes_read; } } return total_bytes_read; } error.SetErrorString("not connected"); return 0; } size_t ProcessKDP::DoWriteMemory(addr_t addr, const void *buf, size_t size, Status &error) { if (m_comm.IsConnected()) return m_comm.SendRequestWriteMemory(addr, buf, size, error); error.SetErrorString("not connected"); return 0; } lldb::addr_t ProcessKDP::DoAllocateMemory(size_t size, uint32_t permissions, Status &error) { error.SetErrorString( "memory allocation not supported in kdp remote debugging"); return LLDB_INVALID_ADDRESS; } Status ProcessKDP::DoDeallocateMemory(lldb::addr_t addr) { Status error; error.SetErrorString( "memory deallocation not supported in kdp remote debugging"); return error; } Status ProcessKDP::EnableBreakpointSite(BreakpointSite *bp_site) { if (m_comm.LocalBreakpointsAreSupported()) { Status error; if (!bp_site->IsEnabled()) { if (m_comm.SendRequestBreakpoint(true, bp_site->GetLoadAddress())) { bp_site->SetEnabled(true); bp_site->SetType(BreakpointSite::eExternal); } else { error.SetErrorString("KDP set breakpoint failed"); } } return error; } return EnableSoftwareBreakpoint(bp_site); } Status ProcessKDP::DisableBreakpointSite(BreakpointSite *bp_site) { if (m_comm.LocalBreakpointsAreSupported()) { Status error; if (bp_site->IsEnabled()) { BreakpointSite::Type bp_type = bp_site->GetType(); if (bp_type == BreakpointSite::eExternal) { if (m_destroy_in_process && m_comm.IsRunning()) { // We are trying to destroy our connection and we are running bp_site->SetEnabled(false); } else { if (m_comm.SendRequestBreakpoint(false, bp_site->GetLoadAddress())) bp_site->SetEnabled(false); else error.SetErrorString("KDP remove breakpoint failed"); } } else { error = DisableSoftwareBreakpoint(bp_site); } } return error; } return DisableSoftwareBreakpoint(bp_site); } Status ProcessKDP::EnableWatchpoint(Watchpoint *wp, bool notify) { Status error; error.SetErrorString( "watchpoints are not supported in kdp remote debugging"); return error; } Status ProcessKDP::DisableWatchpoint(Watchpoint *wp, bool notify) { Status error; error.SetErrorString( "watchpoints are not supported in kdp remote debugging"); return error; } void ProcessKDP::Clear() { m_thread_list.Clear(); } Status ProcessKDP::DoSignal(int signo) { Status error; error.SetErrorString( "sending signals is not supported in kdp remote debugging"); return error; } void ProcessKDP::Initialize() { static llvm::once_flag g_once_flag; llvm::call_once(g_once_flag, []() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, DebuggerInitialize); ProcessKDPLog::Initialize(); }); } void ProcessKDP::DebuggerInitialize(lldb_private::Debugger &debugger) { if (!PluginManager::GetSettingForProcessPlugin( debugger, PluginProperties::GetSettingName())) { const bool is_global_setting = true; PluginManager::CreateSettingForProcessPlugin( debugger, GetGlobalPluginProperties()->GetValueProperties(), ConstString("Properties for the kdp-remote process plug-in."), is_global_setting); } } bool ProcessKDP::StartAsyncThread() { Log *log(ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); LLDB_LOGF(log, "ProcessKDP::StartAsyncThread ()"); if (m_async_thread.IsJoinable()) return true; llvm::Expected async_thread = ThreadLauncher::LaunchThread( "", ProcessKDP::AsyncThread, this); if (!async_thread) { LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST), "failed to launch host thread: {}", llvm::toString(async_thread.takeError())); return false; } m_async_thread = *async_thread; return m_async_thread.IsJoinable(); } void ProcessKDP::StopAsyncThread() { Log *log(ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); LLDB_LOGF(log, "ProcessKDP::StopAsyncThread ()"); m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncThreadShouldExit); // Stop the stdio thread if (m_async_thread.IsJoinable()) m_async_thread.Join(nullptr); } void *ProcessKDP::AsyncThread(void *arg) { ProcessKDP *process = (ProcessKDP *)arg; const lldb::pid_t pid = process->GetID(); Log *log(ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); LLDB_LOGF(log, "ProcessKDP::AsyncThread (arg = %p, pid = %" PRIu64 ") thread starting...", arg, pid); ListenerSP listener_sp(Listener::MakeListener("ProcessKDP::AsyncThread")); EventSP event_sp; const uint32_t desired_event_mask = eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit; if (listener_sp->StartListeningForEvents(&process->m_async_broadcaster, desired_event_mask) == desired_event_mask) { bool done = false; while (!done) { LLDB_LOGF(log, "ProcessKDP::AsyncThread (pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp)...", pid); if (listener_sp->GetEvent(event_sp, llvm::None)) { uint32_t event_type = event_sp->GetType(); LLDB_LOGF(log, "ProcessKDP::AsyncThread (pid = %" PRIu64 ") Got an event of type: %d...", pid, event_type); // When we are running, poll for 1 second to try and get an exception // to indicate the process has stopped. If we don't get one, check to // make sure no one asked us to exit bool is_running = false; DataExtractor exc_reply_packet; do { switch (event_type) { case eBroadcastBitAsyncContinue: { is_running = true; if (process->m_comm.WaitForPacketWithTimeoutMicroSeconds( exc_reply_packet, 1 * USEC_PER_SEC)) { ThreadSP thread_sp(process->GetKernelThread()); if (thread_sp) { lldb::RegisterContextSP reg_ctx_sp( thread_sp->GetRegisterContext()); if (reg_ctx_sp) reg_ctx_sp->InvalidateAllRegisters(); static_cast(thread_sp.get()) ->SetStopInfoFrom_KDP_EXCEPTION(exc_reply_packet); } // TODO: parse the stop reply packet is_running = false; process->SetPrivateState(eStateStopped); } else { // Check to see if we are supposed to exit. There is no way to // interrupt a running kernel, so all we can do is wait for an // exception or detach... if (listener_sp->GetEvent(event_sp, std::chrono::microseconds(0))) { // We got an event, go through the loop again event_type = event_sp->GetType(); } } } break; case eBroadcastBitAsyncThreadShouldExit: LLDB_LOGF(log, "ProcessKDP::AsyncThread (pid = %" PRIu64 ") got eBroadcastBitAsyncThreadShouldExit...", pid); done = true; is_running = false; break; default: LLDB_LOGF(log, "ProcessKDP::AsyncThread (pid = %" PRIu64 ") got unknown event 0x%8.8x", pid, event_type); done = true; is_running = false; break; } } while (is_running); } else { LLDB_LOGF(log, "ProcessKDP::AsyncThread (pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp) => false", pid); done = true; } } } LLDB_LOGF(log, "ProcessKDP::AsyncThread (arg = %p, pid = %" PRIu64 ") thread exiting...", arg, pid); process->m_async_thread.Reset(); return NULL; } class CommandObjectProcessKDPPacketSend : public CommandObjectParsed { private: OptionGroupOptions m_option_group; OptionGroupUInt64 m_command_byte; OptionGroupString m_packet_data; virtual Options *GetOptions() { return &m_option_group; } public: CommandObjectProcessKDPPacketSend(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process plugin packet send", "Send a custom packet through the KDP protocol by " "specifying the command byte and the packet " "payload data. A packet will be sent with a " "correct header and payload, and the raw result " "bytes will be displayed as a string value. ", NULL), m_option_group(), m_command_byte(LLDB_OPT_SET_1, true, "command", 'c', 0, eArgTypeNone, "Specify the command byte to use when sending the KDP " "request packet.", 0), m_packet_data(LLDB_OPT_SET_1, false, "payload", 'p', 0, eArgTypeNone, "Specify packet payload bytes as a hex ASCII string with " "no spaces or hex prefixes.", NULL) { m_option_group.Append(&m_command_byte, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_packet_data, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectProcessKDPPacketSend() {} bool DoExecute(Args &command, CommandReturnObject &result) { const size_t argc = command.GetArgumentCount(); if (argc == 0) { if (!m_command_byte.GetOptionValue().OptionWasSet()) { result.AppendError( "the --command option must be set to a valid command byte"); result.SetStatus(eReturnStatusFailed); } else { const uint64_t command_byte = m_command_byte.GetOptionValue().GetUInt64Value(0); if (command_byte > 0 && command_byte <= UINT8_MAX) { ProcessKDP *process = (ProcessKDP *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { const StateType state = process->GetState(); if (StateIsStoppedState(state, true)) { std::vector payload_bytes; const char *ascii_hex_bytes_cstr = m_packet_data.GetOptionValue().GetCurrentValue(); if (ascii_hex_bytes_cstr && ascii_hex_bytes_cstr[0]) { StringExtractor extractor(ascii_hex_bytes_cstr); const size_t ascii_hex_bytes_cstr_len = extractor.GetStringRef().size(); if (ascii_hex_bytes_cstr_len & 1) { result.AppendErrorWithFormat("payload data must contain an " "even number of ASCII hex " "characters: '%s'", ascii_hex_bytes_cstr); result.SetStatus(eReturnStatusFailed); return false; } payload_bytes.resize(ascii_hex_bytes_cstr_len / 2); if (extractor.GetHexBytes(payload_bytes, '\xdd') != payload_bytes.size()) { result.AppendErrorWithFormat("payload data must only contain " "ASCII hex characters (no " "spaces or hex prefixes): '%s'", ascii_hex_bytes_cstr); result.SetStatus(eReturnStatusFailed); return false; } } Status error; DataExtractor reply; process->GetCommunication().SendRawRequest( command_byte, payload_bytes.empty() ? NULL : payload_bytes.data(), payload_bytes.size(), reply, error); if (error.Success()) { // Copy the binary bytes into a hex ASCII string for the result StreamString packet; packet.PutBytesAsRawHex8( reply.GetDataStart(), reply.GetByteSize(), endian::InlHostByteOrder(), endian::InlHostByteOrder()); result.AppendMessage(packet.GetString()); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } else { const char *error_cstr = error.AsCString(); if (error_cstr && error_cstr[0]) result.AppendError(error_cstr); else result.AppendErrorWithFormat("unknown error 0x%8.8x", error.GetError()); result.SetStatus(eReturnStatusFailed); return false; } } else { result.AppendErrorWithFormat("process must be stopped in order " "to send KDP packets, state is %s", StateAsCString(state)); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError("invalid process"); result.SetStatus(eReturnStatusFailed); } } else { result.AppendErrorWithFormat("invalid command byte 0x%" PRIx64 ", valid values are 1 - 255", command_byte); result.SetStatus(eReturnStatusFailed); } } } else { result.AppendErrorWithFormat("'%s' takes no arguments, only options.", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); } return false; } }; class CommandObjectProcessKDPPacket : public CommandObjectMultiword { private: public: CommandObjectProcessKDPPacket(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "process plugin packet", "Commands that deal with KDP remote packets.", NULL) { LoadSubCommand( "send", CommandObjectSP(new CommandObjectProcessKDPPacketSend(interpreter))); } ~CommandObjectProcessKDPPacket() {} }; class CommandObjectMultiwordProcessKDP : public CommandObjectMultiword { public: CommandObjectMultiwordProcessKDP(CommandInterpreter &interpreter) : CommandObjectMultiword( interpreter, "process plugin", "Commands for operating on a ProcessKDP process.", "process plugin []") { LoadSubCommand("packet", CommandObjectSP(new CommandObjectProcessKDPPacket( interpreter))); } ~CommandObjectMultiwordProcessKDP() {} }; CommandObject *ProcessKDP::GetPluginCommandObject() { if (!m_command_sp) m_command_sp = std::make_shared( GetTarget().GetDebugger().GetCommandInterpreter()); return m_command_sp.get(); } diff --git a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h index 67f8ac069820..52af56134404 100644 --- a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h +++ b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h @@ -1,193 +1,192 @@ //===-- ProcessKDP.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_SOURCE_PLUGINS_PROCESS_MACOSX_KERNEL_PROCESSKDP_H #define LLDB_SOURCE_PLUGINS_PROCESS_MACOSX_KERNEL_PROCESSKDP_H #include #include #include "lldb/Core/ThreadSafeValue.h" #include "lldb/Host/HostThread.h" #include "lldb/Target/Process.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/Broadcaster.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/StringList.h" #include "CommunicationKDP.h" class ThreadKDP; class ProcessKDP : public lldb_private::Process { public: // Constructors and Destructors static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const lldb_private::FileSpec *crash_file_path); static void Initialize(); static void DebuggerInitialize(lldb_private::Debugger &debugger); static void Terminate(); static lldb_private::ConstString GetPluginNameStatic(); static const char *GetPluginDescriptionStatic(); // Constructors and Destructors ProcessKDP(lldb::TargetSP target_sp, lldb::ListenerSP listener); ~ProcessKDP() override; // Check if a given Process bool CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) override; lldb_private::CommandObject *GetPluginCommandObject() override; // Creating a new process, or attaching to an existing one lldb_private::Status WillLaunch(lldb_private::Module *module) override; lldb_private::Status DoLaunch(lldb_private::Module *exe_module, lldb_private::ProcessLaunchInfo &launch_info) override; lldb_private::Status WillAttachToProcessWithID(lldb::pid_t pid) override; lldb_private::Status WillAttachToProcessWithName(const char *process_name, bool wait_for_launch) override; - lldb_private::Status DoConnectRemote(lldb_private::Stream *strm, - llvm::StringRef remote_url) override; + lldb_private::Status DoConnectRemote(llvm::StringRef remote_url) override; lldb_private::Status DoAttachToProcessWithID( lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info) override; lldb_private::Status DoAttachToProcessWithName( const char *process_name, const lldb_private::ProcessAttachInfo &attach_info) override; void DidAttach(lldb_private::ArchSpec &process_arch) override; lldb::addr_t GetImageInfoAddress() override; lldb_private::DynamicLoader *GetDynamicLoader() override; // PluginInterface protocol lldb_private::ConstString GetPluginName() override; uint32_t GetPluginVersion() override; // Process Control lldb_private::Status WillResume() override; lldb_private::Status DoResume() override; lldb_private::Status DoHalt(bool &caused_stop) override; lldb_private::Status DoDetach(bool keep_stopped) override; lldb_private::Status DoSignal(int signal) override; lldb_private::Status DoDestroy() override; void RefreshStateAfterStop() override; // Process Queries bool IsAlive() override; // Process Memory size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, lldb_private::Status &error) override; size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size, lldb_private::Status &error) override; lldb::addr_t DoAllocateMemory(size_t size, uint32_t permissions, lldb_private::Status &error) override; lldb_private::Status DoDeallocateMemory(lldb::addr_t ptr) override; // Process Breakpoints lldb_private::Status EnableBreakpointSite(lldb_private::BreakpointSite *bp_site) override; lldb_private::Status DisableBreakpointSite(lldb_private::BreakpointSite *bp_site) override; // Process Watchpoints lldb_private::Status EnableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true) override; lldb_private::Status DisableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true) override; CommunicationKDP &GetCommunication() { return m_comm; } protected: friend class ThreadKDP; friend class CommunicationKDP; // Accessors bool IsRunning(lldb::StateType state) { return state == lldb::eStateRunning || IsStepping(state); } bool IsStepping(lldb::StateType state) { return state == lldb::eStateStepping; } bool CanResume(lldb::StateType state) { return state == lldb::eStateStopped; } bool HasExited(lldb::StateType state) { return state == lldb::eStateExited; } bool GetHostArchitecture(lldb_private::ArchSpec &arch); bool ProcessIDIsValid() const; void Clear(); bool UpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list) override; enum { eBroadcastBitAsyncContinue = (1 << 0), eBroadcastBitAsyncThreadShouldExit = (1 << 1) }; lldb::ThreadSP GetKernelThread(); /// Broadcaster event bits definitions. CommunicationKDP m_comm; lldb_private::Broadcaster m_async_broadcaster; lldb_private::HostThread m_async_thread; lldb_private::ConstString m_dyld_plugin_name; lldb::addr_t m_kernel_load_addr; lldb::CommandObjectSP m_command_sp; lldb::ThreadWP m_kernel_thread_wp; bool StartAsyncThread(); void StopAsyncThread(); static void *AsyncThread(void *arg); private: // For ProcessKDP only ProcessKDP(const ProcessKDP &) = delete; const ProcessKDP &operator=(const ProcessKDP &) = delete; }; #endif // LLDB_SOURCE_PLUGINS_PROCESS_MACOSX_KERNEL_PROCESSKDP_H diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index ff263fa16258..1fed8e064267 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -1,5383 +1,5382 @@ //===-- ProcessGDBRemote.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/Host/Config.h" #include #include #if LLDB_ENABLE_POSIX #include #include #include #include #endif #include #if defined(__APPLE__) #include #endif #include #include #include #include #include #include #include #include #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/StreamFile.h" #include "lldb/Core/Value.h" #include "lldb/DataFormatters/FormatManager.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/HostThread.h" #include "lldb/Host/PosixApi.h" #include "lldb/Host/PseudoTerminal.h" #include "lldb/Host/StringConvert.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/XML.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Interpreter/OptionGroupBoolean.h" #include "lldb/Interpreter/OptionGroupUInt64.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/Options.h" #include "lldb/Interpreter/Property.h" #include "lldb/Symbol/LocateSymbolFile.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/ABI.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/TargetList.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Reproducer.h" #include "lldb/Utility/State.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/Timer.h" #include "GDBRemoteRegisterContext.h" #include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h" #include "Plugins/Process/Utility/GDBRemoteSignals.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" #include "Plugins/Process/Utility/StopInfoMachException.h" #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "ThreadGDBRemote.h" #include "lldb/Host/Host.h" #include "lldb/Utility/StringExtractorGDBRemote.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Threading.h" #include "llvm/Support/raw_ostream.h" #define DEBUGSERVER_BASENAME "debugserver" using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_gdb_remote; LLDB_PLUGIN_DEFINE(ProcessGDBRemote) namespace lldb { // Provide a function that can easily dump the packet history if we know a // ProcessGDBRemote * value (which we can get from logs or from debugging). We // need the function in the lldb namespace so it makes it into the final // executable since the LLDB shared library only exports stuff in the lldb // namespace. This allows you to attach with a debugger and call this function // and get the packet history dumped to a file. void DumpProcessGDBRemotePacketHistory(void *p, const char *path) { auto file = FileSystem::Instance().Open( FileSpec(path), File::eOpenOptionWrite | File::eOpenOptionCanCreate); if (!file) { llvm::consumeError(file.takeError()); return; } StreamFile stream(std::move(file.get())); ((ProcessGDBRemote *)p)->GetGDBRemote().DumpHistory(stream); } } // namespace lldb namespace { #define LLDB_PROPERTIES_processgdbremote #include "ProcessGDBRemoteProperties.inc" enum { #define LLDB_PROPERTIES_processgdbremote #include "ProcessGDBRemotePropertiesEnum.inc" }; class PluginProperties : public Properties { public: static ConstString GetSettingName() { return ProcessGDBRemote::GetPluginNameStatic(); } PluginProperties() : Properties() { m_collection_sp = std::make_shared(GetSettingName()); m_collection_sp->Initialize(g_processgdbremote_properties); } ~PluginProperties() override {} uint64_t GetPacketTimeout() { const uint32_t idx = ePropertyPacketTimeout; return m_collection_sp->GetPropertyAtIndexAsUInt64( nullptr, idx, g_processgdbremote_properties[idx].default_uint_value); } bool SetPacketTimeout(uint64_t timeout) { const uint32_t idx = ePropertyPacketTimeout; return m_collection_sp->SetPropertyAtIndexAsUInt64(nullptr, idx, timeout); } FileSpec GetTargetDefinitionFile() const { const uint32_t idx = ePropertyTargetDefinitionFile; return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); } bool GetUseSVR4() const { const uint32_t idx = ePropertyUseSVR4; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_processgdbremote_properties[idx].default_uint_value != 0); } bool GetUseGPacketForReading() const { const uint32_t idx = ePropertyUseGPacketForReading; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true); } }; typedef std::shared_ptr ProcessKDPPropertiesSP; static const ProcessKDPPropertiesSP &GetGlobalPluginProperties() { static ProcessKDPPropertiesSP g_settings_sp; if (!g_settings_sp) g_settings_sp = std::make_shared(); return g_settings_sp; } } // namespace // TODO Randomly assigning a port is unsafe. We should get an unused // ephemeral port from the kernel and make sure we reserve it before passing it // to debugserver. #if defined(__APPLE__) #define LOW_PORT (IPPORT_RESERVED) #define HIGH_PORT (IPPORT_HIFIRSTAUTO) #else #define LOW_PORT (1024u) #define HIGH_PORT (49151u) #endif ConstString ProcessGDBRemote::GetPluginNameStatic() { static ConstString g_name("gdb-remote"); return g_name; } const char *ProcessGDBRemote::GetPluginDescriptionStatic() { return "GDB Remote protocol based debugging plug-in."; } void ProcessGDBRemote::Terminate() { PluginManager::UnregisterPlugin(ProcessGDBRemote::CreateInstance); } lldb::ProcessSP ProcessGDBRemote::CreateInstance(lldb::TargetSP target_sp, ListenerSP listener_sp, const FileSpec *crash_file_path) { lldb::ProcessSP process_sp; if (crash_file_path == nullptr) process_sp = std::make_shared(target_sp, listener_sp); return process_sp; } bool ProcessGDBRemote::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) { if (plugin_specified_by_name) return true; // For now we are just making sure the file exists for a given module Module *exe_module = target_sp->GetExecutableModulePointer(); if (exe_module) { ObjectFile *exe_objfile = exe_module->GetObjectFile(); // We can't debug core files... switch (exe_objfile->GetType()) { case ObjectFile::eTypeInvalid: case ObjectFile::eTypeCoreFile: case ObjectFile::eTypeDebugInfo: case ObjectFile::eTypeObjectFile: case ObjectFile::eTypeSharedLibrary: case ObjectFile::eTypeStubLibrary: case ObjectFile::eTypeJIT: return false; case ObjectFile::eTypeExecutable: case ObjectFile::eTypeDynamicLinker: case ObjectFile::eTypeUnknown: break; } return FileSystem::Instance().Exists(exe_module->GetFileSpec()); } // However, if there is no executable module, we return true since we might // be preparing to attach. return true; } // ProcessGDBRemote constructor ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp, ListenerSP listener_sp) : Process(target_sp, listener_sp), m_debugserver_pid(LLDB_INVALID_PROCESS_ID), m_last_stop_packet_mutex(), m_register_info(), m_async_broadcaster(nullptr, "lldb.process.gdb-remote.async-broadcaster"), m_async_listener_sp( Listener::MakeListener("lldb.process.gdb-remote.async-listener")), m_async_thread_state_mutex(), m_thread_ids(), m_thread_pcs(), m_jstopinfo_sp(), m_jthreadsinfo_sp(), m_continue_c_tids(), m_continue_C_tids(), m_continue_s_tids(), m_continue_S_tids(), m_max_memory_size(0), m_remote_stub_max_memory_size(0), m_addr_to_mmap_size(), m_thread_create_bp_sp(), m_waiting_for_attach(false), m_destroy_tried_resuming(false), m_command_sp(), m_breakpoint_pc_offset(0), m_initial_tid(LLDB_INVALID_THREAD_ID), m_replay_mode(false), m_allow_flash_writes(false), m_erased_flash_ranges() { m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue, "async thread continue"); m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadDidExit, "async thread did exit"); if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) { repro::GDBRemoteProvider &provider = g->GetOrCreate(); m_gdb_comm.SetPacketRecorder(provider.GetNewPacketRecorder()); } Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_ASYNC)); const uint32_t async_event_mask = eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit; if (m_async_listener_sp->StartListeningForEvents( &m_async_broadcaster, async_event_mask) != async_event_mask) { LLDB_LOGF(log, "ProcessGDBRemote::%s failed to listen for " "m_async_broadcaster events", __FUNCTION__); } const uint32_t gdb_event_mask = Communication::eBroadcastBitReadThreadDidExit | GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify; if (m_async_listener_sp->StartListeningForEvents( &m_gdb_comm, gdb_event_mask) != gdb_event_mask) { LLDB_LOGF(log, "ProcessGDBRemote::%s failed to listen for m_gdb_comm events", __FUNCTION__); } const uint64_t timeout_seconds = GetGlobalPluginProperties()->GetPacketTimeout(); if (timeout_seconds > 0) m_gdb_comm.SetPacketTimeout(std::chrono::seconds(timeout_seconds)); m_use_g_packet_for_reading = GetGlobalPluginProperties()->GetUseGPacketForReading(); } // Destructor ProcessGDBRemote::~ProcessGDBRemote() { // m_mach_process.UnregisterNotificationCallbacks (this); Clear(); // We need to call finalize on the process before destroying ourselves to // make sure all of the broadcaster cleanup goes as planned. If we destruct // this class, then Process::~Process() might have problems trying to fully // destroy the broadcaster. Finalize(); // The general Finalize is going to try to destroy the process and that // SHOULD shut down the async thread. However, if we don't kill it it will // get stranded and its connection will go away so when it wakes up it will // crash. So kill it for sure here. StopAsyncThread(); KillDebugserverProcess(); } // PluginInterface ConstString ProcessGDBRemote::GetPluginName() { return GetPluginNameStatic(); } uint32_t ProcessGDBRemote::GetPluginVersion() { return 1; } bool ProcessGDBRemote::ParsePythonTargetDefinition( const FileSpec &target_definition_fspec) { ScriptInterpreter *interpreter = GetTarget().GetDebugger().GetScriptInterpreter(); Status error; StructuredData::ObjectSP module_object_sp( interpreter->LoadPluginModule(target_definition_fspec, error)); if (module_object_sp) { StructuredData::DictionarySP target_definition_sp( interpreter->GetDynamicSettings(module_object_sp, &GetTarget(), "gdb-server-target-definition", error)); if (target_definition_sp) { StructuredData::ObjectSP target_object( target_definition_sp->GetValueForKey("host-info")); if (target_object) { if (auto host_info_dict = target_object->GetAsDictionary()) { StructuredData::ObjectSP triple_value = host_info_dict->GetValueForKey("triple"); if (auto triple_string_value = triple_value->GetAsString()) { std::string triple_string = std::string(triple_string_value->GetValue()); ArchSpec host_arch(triple_string.c_str()); if (!host_arch.IsCompatibleMatch(GetTarget().GetArchitecture())) { GetTarget().SetArchitecture(host_arch); } } } } m_breakpoint_pc_offset = 0; StructuredData::ObjectSP breakpoint_pc_offset_value = target_definition_sp->GetValueForKey("breakpoint-pc-offset"); if (breakpoint_pc_offset_value) { if (auto breakpoint_pc_int_value = breakpoint_pc_offset_value->GetAsInteger()) m_breakpoint_pc_offset = breakpoint_pc_int_value->GetValue(); } if (m_register_info.SetRegisterInfo(*target_definition_sp, GetTarget().GetArchitecture()) > 0) { return true; } } } return false; } static size_t SplitCommaSeparatedRegisterNumberString( const llvm::StringRef &comma_separated_regiter_numbers, std::vector ®nums, int base) { regnums.clear(); std::pair value_pair; value_pair.second = comma_separated_regiter_numbers; do { value_pair = value_pair.second.split(','); if (!value_pair.first.empty()) { uint32_t reg = StringConvert::ToUInt32(value_pair.first.str().c_str(), LLDB_INVALID_REGNUM, base); if (reg != LLDB_INVALID_REGNUM) regnums.push_back(reg); } } while (!value_pair.second.empty()); return regnums.size(); } void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { if (!force && m_register_info.GetNumRegisters() > 0) return; m_register_info.Clear(); // Check if qHostInfo specified a specific packet timeout for this // connection. If so then lets update our setting so the user knows what the // timeout is and can see it. const auto host_packet_timeout = m_gdb_comm.GetHostDefaultPacketTimeout(); if (host_packet_timeout > std::chrono::seconds(0)) { GetGlobalPluginProperties()->SetPacketTimeout(host_packet_timeout.count()); } // Register info search order: // 1 - Use the target definition python file if one is specified. // 2 - If the target definition doesn't have any of the info from the // target.xml (registers) then proceed to read the target.xml. // 3 - Fall back on the qRegisterInfo packets. FileSpec target_definition_fspec = GetGlobalPluginProperties()->GetTargetDefinitionFile(); if (!FileSystem::Instance().Exists(target_definition_fspec)) { // If the filename doesn't exist, it may be a ~ not having been expanded - // try to resolve it. FileSystem::Instance().Resolve(target_definition_fspec); } if (target_definition_fspec) { // See if we can get register definitions from a python file if (ParsePythonTargetDefinition(target_definition_fspec)) { return; } else { StreamSP stream_sp = GetTarget().GetDebugger().GetAsyncOutputStream(); stream_sp->Printf("ERROR: target description file %s failed to parse.\n", target_definition_fspec.GetPath().c_str()); } } const ArchSpec &target_arch = GetTarget().GetArchitecture(); const ArchSpec &remote_host_arch = m_gdb_comm.GetHostArchitecture(); const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); // Use the process' architecture instead of the host arch, if available ArchSpec arch_to_use; if (remote_process_arch.IsValid()) arch_to_use = remote_process_arch; else arch_to_use = remote_host_arch; if (!arch_to_use.IsValid()) arch_to_use = target_arch; if (GetGDBServerRegisterInfo(arch_to_use)) return; char packet[128]; uint32_t reg_offset = 0; uint32_t reg_num = 0; for (StringExtractorGDBRemote::ResponseType response_type = StringExtractorGDBRemote::eResponse; response_type == StringExtractorGDBRemote::eResponse; ++reg_num) { const int packet_len = ::snprintf(packet, sizeof(packet), "qRegisterInfo%x", reg_num); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, false) == GDBRemoteCommunication::PacketResult::Success) { response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { llvm::StringRef name; llvm::StringRef value; ConstString reg_name; ConstString alt_name; ConstString set_name; std::vector value_regs; std::vector invalidate_regs; std::vector dwarf_opcode_bytes; RegisterInfo reg_info = { nullptr, // Name nullptr, // Alt name 0, // byte size reg_offset, // offset eEncodingUint, // encoding eFormatHex, // format { LLDB_INVALID_REGNUM, // eh_frame reg num LLDB_INVALID_REGNUM, // DWARF reg num LLDB_INVALID_REGNUM, // generic reg num reg_num, // process plugin reg num reg_num // native register number }, nullptr, nullptr, nullptr, // Dwarf expression opcode bytes pointer 0 // Dwarf expression opcode bytes length }; while (response.GetNameColonValue(name, value)) { if (name.equals("name")) { reg_name.SetString(value); } else if (name.equals("alt-name")) { alt_name.SetString(value); } else if (name.equals("bitsize")) { value.getAsInteger(0, reg_info.byte_size); reg_info.byte_size /= CHAR_BIT; } else if (name.equals("offset")) { if (value.getAsInteger(0, reg_offset)) reg_offset = UINT32_MAX; } else if (name.equals("encoding")) { const Encoding encoding = Args::StringToEncoding(value); if (encoding != eEncodingInvalid) reg_info.encoding = encoding; } else if (name.equals("format")) { Format format = eFormatInvalid; if (OptionArgParser::ToFormat(value.str().c_str(), format, nullptr) .Success()) reg_info.format = format; else { reg_info.format = llvm::StringSwitch(value) .Case("binary", eFormatBinary) .Case("decimal", eFormatDecimal) .Case("hex", eFormatHex) .Case("float", eFormatFloat) .Case("vector-sint8", eFormatVectorOfSInt8) .Case("vector-uint8", eFormatVectorOfUInt8) .Case("vector-sint16", eFormatVectorOfSInt16) .Case("vector-uint16", eFormatVectorOfUInt16) .Case("vector-sint32", eFormatVectorOfSInt32) .Case("vector-uint32", eFormatVectorOfUInt32) .Case("vector-float32", eFormatVectorOfFloat32) .Case("vector-uint64", eFormatVectorOfUInt64) .Case("vector-uint128", eFormatVectorOfUInt128) .Default(eFormatInvalid); } } else if (name.equals("set")) { set_name.SetString(value); } else if (name.equals("gcc") || name.equals("ehframe")) { if (value.getAsInteger(0, reg_info.kinds[eRegisterKindEHFrame])) reg_info.kinds[eRegisterKindEHFrame] = LLDB_INVALID_REGNUM; } else if (name.equals("dwarf")) { if (value.getAsInteger(0, reg_info.kinds[eRegisterKindDWARF])) reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM; } else if (name.equals("generic")) { reg_info.kinds[eRegisterKindGeneric] = Args::StringToGenericRegister(value); } else if (name.equals("container-regs")) { SplitCommaSeparatedRegisterNumberString(value, value_regs, 16); } else if (name.equals("invalidate-regs")) { SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 16); } else if (name.equals("dynamic_size_dwarf_expr_bytes")) { size_t dwarf_opcode_len = value.size() / 2; assert(dwarf_opcode_len > 0); dwarf_opcode_bytes.resize(dwarf_opcode_len); reg_info.dynamic_size_dwarf_len = dwarf_opcode_len; StringExtractor opcode_extractor(value); uint32_t ret_val = opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes); assert(dwarf_opcode_len == ret_val); UNUSED_IF_ASSERT_DISABLED(ret_val); reg_info.dynamic_size_dwarf_expr_bytes = dwarf_opcode_bytes.data(); } } reg_info.byte_offset = reg_offset; assert(reg_info.byte_size != 0); reg_offset += reg_info.byte_size; if (!value_regs.empty()) { value_regs.push_back(LLDB_INVALID_REGNUM); reg_info.value_regs = value_regs.data(); } if (!invalidate_regs.empty()) { invalidate_regs.push_back(LLDB_INVALID_REGNUM); reg_info.invalidate_regs = invalidate_regs.data(); } reg_info.name = reg_name.AsCString(); // We have to make a temporary ABI here, and not use the GetABI because // this code gets called in DidAttach, when the target architecture // (and consequently the ABI we'll get from the process) may be wrong. if (ABISP abi_sp = ABI::FindPlugin(shared_from_this(), arch_to_use)) abi_sp->AugmentRegisterInfo(reg_info); m_register_info.AddRegister(reg_info, reg_name, alt_name, set_name); } else { break; // ensure exit before reg_num is incremented } } else { break; } } if (m_register_info.GetNumRegisters() > 0) { m_register_info.Finalize(GetTarget().GetArchitecture()); return; } // We didn't get anything if the accumulated reg_num is zero. See if we are // debugging ARM and fill with a hard coded register set until we can get an // updated debugserver down on the devices. On the other hand, if the // accumulated reg_num is positive, see if we can add composite registers to // the existing primordial ones. bool from_scratch = (m_register_info.GetNumRegisters() == 0); if (!target_arch.IsValid()) { if (arch_to_use.IsValid() && (arch_to_use.GetMachine() == llvm::Triple::arm || arch_to_use.GetMachine() == llvm::Triple::thumb) && arch_to_use.GetTriple().getVendor() == llvm::Triple::Apple) m_register_info.HardcodeARMRegisters(from_scratch); } else if (target_arch.GetMachine() == llvm::Triple::arm || target_arch.GetMachine() == llvm::Triple::thumb) { m_register_info.HardcodeARMRegisters(from_scratch); } // At this point, we can finalize our register info. m_register_info.Finalize(GetTarget().GetArchitecture()); } Status ProcessGDBRemote::WillLaunch(lldb_private::Module *module) { return WillLaunchOrAttach(); } Status ProcessGDBRemote::WillAttachToProcessWithID(lldb::pid_t pid) { return WillLaunchOrAttach(); } Status ProcessGDBRemote::WillAttachToProcessWithName(const char *process_name, bool wait_for_launch) { return WillLaunchOrAttach(); } -Status ProcessGDBRemote::DoConnectRemote(Stream *strm, - llvm::StringRef remote_url) { +Status ProcessGDBRemote::DoConnectRemote(llvm::StringRef remote_url) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); Status error(WillLaunchOrAttach()); if (error.Fail()) return error; if (repro::Reproducer::Instance().IsReplaying()) error = ConnectToReplayServer(); else error = ConnectToDebugserver(remote_url); if (error.Fail()) return error; StartAsyncThread(); lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); if (pid == LLDB_INVALID_PROCESS_ID) { // We don't have a valid process ID, so note that we are connected and // could now request to launch or attach, or get remote process listings... SetPrivateState(eStateConnected); } else { // We have a valid process SetID(pid); GetThreadList(); StringExtractorGDBRemote response; if (m_gdb_comm.GetStopReply(response)) { SetLastStopPacket(response); // '?' Packets must be handled differently in non-stop mode if (GetTarget().GetNonStopModeEnabled()) HandleStopReplySequence(); Target &target = GetTarget(); if (!target.GetArchitecture().IsValid()) { if (m_gdb_comm.GetProcessArchitecture().IsValid()) { target.SetArchitecture(m_gdb_comm.GetProcessArchitecture()); } else { if (m_gdb_comm.GetHostArchitecture().IsValid()) { target.SetArchitecture(m_gdb_comm.GetHostArchitecture()); } } } const StateType state = SetThreadStopInfo(response); if (state != eStateInvalid) { SetPrivateState(state); } else error.SetErrorStringWithFormat( "Process %" PRIu64 " was reported after connecting to " "'%s', but state was not stopped: %s", pid, remote_url.str().c_str(), StateAsCString(state)); } else error.SetErrorStringWithFormat("Process %" PRIu64 " was reported after connecting to '%s', " "but no stop reply packet was received", pid, remote_url.str().c_str()); } LLDB_LOGF(log, "ProcessGDBRemote::%s pid %" PRIu64 ": normalizing target architecture initial triple: %s " "(GetTarget().GetArchitecture().IsValid() %s, " "m_gdb_comm.GetHostArchitecture().IsValid(): %s)", __FUNCTION__, GetID(), GetTarget().GetArchitecture().GetTriple().getTriple().c_str(), GetTarget().GetArchitecture().IsValid() ? "true" : "false", m_gdb_comm.GetHostArchitecture().IsValid() ? "true" : "false"); if (error.Success() && !GetTarget().GetArchitecture().IsValid() && m_gdb_comm.GetHostArchitecture().IsValid()) { // Prefer the *process'* architecture over that of the *host*, if // available. if (m_gdb_comm.GetProcessArchitecture().IsValid()) GetTarget().SetArchitecture(m_gdb_comm.GetProcessArchitecture()); else GetTarget().SetArchitecture(m_gdb_comm.GetHostArchitecture()); } LLDB_LOGF(log, "ProcessGDBRemote::%s pid %" PRIu64 ": normalized target architecture triple: %s", __FUNCTION__, GetID(), GetTarget().GetArchitecture().GetTriple().getTriple().c_str()); if (error.Success()) { PlatformSP platform_sp = GetTarget().GetPlatform(); if (platform_sp && platform_sp->IsConnected()) SetUnixSignals(platform_sp->GetUnixSignals()); else SetUnixSignals(UnixSignals::Create(GetTarget().GetArchitecture())); } return error; } Status ProcessGDBRemote::WillLaunchOrAttach() { Status error; m_stdio_communication.Clear(); return error; } // Process Control Status ProcessGDBRemote::DoLaunch(lldb_private::Module *exe_module, ProcessLaunchInfo &launch_info) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); Status error; LLDB_LOGF(log, "ProcessGDBRemote::%s() entered", __FUNCTION__); uint32_t launch_flags = launch_info.GetFlags().Get(); FileSpec stdin_file_spec{}; FileSpec stdout_file_spec{}; FileSpec stderr_file_spec{}; FileSpec working_dir = launch_info.GetWorkingDirectory(); const FileAction *file_action; file_action = launch_info.GetFileActionForFD(STDIN_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) stdin_file_spec = file_action->GetFileSpec(); } file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) stdout_file_spec = file_action->GetFileSpec(); } file_action = launch_info.GetFileActionForFD(STDERR_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) stderr_file_spec = file_action->GetFileSpec(); } if (log) { if (stdin_file_spec || stdout_file_spec || stderr_file_spec) LLDB_LOGF(log, "ProcessGDBRemote::%s provided with STDIO paths via " "launch_info: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, stdin_file_spec ? stdin_file_spec.GetCString() : "", stdout_file_spec ? stdout_file_spec.GetCString() : "", stderr_file_spec ? stderr_file_spec.GetCString() : ""); else LLDB_LOGF(log, "ProcessGDBRemote::%s no STDIO paths given via launch_info", __FUNCTION__); } const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; if (stdin_file_spec || disable_stdio) { // the inferior will be reading stdin from the specified file or stdio is // completely disabled m_stdin_forward = false; } else { m_stdin_forward = true; } // ::LogSetBitMask (GDBR_LOG_DEFAULT); // ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | // LLDB_LOG_OPTION_PREPEND_TIMESTAMP | // LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); // ::LogSetLogFile ("/dev/stdout"); ObjectFile *object_file = exe_module->GetObjectFile(); if (object_file) { error = EstablishConnectionIfNeeded(launch_info); if (error.Success()) { PseudoTerminal pty; const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; PlatformSP platform_sp(GetTarget().GetPlatform()); if (disable_stdio) { // set to /dev/null unless redirected to a file above if (!stdin_file_spec) stdin_file_spec.SetFile(FileSystem::DEV_NULL, FileSpec::Style::native); if (!stdout_file_spec) stdout_file_spec.SetFile(FileSystem::DEV_NULL, FileSpec::Style::native); if (!stderr_file_spec) stderr_file_spec.SetFile(FileSystem::DEV_NULL, FileSpec::Style::native); } else if (platform_sp && platform_sp->IsHost()) { // If the debugserver is local and we aren't disabling STDIO, lets use // a pseudo terminal to instead of relying on the 'O' packets for stdio // since 'O' packets can really slow down debugging if the inferior // does a lot of output. if ((!stdin_file_spec || !stdout_file_spec || !stderr_file_spec) && pty.OpenFirstAvailablePrimary(O_RDWR | O_NOCTTY, nullptr, 0)) { FileSpec secondary_name{pty.GetSecondaryName(nullptr, 0)}; if (!stdin_file_spec) stdin_file_spec = secondary_name; if (!stdout_file_spec) stdout_file_spec = secondary_name; if (!stderr_file_spec) stderr_file_spec = secondary_name; } LLDB_LOGF( log, "ProcessGDBRemote::%s adjusted STDIO paths for local platform " "(IsHost() is true) using secondary: stdin=%s, stdout=%s, " "stderr=%s", __FUNCTION__, stdin_file_spec ? stdin_file_spec.GetCString() : "", stdout_file_spec ? stdout_file_spec.GetCString() : "", stderr_file_spec ? stderr_file_spec.GetCString() : ""); } LLDB_LOGF(log, "ProcessGDBRemote::%s final STDIO paths after all " "adjustments: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, stdin_file_spec ? stdin_file_spec.GetCString() : "", stdout_file_spec ? stdout_file_spec.GetCString() : "", stderr_file_spec ? stderr_file_spec.GetCString() : ""); if (stdin_file_spec) m_gdb_comm.SetSTDIN(stdin_file_spec); if (stdout_file_spec) m_gdb_comm.SetSTDOUT(stdout_file_spec); if (stderr_file_spec) m_gdb_comm.SetSTDERR(stderr_file_spec); m_gdb_comm.SetDisableASLR(launch_flags & eLaunchFlagDisableASLR); m_gdb_comm.SetDetachOnError(launch_flags & eLaunchFlagDetachOnError); m_gdb_comm.SendLaunchArchPacket( GetTarget().GetArchitecture().GetArchitectureName()); const char *launch_event_data = launch_info.GetLaunchEventData(); if (launch_event_data != nullptr && *launch_event_data != '\0') m_gdb_comm.SendLaunchEventDataPacket(launch_event_data); if (working_dir) { m_gdb_comm.SetWorkingDir(working_dir); } // Send the environment and the program + arguments after we connect m_gdb_comm.SendEnvironment(launch_info.GetEnvironment()); { // Scope for the scoped timeout object GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm, std::chrono::seconds(10)); int arg_packet_err = m_gdb_comm.SendArgumentsPacket(launch_info); if (arg_packet_err == 0) { std::string error_str; if (m_gdb_comm.GetLaunchSuccess(error_str)) { SetID(m_gdb_comm.GetCurrentProcessID()); } else { error.SetErrorString(error_str.c_str()); } } else { error.SetErrorStringWithFormat("'A' packet returned an error: %i", arg_packet_err); } } if (GetID() == LLDB_INVALID_PROCESS_ID) { LLDB_LOGF(log, "failed to connect to debugserver: %s", error.AsCString()); KillDebugserverProcess(); return error; } StringExtractorGDBRemote response; if (m_gdb_comm.GetStopReply(response)) { SetLastStopPacket(response); // '?' Packets must be handled differently in non-stop mode if (GetTarget().GetNonStopModeEnabled()) HandleStopReplySequence(); const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture(); if (process_arch.IsValid()) { GetTarget().MergeArchitecture(process_arch); } else { const ArchSpec &host_arch = m_gdb_comm.GetHostArchitecture(); if (host_arch.IsValid()) GetTarget().MergeArchitecture(host_arch); } SetPrivateState(SetThreadStopInfo(response)); if (!disable_stdio) { if (pty.GetPrimaryFileDescriptor() != PseudoTerminal::invalid_fd) SetSTDIOFileDescriptor(pty.ReleasePrimaryFileDescriptor()); } } } else { LLDB_LOGF(log, "failed to connect to debugserver: %s", error.AsCString()); } } else { // Set our user ID to an invalid process ID. SetID(LLDB_INVALID_PROCESS_ID); error.SetErrorStringWithFormat( "failed to get object file from '%s' for arch %s", exe_module->GetFileSpec().GetFilename().AsCString(), exe_module->GetArchitecture().GetArchitectureName()); } return error; } Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) { Status error; // Only connect if we have a valid connect URL Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (!connect_url.empty()) { LLDB_LOGF(log, "ProcessGDBRemote::%s Connecting to %s", __FUNCTION__, connect_url.str().c_str()); std::unique_ptr conn_up( new ConnectionFileDescriptor()); if (conn_up) { const uint32_t max_retry_count = 50; uint32_t retry_count = 0; while (!m_gdb_comm.IsConnected()) { if (conn_up->Connect(connect_url, &error) == eConnectionStatusSuccess) { m_gdb_comm.SetConnection(std::move(conn_up)); break; } else if (error.WasInterrupted()) { // If we were interrupted, don't keep retrying. break; } retry_count++; if (retry_count >= max_retry_count) break; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } } if (!m_gdb_comm.IsConnected()) { if (error.Success()) error.SetErrorString("not connected to remote gdb server"); return error; } // Start the communications read thread so all incoming data can be parsed // into packets and queued as they arrive. if (GetTarget().GetNonStopModeEnabled()) m_gdb_comm.StartReadThread(); // We always seem to be able to open a connection to a local port so we need // to make sure we can then send data to it. If we can't then we aren't // actually connected to anything, so try and do the handshake with the // remote GDB server and make sure that goes alright. if (!m_gdb_comm.HandshakeWithServer(&error)) { m_gdb_comm.Disconnect(); if (error.Success()) error.SetErrorString("not connected to remote gdb server"); return error; } // Send $QNonStop:1 packet on startup if required if (GetTarget().GetNonStopModeEnabled()) GetTarget().SetNonStopModeEnabled(m_gdb_comm.SetNonStopMode(true)); m_gdb_comm.GetEchoSupported(); m_gdb_comm.GetThreadSuffixSupported(); m_gdb_comm.GetListThreadsInStopReplySupported(); m_gdb_comm.GetHostInfo(); m_gdb_comm.GetVContSupported('c'); m_gdb_comm.GetVAttachOrWaitSupported(); m_gdb_comm.EnableErrorStringInPacket(); // Ask the remote server for the default thread id if (GetTarget().GetNonStopModeEnabled()) m_gdb_comm.GetDefaultThreadId(m_initial_tid); size_t num_cmds = GetExtraStartupCommands().GetArgumentCount(); for (size_t idx = 0; idx < num_cmds; idx++) { StringExtractorGDBRemote response; m_gdb_comm.SendPacketAndWaitForResponse( GetExtraStartupCommands().GetArgumentAtIndex(idx), response, false); } return error; } void ProcessGDBRemote::DidLaunchOrAttach(ArchSpec &process_arch) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); BuildDynamicRegisterInfo(false); // See if the GDB server supports qHostInfo or qProcessInfo packets. Prefer // qProcessInfo as it will be more specific to our process. const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); if (remote_process_arch.IsValid()) { process_arch = remote_process_arch; LLDB_LOG(log, "gdb-remote had process architecture, using {0} {1}", process_arch.GetArchitectureName(), process_arch.GetTriple().getTriple()); } else { process_arch = m_gdb_comm.GetHostArchitecture(); LLDB_LOG(log, "gdb-remote did not have process architecture, using gdb-remote " "host architecture {0} {1}", process_arch.GetArchitectureName(), process_arch.GetTriple().getTriple()); } if (process_arch.IsValid()) { const ArchSpec &target_arch = GetTarget().GetArchitecture(); if (target_arch.IsValid()) { LLDB_LOG(log, "analyzing target arch, currently {0} {1}", target_arch.GetArchitectureName(), target_arch.GetTriple().getTriple()); // If the remote host is ARM and we have apple as the vendor, then // ARM executables and shared libraries can have mixed ARM // architectures. // You can have an armv6 executable, and if the host is armv7, then the // system will load the best possible architecture for all shared // libraries it has, so we really need to take the remote host // architecture as our defacto architecture in this case. if ((process_arch.GetMachine() == llvm::Triple::arm || process_arch.GetMachine() == llvm::Triple::thumb) && process_arch.GetTriple().getVendor() == llvm::Triple::Apple) { GetTarget().SetArchitecture(process_arch); LLDB_LOG(log, "remote process is ARM/Apple, " "setting target arch to {0} {1}", process_arch.GetArchitectureName(), process_arch.GetTriple().getTriple()); } else { // Fill in what is missing in the triple const llvm::Triple &remote_triple = process_arch.GetTriple(); llvm::Triple new_target_triple = target_arch.GetTriple(); if (new_target_triple.getVendorName().size() == 0) { new_target_triple.setVendor(remote_triple.getVendor()); if (new_target_triple.getOSName().size() == 0) { new_target_triple.setOS(remote_triple.getOS()); if (new_target_triple.getEnvironmentName().size() == 0) new_target_triple.setEnvironment(remote_triple.getEnvironment()); } ArchSpec new_target_arch = target_arch; new_target_arch.SetTriple(new_target_triple); GetTarget().SetArchitecture(new_target_arch); } } LLDB_LOG(log, "final target arch after adjustments for remote architecture: " "{0} {1}", target_arch.GetArchitectureName(), target_arch.GetTriple().getTriple()); } else { // The target doesn't have a valid architecture yet, set it from the // architecture we got from the remote GDB server GetTarget().SetArchitecture(process_arch); } } MaybeLoadExecutableModule(); // Find out which StructuredDataPlugins are supported by the debug monitor. // These plugins transmit data over async $J packets. if (StructuredData::Array *supported_packets = m_gdb_comm.GetSupportedStructuredDataPlugins()) MapSupportedStructuredDataPlugins(*supported_packets); } void ProcessGDBRemote::MaybeLoadExecutableModule() { ModuleSP module_sp = GetTarget().GetExecutableModule(); if (!module_sp) return; llvm::Optional offsets = m_gdb_comm.GetQOffsets(); if (!offsets) return; bool is_uniform = size_t(llvm::count(offsets->offsets, offsets->offsets[0])) == offsets->offsets.size(); if (!is_uniform) return; // TODO: Handle non-uniform responses. bool changed = false; module_sp->SetLoadAddress(GetTarget(), offsets->offsets[0], /*value_is_offset=*/true, changed); if (changed) { ModuleList list; list.Append(module_sp); m_process->GetTarget().ModulesDidLoad(list); } } void ProcessGDBRemote::DidLaunch() { ArchSpec process_arch; DidLaunchOrAttach(process_arch); } Status ProcessGDBRemote::DoAttachToProcessWithID( lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); Status error; LLDB_LOGF(log, "ProcessGDBRemote::%s()", __FUNCTION__); // Clear out and clean up from any current state Clear(); if (attach_pid != LLDB_INVALID_PROCESS_ID) { error = EstablishConnectionIfNeeded(attach_info); if (error.Success()) { m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid); SetID(attach_pid); m_async_broadcaster.BroadcastEvent( eBroadcastBitAsyncContinue, new EventDataBytes(packet, packet_len)); } else SetExitStatus(-1, error.AsCString()); } return error; } Status ProcessGDBRemote::DoAttachToProcessWithName( const char *process_name, const ProcessAttachInfo &attach_info) { Status error; // Clear out and clean up from any current state Clear(); if (process_name && process_name[0]) { error = EstablishConnectionIfNeeded(attach_info); if (error.Success()) { StreamString packet; m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); if (attach_info.GetWaitForLaunch()) { if (!m_gdb_comm.GetVAttachOrWaitSupported()) { packet.PutCString("vAttachWait"); } else { if (attach_info.GetIgnoreExisting()) packet.PutCString("vAttachWait"); else packet.PutCString("vAttachOrWait"); } } else packet.PutCString("vAttachName"); packet.PutChar(';'); packet.PutBytesAsRawHex8(process_name, strlen(process_name), endian::InlHostByteOrder(), endian::InlHostByteOrder()); m_async_broadcaster.BroadcastEvent( eBroadcastBitAsyncContinue, new EventDataBytes(packet.GetString().data(), packet.GetSize())); } else SetExitStatus(-1, error.AsCString()); } return error; } lldb::user_id_t ProcessGDBRemote::StartTrace(const TraceOptions &options, Status &error) { return m_gdb_comm.SendStartTracePacket(options, error); } Status ProcessGDBRemote::StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) { return m_gdb_comm.SendStopTracePacket(uid, thread_id); } Status ProcessGDBRemote::GetData(lldb::user_id_t uid, lldb::tid_t thread_id, llvm::MutableArrayRef &buffer, size_t offset) { return m_gdb_comm.SendGetDataPacket(uid, thread_id, buffer, offset); } Status ProcessGDBRemote::GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id, llvm::MutableArrayRef &buffer, size_t offset) { return m_gdb_comm.SendGetMetaDataPacket(uid, thread_id, buffer, offset); } Status ProcessGDBRemote::GetTraceConfig(lldb::user_id_t uid, TraceOptions &options) { return m_gdb_comm.SendGetTraceConfigPacket(uid, options); } void ProcessGDBRemote::DidExit() { // When we exit, disconnect from the GDB server communications m_gdb_comm.Disconnect(); } void ProcessGDBRemote::DidAttach(ArchSpec &process_arch) { // If you can figure out what the architecture is, fill it in here. process_arch.Clear(); DidLaunchOrAttach(process_arch); } Status ProcessGDBRemote::WillResume() { m_continue_c_tids.clear(); m_continue_C_tids.clear(); m_continue_s_tids.clear(); m_continue_S_tids.clear(); m_jstopinfo_sp.reset(); m_jthreadsinfo_sp.reset(); return Status(); } Status ProcessGDBRemote::DoResume() { Status error; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); LLDB_LOGF(log, "ProcessGDBRemote::Resume()"); ListenerSP listener_sp( Listener::MakeListener("gdb-remote.resume-packet-sent")); if (listener_sp->StartListeningForEvents( &m_gdb_comm, GDBRemoteCommunication::eBroadcastBitRunPacketSent)) { listener_sp->StartListeningForEvents( &m_async_broadcaster, ProcessGDBRemote::eBroadcastBitAsyncThreadDidExit); const size_t num_threads = GetThreadList().GetSize(); StreamString continue_packet; bool continue_packet_error = false; if (m_gdb_comm.HasAnyVContSupport()) { if (!GetTarget().GetNonStopModeEnabled() && (m_continue_c_tids.size() == num_threads || (m_continue_c_tids.empty() && m_continue_C_tids.empty() && m_continue_s_tids.empty() && m_continue_S_tids.empty()))) { // All threads are continuing, just send a "c" packet continue_packet.PutCString("c"); } else { continue_packet.PutCString("vCont"); if (!m_continue_c_tids.empty()) { if (m_gdb_comm.GetVContSupported('c')) { for (tid_collection::const_iterator t_pos = m_continue_c_tids.begin(), t_end = m_continue_c_tids.end(); t_pos != t_end; ++t_pos) continue_packet.Printf(";c:%4.4" PRIx64, *t_pos); } else continue_packet_error = true; } if (!continue_packet_error && !m_continue_C_tids.empty()) { if (m_gdb_comm.GetVContSupported('C')) { for (tid_sig_collection::const_iterator s_pos = m_continue_C_tids.begin(), s_end = m_continue_C_tids.end(); s_pos != s_end; ++s_pos) continue_packet.Printf(";C%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first); } else continue_packet_error = true; } if (!continue_packet_error && !m_continue_s_tids.empty()) { if (m_gdb_comm.GetVContSupported('s')) { for (tid_collection::const_iterator t_pos = m_continue_s_tids.begin(), t_end = m_continue_s_tids.end(); t_pos != t_end; ++t_pos) continue_packet.Printf(";s:%4.4" PRIx64, *t_pos); } else continue_packet_error = true; } if (!continue_packet_error && !m_continue_S_tids.empty()) { if (m_gdb_comm.GetVContSupported('S')) { for (tid_sig_collection::const_iterator s_pos = m_continue_S_tids.begin(), s_end = m_continue_S_tids.end(); s_pos != s_end; ++s_pos) continue_packet.Printf(";S%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first); } else continue_packet_error = true; } if (continue_packet_error) continue_packet.Clear(); } } else continue_packet_error = true; if (continue_packet_error) { // Either no vCont support, or we tried to use part of the vCont packet // that wasn't supported by the remote GDB server. We need to try and // make a simple packet that can do our continue const size_t num_continue_c_tids = m_continue_c_tids.size(); const size_t num_continue_C_tids = m_continue_C_tids.size(); const size_t num_continue_s_tids = m_continue_s_tids.size(); const size_t num_continue_S_tids = m_continue_S_tids.size(); if (num_continue_c_tids > 0) { if (num_continue_c_tids == num_threads) { // All threads are resuming... m_gdb_comm.SetCurrentThreadForRun(-1); continue_packet.PutChar('c'); continue_packet_error = false; } else if (num_continue_c_tids == 1 && num_continue_C_tids == 0 && num_continue_s_tids == 0 && num_continue_S_tids == 0) { // Only one thread is continuing m_gdb_comm.SetCurrentThreadForRun(m_continue_c_tids.front()); continue_packet.PutChar('c'); continue_packet_error = false; } } if (continue_packet_error && num_continue_C_tids > 0) { if ((num_continue_C_tids + num_continue_c_tids) == num_threads && num_continue_C_tids > 0 && num_continue_s_tids == 0 && num_continue_S_tids == 0) { const int continue_signo = m_continue_C_tids.front().second; // Only one thread is continuing if (num_continue_C_tids > 1) { // More that one thread with a signal, yet we don't have vCont // support and we are being asked to resume each thread with a // signal, we need to make sure they are all the same signal, or we // can't issue the continue accurately with the current support... if (num_continue_C_tids > 1) { continue_packet_error = false; for (size_t i = 1; i < m_continue_C_tids.size(); ++i) { if (m_continue_C_tids[i].second != continue_signo) continue_packet_error = true; } } if (!continue_packet_error) m_gdb_comm.SetCurrentThreadForRun(-1); } else { // Set the continue thread ID continue_packet_error = false; m_gdb_comm.SetCurrentThreadForRun(m_continue_C_tids.front().first); } if (!continue_packet_error) { // Add threads continuing with the same signo... continue_packet.Printf("C%2.2x", continue_signo); } } } if (continue_packet_error && num_continue_s_tids > 0) { if (num_continue_s_tids == num_threads) { // All threads are resuming... m_gdb_comm.SetCurrentThreadForRun(-1); // If in Non-Stop-Mode use vCont when stepping if (GetTarget().GetNonStopModeEnabled()) { if (m_gdb_comm.GetVContSupported('s')) continue_packet.PutCString("vCont;s"); else continue_packet.PutChar('s'); } else continue_packet.PutChar('s'); continue_packet_error = false; } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 && num_continue_s_tids == 1 && num_continue_S_tids == 0) { // Only one thread is stepping m_gdb_comm.SetCurrentThreadForRun(m_continue_s_tids.front()); continue_packet.PutChar('s'); continue_packet_error = false; } } if (!continue_packet_error && num_continue_S_tids > 0) { if (num_continue_S_tids == num_threads) { const int step_signo = m_continue_S_tids.front().second; // Are all threads trying to step with the same signal? continue_packet_error = false; if (num_continue_S_tids > 1) { for (size_t i = 1; i < num_threads; ++i) { if (m_continue_S_tids[i].second != step_signo) continue_packet_error = true; } } if (!continue_packet_error) { // Add threads stepping with the same signo... m_gdb_comm.SetCurrentThreadForRun(-1); continue_packet.Printf("S%2.2x", step_signo); } } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 && num_continue_s_tids == 0 && num_continue_S_tids == 1) { // Only one thread is stepping with signal m_gdb_comm.SetCurrentThreadForRun(m_continue_S_tids.front().first); continue_packet.Printf("S%2.2x", m_continue_S_tids.front().second); continue_packet_error = false; } } } if (continue_packet_error) { error.SetErrorString("can't make continue packet for this resume"); } else { EventSP event_sp; if (!m_async_thread.IsJoinable()) { error.SetErrorString("Trying to resume but the async thread is dead."); LLDB_LOGF(log, "ProcessGDBRemote::DoResume: Trying to resume but the " "async thread is dead."); return error; } m_async_broadcaster.BroadcastEvent( eBroadcastBitAsyncContinue, new EventDataBytes(continue_packet.GetString().data(), continue_packet.GetSize())); if (!listener_sp->GetEvent(event_sp, std::chrono::seconds(5))) { error.SetErrorString("Resume timed out."); LLDB_LOGF(log, "ProcessGDBRemote::DoResume: Resume timed out."); } else if (event_sp->BroadcasterIs(&m_async_broadcaster)) { error.SetErrorString("Broadcast continue, but the async thread was " "killed before we got an ack back."); LLDB_LOGF(log, "ProcessGDBRemote::DoResume: Broadcast continue, but the " "async thread was killed before we got an ack back."); return error; } } } return error; } void ProcessGDBRemote::HandleStopReplySequence() { while (true) { // Send vStopped StringExtractorGDBRemote response; m_gdb_comm.SendPacketAndWaitForResponse("vStopped", response, false); // OK represents end of signal list if (response.IsOKResponse()) break; // If not OK or a normal packet we have a problem if (!response.IsNormalResponse()) break; SetLastStopPacket(response); } } void ProcessGDBRemote::ClearThreadIDList() { std::lock_guard guard(m_thread_list_real.GetMutex()); m_thread_ids.clear(); m_thread_pcs.clear(); } size_t ProcessGDBRemote::UpdateThreadIDsFromStopReplyThreadsValue(std::string &value) { m_thread_ids.clear(); size_t comma_pos; lldb::tid_t tid; while ((comma_pos = value.find(',')) != std::string::npos) { value[comma_pos] = '\0'; // thread in big endian hex tid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_THREAD_ID, 16); if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back(tid); value.erase(0, comma_pos + 1); } tid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_THREAD_ID, 16); if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back(tid); return m_thread_ids.size(); } size_t ProcessGDBRemote::UpdateThreadPCsFromStopReplyThreadsValue(std::string &value) { m_thread_pcs.clear(); size_t comma_pos; lldb::addr_t pc; while ((comma_pos = value.find(',')) != std::string::npos) { value[comma_pos] = '\0'; pc = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16); if (pc != LLDB_INVALID_ADDRESS) m_thread_pcs.push_back(pc); value.erase(0, comma_pos + 1); } pc = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16); if (pc != LLDB_INVALID_THREAD_ID) m_thread_pcs.push_back(pc); return m_thread_pcs.size(); } bool ProcessGDBRemote::UpdateThreadIDList() { std::lock_guard guard(m_thread_list_real.GetMutex()); if (m_jthreadsinfo_sp) { // If we have the JSON threads info, we can get the thread list from that StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray(); if (thread_infos && thread_infos->GetSize() > 0) { m_thread_ids.clear(); m_thread_pcs.clear(); thread_infos->ForEach([this](StructuredData::Object *object) -> bool { StructuredData::Dictionary *thread_dict = object->GetAsDictionary(); if (thread_dict) { // Set the thread stop info from the JSON dictionary SetThreadStopInfo(thread_dict); lldb::tid_t tid = LLDB_INVALID_THREAD_ID; if (thread_dict->GetValueForKeyAsInteger("tid", tid)) m_thread_ids.push_back(tid); } return true; // Keep iterating through all thread_info objects }); } if (!m_thread_ids.empty()) return true; } else { // See if we can get the thread IDs from the current stop reply packets // that might contain a "threads" key/value pair // Lock the thread stack while we access it // Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); std::unique_lock stop_stack_lock( m_last_stop_packet_mutex, std::defer_lock); if (stop_stack_lock.try_lock()) { // Get the number of stop packets on the stack int nItems = m_stop_packet_stack.size(); // Iterate over them for (int i = 0; i < nItems; i++) { // Get the thread stop info StringExtractorGDBRemote &stop_info = m_stop_packet_stack[i]; const std::string &stop_info_str = std::string(stop_info.GetStringRef()); m_thread_pcs.clear(); const size_t thread_pcs_pos = stop_info_str.find(";thread-pcs:"); if (thread_pcs_pos != std::string::npos) { const size_t start = thread_pcs_pos + strlen(";thread-pcs:"); const size_t end = stop_info_str.find(';', start); if (end != std::string::npos) { std::string value = stop_info_str.substr(start, end - start); UpdateThreadPCsFromStopReplyThreadsValue(value); } } const size_t threads_pos = stop_info_str.find(";threads:"); if (threads_pos != std::string::npos) { const size_t start = threads_pos + strlen(";threads:"); const size_t end = stop_info_str.find(';', start); if (end != std::string::npos) { std::string value = stop_info_str.substr(start, end - start); if (UpdateThreadIDsFromStopReplyThreadsValue(value)) return true; } } } } } bool sequence_mutex_unavailable = false; m_gdb_comm.GetCurrentThreadIDs(m_thread_ids, sequence_mutex_unavailable); if (sequence_mutex_unavailable) { return false; // We just didn't get the list } return true; } bool ProcessGDBRemote::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) { // locker will keep a mutex locked until it goes out of scope Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_THREAD)); LLDB_LOGV(log, "pid = {0}", GetID()); size_t num_thread_ids = m_thread_ids.size(); // The "m_thread_ids" thread ID list should always be updated after each stop // reply packet, but in case it isn't, update it here. if (num_thread_ids == 0) { if (!UpdateThreadIDList()) return false; num_thread_ids = m_thread_ids.size(); } ThreadList old_thread_list_copy(old_thread_list); if (num_thread_ids > 0) { for (size_t i = 0; i < num_thread_ids; ++i) { tid_t tid = m_thread_ids[i]; ThreadSP thread_sp( old_thread_list_copy.RemoveThreadByProtocolID(tid, false)); if (!thread_sp) { thread_sp = std::make_shared(*this, tid); LLDB_LOGV(log, "Making new thread: {0} for thread ID: {1:x}.", thread_sp.get(), thread_sp->GetID()); } else { LLDB_LOGV(log, "Found old thread: {0} for thread ID: {1:x}.", thread_sp.get(), thread_sp->GetID()); } SetThreadPc(thread_sp, i); new_thread_list.AddThreadSortedByIndexID(thread_sp); } } // Whatever that is left in old_thread_list_copy are not present in // new_thread_list. Remove non-existent threads from internal id table. size_t old_num_thread_ids = old_thread_list_copy.GetSize(false); for (size_t i = 0; i < old_num_thread_ids; i++) { ThreadSP old_thread_sp(old_thread_list_copy.GetThreadAtIndex(i, false)); if (old_thread_sp) { lldb::tid_t old_thread_id = old_thread_sp->GetProtocolID(); m_thread_id_to_index_id_map.erase(old_thread_id); } } return true; } void ProcessGDBRemote::SetThreadPc(const ThreadSP &thread_sp, uint64_t index) { if (m_thread_ids.size() == m_thread_pcs.size() && thread_sp.get() && GetByteOrder() != eByteOrderInvalid) { ThreadGDBRemote *gdb_thread = static_cast(thread_sp.get()); RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext()); if (reg_ctx_sp) { uint32_t pc_regnum = reg_ctx_sp->ConvertRegisterKindToRegisterNumber( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); if (pc_regnum != LLDB_INVALID_REGNUM) { gdb_thread->PrivateSetRegisterValue(pc_regnum, m_thread_pcs[index]); } } } } bool ProcessGDBRemote::GetThreadStopInfoFromJSON( ThreadGDBRemote *thread, const StructuredData::ObjectSP &thread_infos_sp) { // See if we got thread stop infos for all threads via the "jThreadsInfo" // packet if (thread_infos_sp) { StructuredData::Array *thread_infos = thread_infos_sp->GetAsArray(); if (thread_infos) { lldb::tid_t tid; const size_t n = thread_infos->GetSize(); for (size_t i = 0; i < n; ++i) { StructuredData::Dictionary *thread_dict = thread_infos->GetItemAtIndex(i)->GetAsDictionary(); if (thread_dict) { if (thread_dict->GetValueForKeyAsInteger( "tid", tid, LLDB_INVALID_THREAD_ID)) { if (tid == thread->GetID()) return (bool)SetThreadStopInfo(thread_dict); } } } } } return false; } bool ProcessGDBRemote::CalculateThreadStopInfo(ThreadGDBRemote *thread) { // See if we got thread stop infos for all threads via the "jThreadsInfo" // packet if (GetThreadStopInfoFromJSON(thread, m_jthreadsinfo_sp)) return true; // See if we got thread stop info for any threads valid stop info reasons // threads via the "jstopinfo" packet stop reply packet key/value pair? if (m_jstopinfo_sp) { // If we have "jstopinfo" then we have stop descriptions for all threads // that have stop reasons, and if there is no entry for a thread, then it // has no stop reason. thread->GetRegisterContext()->InvalidateIfNeeded(true); if (!GetThreadStopInfoFromJSON(thread, m_jstopinfo_sp)) { thread->SetStopInfo(StopInfoSP()); } return true; } // Fall back to using the qThreadStopInfo packet StringExtractorGDBRemote stop_packet; if (GetGDBRemote().GetThreadStopInfo(thread->GetProtocolID(), stop_packet)) return SetThreadStopInfo(stop_packet) == eStateStopped; return false; } ThreadSP ProcessGDBRemote::SetThreadStopInfo( lldb::tid_t tid, ExpeditedRegisterMap &expedited_register_map, uint8_t signo, const std::string &thread_name, const std::string &reason, const std::string &description, uint32_t exc_type, const std::vector &exc_data, addr_t thread_dispatch_qaddr, bool queue_vars_valid, // Set to true if queue_name, queue_kind and // queue_serial are valid LazyBool associated_with_dispatch_queue, addr_t dispatch_queue_t, std::string &queue_name, QueueKind queue_kind, uint64_t queue_serial) { ThreadSP thread_sp; if (tid != LLDB_INVALID_THREAD_ID) { // Scope for "locker" below { // m_thread_list_real does have its own mutex, but we need to hold onto // the mutex between the call to m_thread_list_real.FindThreadByID(...) // and the m_thread_list_real.AddThread(...) so it doesn't change on us std::lock_guard guard( m_thread_list_real.GetMutex()); thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false); if (!thread_sp) { // Create the thread if we need to thread_sp = std::make_shared(*this, tid); m_thread_list_real.AddThread(thread_sp); } } if (thread_sp) { ThreadGDBRemote *gdb_thread = static_cast(thread_sp.get()); gdb_thread->GetRegisterContext()->InvalidateIfNeeded(true); auto iter = std::find(m_thread_ids.begin(), m_thread_ids.end(), tid); if (iter != m_thread_ids.end()) { SetThreadPc(thread_sp, iter - m_thread_ids.begin()); } for (const auto &pair : expedited_register_map) { StringExtractor reg_value_extractor(pair.second); DataBufferSP buffer_sp(new DataBufferHeap( reg_value_extractor.GetStringRef().size() / 2, 0)); reg_value_extractor.GetHexBytes(buffer_sp->GetData(), '\xcc'); gdb_thread->PrivateSetRegisterValue(pair.first, buffer_sp->GetData()); } thread_sp->SetName(thread_name.empty() ? nullptr : thread_name.c_str()); gdb_thread->SetThreadDispatchQAddr(thread_dispatch_qaddr); // Check if the GDB server was able to provide the queue name, kind and // serial number if (queue_vars_valid) gdb_thread->SetQueueInfo(std::move(queue_name), queue_kind, queue_serial, dispatch_queue_t, associated_with_dispatch_queue); else gdb_thread->ClearQueueInfo(); gdb_thread->SetAssociatedWithLibdispatchQueue( associated_with_dispatch_queue); if (dispatch_queue_t != LLDB_INVALID_ADDRESS) gdb_thread->SetQueueLibdispatchQueueAddress(dispatch_queue_t); // Make sure we update our thread stop reason just once if (!thread_sp->StopInfoIsUpToDate()) { thread_sp->SetStopInfo(StopInfoSP()); // If there's a memory thread backed by this thread, we need to use it // to calculate StopInfo. if (ThreadSP memory_thread_sp = m_thread_list.GetBackingThread(thread_sp)) thread_sp = memory_thread_sp; if (exc_type != 0) { const size_t exc_data_size = exc_data.size(); thread_sp->SetStopInfo( StopInfoMachException::CreateStopReasonWithMachException( *thread_sp, exc_type, exc_data_size, exc_data_size >= 1 ? exc_data[0] : 0, exc_data_size >= 2 ? exc_data[1] : 0, exc_data_size >= 3 ? exc_data[2] : 0)); } else { bool handled = false; bool did_exec = false; if (!reason.empty()) { if (reason == "trace") { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess() ->GetBreakpointSiteList() .FindByAddress(pc); // If the current pc is a breakpoint site then the StopInfo // should be set to Breakpoint Otherwise, it will be set to // Trace. if (bp_site_sp && bp_site_sp->ValidForThisThread(thread_sp.get())) { thread_sp->SetStopInfo( StopInfo::CreateStopReasonWithBreakpointSiteID( *thread_sp, bp_site_sp->GetID())); } else thread_sp->SetStopInfo( StopInfo::CreateStopReasonToTrace(*thread_sp)); handled = true; } else if (reason == "breakpoint") { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess() ->GetBreakpointSiteList() .FindByAddress(pc); if (bp_site_sp) { // If the breakpoint is for this thread, then we'll report the // hit, but if it is for another thread, we can just report no // reason. We don't need to worry about stepping over the // breakpoint here, that will be taken care of when the thread // resumes and notices that there's a breakpoint under the pc. handled = true; if (bp_site_sp->ValidForThisThread(thread_sp.get())) { thread_sp->SetStopInfo( StopInfo::CreateStopReasonWithBreakpointSiteID( *thread_sp, bp_site_sp->GetID())); } else { StopInfoSP invalid_stop_info_sp; thread_sp->SetStopInfo(invalid_stop_info_sp); } } } else if (reason == "trap") { // Let the trap just use the standard signal stop reason below... } else if (reason == "watchpoint") { StringExtractor desc_extractor(description.c_str()); addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32); addr_t wp_hit_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); watch_id_t watch_id = LLDB_INVALID_WATCH_ID; if (wp_addr != LLDB_INVALID_ADDRESS) { WatchpointSP wp_sp; ArchSpec::Core core = GetTarget().GetArchitecture().GetCore(); if ((core >= ArchSpec::kCore_mips_first && core <= ArchSpec::kCore_mips_last) || (core >= ArchSpec::eCore_arm_generic && core <= ArchSpec::eCore_arm_aarch64)) wp_sp = GetTarget().GetWatchpointList().FindByAddress( wp_hit_addr); if (!wp_sp) wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr); if (wp_sp) { wp_sp->SetHardwareIndex(wp_index); watch_id = wp_sp->GetID(); } } if (watch_id == LLDB_INVALID_WATCH_ID) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet( GDBR_LOG_WATCHPOINTS)); LLDB_LOGF(log, "failed to find watchpoint"); } thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithWatchpointID( *thread_sp, watch_id, wp_hit_addr)); handled = true; } else if (reason == "exception") { thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithException( *thread_sp, description.c_str())); handled = true; } else if (reason == "exec") { did_exec = true; thread_sp->SetStopInfo( StopInfo::CreateStopReasonWithExec(*thread_sp)); handled = true; } } else if (!signo) { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress( pc); // If the current pc is a breakpoint site then the StopInfo should // be set to Breakpoint even though the remote stub did not set it // as such. This can happen when the thread is involuntarily // interrupted (e.g. due to stops on other threads) just as it is // about to execute the breakpoint instruction. if (bp_site_sp && bp_site_sp->ValidForThisThread(thread_sp.get())) { thread_sp->SetStopInfo( StopInfo::CreateStopReasonWithBreakpointSiteID( *thread_sp, bp_site_sp->GetID())); handled = true; } } if (!handled && signo && !did_exec) { if (signo == SIGTRAP) { // Currently we are going to assume SIGTRAP means we are either // hitting a breakpoint or hardware single stepping. handled = true; addr_t pc = thread_sp->GetRegisterContext()->GetPC() + m_breakpoint_pc_offset; lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess() ->GetBreakpointSiteList() .FindByAddress(pc); if (bp_site_sp) { // If the breakpoint is for this thread, then we'll report the // hit, but if it is for another thread, we can just report no // reason. We don't need to worry about stepping over the // breakpoint here, that will be taken care of when the thread // resumes and notices that there's a breakpoint under the pc. if (bp_site_sp->ValidForThisThread(thread_sp.get())) { if (m_breakpoint_pc_offset != 0) thread_sp->GetRegisterContext()->SetPC(pc); thread_sp->SetStopInfo( StopInfo::CreateStopReasonWithBreakpointSiteID( *thread_sp, bp_site_sp->GetID())); } else { StopInfoSP invalid_stop_info_sp; thread_sp->SetStopInfo(invalid_stop_info_sp); } } else { // If we were stepping then assume the stop was the result of // the trace. If we were not stepping then report the SIGTRAP. // FIXME: We are still missing the case where we single step // over a trap instruction. if (thread_sp->GetTemporaryResumeState() == eStateStepping) thread_sp->SetStopInfo( StopInfo::CreateStopReasonToTrace(*thread_sp)); else thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithSignal( *thread_sp, signo, description.c_str())); } } if (!handled) thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithSignal( *thread_sp, signo, description.c_str())); } if (!description.empty()) { lldb::StopInfoSP stop_info_sp(thread_sp->GetStopInfo()); if (stop_info_sp) { const char *stop_info_desc = stop_info_sp->GetDescription(); if (!stop_info_desc || !stop_info_desc[0]) stop_info_sp->SetDescription(description.c_str()); } else { thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithException( *thread_sp, description.c_str())); } } } } } } return thread_sp; } lldb::ThreadSP ProcessGDBRemote::SetThreadStopInfo(StructuredData::Dictionary *thread_dict) { static ConstString g_key_tid("tid"); static ConstString g_key_name("name"); static ConstString g_key_reason("reason"); static ConstString g_key_metype("metype"); static ConstString g_key_medata("medata"); static ConstString g_key_qaddr("qaddr"); static ConstString g_key_dispatch_queue_t("dispatch_queue_t"); static ConstString g_key_associated_with_dispatch_queue( "associated_with_dispatch_queue"); static ConstString g_key_queue_name("qname"); static ConstString g_key_queue_kind("qkind"); static ConstString g_key_queue_serial_number("qserialnum"); static ConstString g_key_registers("registers"); static ConstString g_key_memory("memory"); static ConstString g_key_address("address"); static ConstString g_key_bytes("bytes"); static ConstString g_key_description("description"); static ConstString g_key_signal("signal"); // Stop with signal and thread info lldb::tid_t tid = LLDB_INVALID_THREAD_ID; uint8_t signo = 0; std::string value; std::string thread_name; std::string reason; std::string description; uint32_t exc_type = 0; std::vector exc_data; addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; ExpeditedRegisterMap expedited_register_map; bool queue_vars_valid = false; addr_t dispatch_queue_t = LLDB_INVALID_ADDRESS; LazyBool associated_with_dispatch_queue = eLazyBoolCalculate; std::string queue_name; QueueKind queue_kind = eQueueKindUnknown; uint64_t queue_serial_number = 0; // Iterate through all of the thread dictionary key/value pairs from the // structured data dictionary thread_dict->ForEach([this, &tid, &expedited_register_map, &thread_name, &signo, &reason, &description, &exc_type, &exc_data, &thread_dispatch_qaddr, &queue_vars_valid, &associated_with_dispatch_queue, &dispatch_queue_t, &queue_name, &queue_kind, &queue_serial_number]( ConstString key, StructuredData::Object *object) -> bool { if (key == g_key_tid) { // thread in big endian hex tid = object->GetIntegerValue(LLDB_INVALID_THREAD_ID); } else if (key == g_key_metype) { // exception type in big endian hex exc_type = object->GetIntegerValue(0); } else if (key == g_key_medata) { // exception data in big endian hex StructuredData::Array *array = object->GetAsArray(); if (array) { array->ForEach([&exc_data](StructuredData::Object *object) -> bool { exc_data.push_back(object->GetIntegerValue()); return true; // Keep iterating through all array items }); } } else if (key == g_key_name) { thread_name = std::string(object->GetStringValue()); } else if (key == g_key_qaddr) { thread_dispatch_qaddr = object->GetIntegerValue(LLDB_INVALID_ADDRESS); } else if (key == g_key_queue_name) { queue_vars_valid = true; queue_name = std::string(object->GetStringValue()); } else if (key == g_key_queue_kind) { std::string queue_kind_str = std::string(object->GetStringValue()); if (queue_kind_str == "serial") { queue_vars_valid = true; queue_kind = eQueueKindSerial; } else if (queue_kind_str == "concurrent") { queue_vars_valid = true; queue_kind = eQueueKindConcurrent; } } else if (key == g_key_queue_serial_number) { queue_serial_number = object->GetIntegerValue(0); if (queue_serial_number != 0) queue_vars_valid = true; } else if (key == g_key_dispatch_queue_t) { dispatch_queue_t = object->GetIntegerValue(0); if (dispatch_queue_t != 0 && dispatch_queue_t != LLDB_INVALID_ADDRESS) queue_vars_valid = true; } else if (key == g_key_associated_with_dispatch_queue) { queue_vars_valid = true; bool associated = object->GetBooleanValue(); if (associated) associated_with_dispatch_queue = eLazyBoolYes; else associated_with_dispatch_queue = eLazyBoolNo; } else if (key == g_key_reason) { reason = std::string(object->GetStringValue()); } else if (key == g_key_description) { description = std::string(object->GetStringValue()); } else if (key == g_key_registers) { StructuredData::Dictionary *registers_dict = object->GetAsDictionary(); if (registers_dict) { registers_dict->ForEach( [&expedited_register_map](ConstString key, StructuredData::Object *object) -> bool { const uint32_t reg = StringConvert::ToUInt32(key.GetCString(), UINT32_MAX, 10); if (reg != UINT32_MAX) expedited_register_map[reg] = std::string(object->GetStringValue()); return true; // Keep iterating through all array items }); } } else if (key == g_key_memory) { StructuredData::Array *array = object->GetAsArray(); if (array) { array->ForEach([this](StructuredData::Object *object) -> bool { StructuredData::Dictionary *mem_cache_dict = object->GetAsDictionary(); if (mem_cache_dict) { lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS; if (mem_cache_dict->GetValueForKeyAsInteger( "address", mem_cache_addr)) { if (mem_cache_addr != LLDB_INVALID_ADDRESS) { llvm::StringRef str; if (mem_cache_dict->GetValueForKeyAsString("bytes", str)) { StringExtractor bytes(str); bytes.SetFilePos(0); const size_t byte_size = bytes.GetStringRef().size() / 2; DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0)); const size_t bytes_copied = bytes.GetHexBytes(data_buffer_sp->GetData(), 0); if (bytes_copied == byte_size) m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp); } } } } return true; // Keep iterating through all array items }); } } else if (key == g_key_signal) signo = object->GetIntegerValue(LLDB_INVALID_SIGNAL_NUMBER); return true; // Keep iterating through all dictionary key/value pairs }); return SetThreadStopInfo(tid, expedited_register_map, signo, thread_name, reason, description, exc_type, exc_data, thread_dispatch_qaddr, queue_vars_valid, associated_with_dispatch_queue, dispatch_queue_t, queue_name, queue_kind, queue_serial_number); } StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) { stop_packet.SetFilePos(0); const char stop_type = stop_packet.GetChar(); switch (stop_type) { case 'T': case 'S': { // This is a bit of a hack, but is is required. If we did exec, we need to // clear our thread lists and also know to rebuild our dynamic register // info before we lookup and threads and populate the expedited register // values so we need to know this right away so we can cleanup and update // our registers. const uint32_t stop_id = GetStopID(); if (stop_id == 0) { // Our first stop, make sure we have a process ID, and also make sure we // know about our registers if (GetID() == LLDB_INVALID_PROCESS_ID) { lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); if (pid != LLDB_INVALID_PROCESS_ID) SetID(pid); } BuildDynamicRegisterInfo(true); } // Stop with signal and thread info lldb::tid_t tid = LLDB_INVALID_THREAD_ID; const uint8_t signo = stop_packet.GetHexU8(); llvm::StringRef key; llvm::StringRef value; std::string thread_name; std::string reason; std::string description; uint32_t exc_type = 0; std::vector exc_data; addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; bool queue_vars_valid = false; // says if locals below that start with "queue_" are valid addr_t dispatch_queue_t = LLDB_INVALID_ADDRESS; LazyBool associated_with_dispatch_queue = eLazyBoolCalculate; std::string queue_name; QueueKind queue_kind = eQueueKindUnknown; uint64_t queue_serial_number = 0; ExpeditedRegisterMap expedited_register_map; while (stop_packet.GetNameColonValue(key, value)) { if (key.compare("metype") == 0) { // exception type in big endian hex value.getAsInteger(16, exc_type); } else if (key.compare("medata") == 0) { // exception data in big endian hex uint64_t x; value.getAsInteger(16, x); exc_data.push_back(x); } else if (key.compare("thread") == 0) { // thread in big endian hex if (value.getAsInteger(16, tid)) tid = LLDB_INVALID_THREAD_ID; } else if (key.compare("threads") == 0) { std::lock_guard guard( m_thread_list_real.GetMutex()); m_thread_ids.clear(); // A comma separated list of all threads in the current // process that includes the thread for this stop reply packet lldb::tid_t tid; while (!value.empty()) { llvm::StringRef tid_str; std::tie(tid_str, value) = value.split(','); if (tid_str.getAsInteger(16, tid)) tid = LLDB_INVALID_THREAD_ID; m_thread_ids.push_back(tid); } } else if (key.compare("thread-pcs") == 0) { m_thread_pcs.clear(); // A comma separated list of all threads in the current // process that includes the thread for this stop reply packet lldb::addr_t pc; while (!value.empty()) { llvm::StringRef pc_str; std::tie(pc_str, value) = value.split(','); if (pc_str.getAsInteger(16, pc)) pc = LLDB_INVALID_ADDRESS; m_thread_pcs.push_back(pc); } } else if (key.compare("jstopinfo") == 0) { StringExtractor json_extractor(value); std::string json; // Now convert the HEX bytes into a string value json_extractor.GetHexByteString(json); // This JSON contains thread IDs and thread stop info for all threads. // It doesn't contain expedited registers, memory or queue info. m_jstopinfo_sp = StructuredData::ParseJSON(json); } else if (key.compare("hexname") == 0) { StringExtractor name_extractor(value); std::string name; // Now convert the HEX bytes into a string value name_extractor.GetHexByteString(thread_name); } else if (key.compare("name") == 0) { thread_name = std::string(value); } else if (key.compare("qaddr") == 0) { value.getAsInteger(16, thread_dispatch_qaddr); } else if (key.compare("dispatch_queue_t") == 0) { queue_vars_valid = true; value.getAsInteger(16, dispatch_queue_t); } else if (key.compare("qname") == 0) { queue_vars_valid = true; StringExtractor name_extractor(value); // Now convert the HEX bytes into a string value name_extractor.GetHexByteString(queue_name); } else if (key.compare("qkind") == 0) { queue_kind = llvm::StringSwitch(value) .Case("serial", eQueueKindSerial) .Case("concurrent", eQueueKindConcurrent) .Default(eQueueKindUnknown); queue_vars_valid = queue_kind != eQueueKindUnknown; } else if (key.compare("qserialnum") == 0) { if (!value.getAsInteger(0, queue_serial_number)) queue_vars_valid = true; } else if (key.compare("reason") == 0) { reason = std::string(value); } else if (key.compare("description") == 0) { StringExtractor desc_extractor(value); // Now convert the HEX bytes into a string value desc_extractor.GetHexByteString(description); } else if (key.compare("memory") == 0) { // Expedited memory. GDB servers can choose to send back expedited // memory that can populate the L1 memory cache in the process so that // things like the frame pointer backchain can be expedited. This will // help stack backtracing be more efficient by not having to send as // many memory read requests down the remote GDB server. // Key/value pair format: memory:=; // is a number whose base will be interpreted by the prefix: // "0x[0-9a-fA-F]+" for hex // "0[0-7]+" for octal // "[1-9]+" for decimal // is native endian ASCII hex bytes just like the register // values llvm::StringRef addr_str, bytes_str; std::tie(addr_str, bytes_str) = value.split('='); if (!addr_str.empty() && !bytes_str.empty()) { lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS; if (!addr_str.getAsInteger(0, mem_cache_addr)) { StringExtractor bytes(bytes_str); const size_t byte_size = bytes.GetBytesLeft() / 2; DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0)); const size_t bytes_copied = bytes.GetHexBytes(data_buffer_sp->GetData(), 0); if (bytes_copied == byte_size) m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp); } } } else if (key.compare("watch") == 0 || key.compare("rwatch") == 0 || key.compare("awatch") == 0) { // Support standard GDB remote stop reply packet 'TAAwatch:addr' lldb::addr_t wp_addr = LLDB_INVALID_ADDRESS; value.getAsInteger(16, wp_addr); WatchpointSP wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr); uint32_t wp_index = LLDB_INVALID_INDEX32; if (wp_sp) wp_index = wp_sp->GetHardwareIndex(); reason = "watchpoint"; StreamString ostr; ostr.Printf("%" PRIu64 " %" PRIu32, wp_addr, wp_index); description = std::string(ostr.GetString()); } else if (key.compare("library") == 0) { auto error = LoadModules(); if (error) { Log *log( ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); LLDB_LOG_ERROR(log, std::move(error), "Failed to load modules: {0}"); } } else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1])) { uint32_t reg = UINT32_MAX; if (!key.getAsInteger(16, reg)) expedited_register_map[reg] = std::string(std::move(value)); } } if (tid == LLDB_INVALID_THREAD_ID) { // A thread id may be invalid if the response is old style 'S' packet // which does not provide the // thread information. So update the thread list and choose the first // one. UpdateThreadIDList(); if (!m_thread_ids.empty()) { tid = m_thread_ids.front(); } } ThreadSP thread_sp = SetThreadStopInfo( tid, expedited_register_map, signo, thread_name, reason, description, exc_type, exc_data, thread_dispatch_qaddr, queue_vars_valid, associated_with_dispatch_queue, dispatch_queue_t, queue_name, queue_kind, queue_serial_number); return eStateStopped; } break; case 'W': case 'X': // process exited return eStateExited; default: break; } return eStateInvalid; } void ProcessGDBRemote::RefreshStateAfterStop() { std::lock_guard guard(m_thread_list_real.GetMutex()); m_thread_ids.clear(); m_thread_pcs.clear(); // Set the thread stop info. It might have a "threads" key whose value is a // list of all thread IDs in the current process, so m_thread_ids might get // set. // Check to see if SetThreadStopInfo() filled in m_thread_ids? if (m_thread_ids.empty()) { // No, we need to fetch the thread list manually UpdateThreadIDList(); } // We might set some stop info's so make sure the thread list is up to // date before we do that or we might overwrite what was computed here. UpdateThreadListIfNeeded(); // Scope for the lock { // Lock the thread stack while we access it std::lock_guard guard(m_last_stop_packet_mutex); // Get the number of stop packets on the stack int nItems = m_stop_packet_stack.size(); // Iterate over them for (int i = 0; i < nItems; i++) { // Get the thread stop info StringExtractorGDBRemote stop_info = m_stop_packet_stack[i]; // Process thread stop info SetThreadStopInfo(stop_info); } // Clear the thread stop stack m_stop_packet_stack.clear(); } // If we have queried for a default thread id if (m_initial_tid != LLDB_INVALID_THREAD_ID) { m_thread_list.SetSelectedThreadByID(m_initial_tid); m_initial_tid = LLDB_INVALID_THREAD_ID; } // Let all threads recover from stopping and do any clean up based on the // previous thread state (if any). m_thread_list_real.RefreshStateAfterStop(); } Status ProcessGDBRemote::DoHalt(bool &caused_stop) { Status error; if (m_public_state.GetValue() == eStateAttaching) { // We are being asked to halt during an attach. We need to just close our // file handle and debugserver will go away, and we can be done... m_gdb_comm.Disconnect(); } else caused_stop = m_gdb_comm.Interrupt(); return error; } Status ProcessGDBRemote::DoDetach(bool keep_stopped) { Status error; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); LLDB_LOGF(log, "ProcessGDBRemote::DoDetach(keep_stopped: %i)", keep_stopped); error = m_gdb_comm.Detach(keep_stopped); if (log) { if (error.Success()) log->PutCString( "ProcessGDBRemote::DoDetach() detach packet sent successfully"); else LLDB_LOGF(log, "ProcessGDBRemote::DoDetach() detach packet send failed: %s", error.AsCString() ? error.AsCString() : ""); } if (!error.Success()) return error; // Sleep for one second to let the process get all detached... StopAsyncThread(); SetPrivateState(eStateDetached); ResumePrivateStateThread(); // KillDebugserverProcess (); return error; } Status ProcessGDBRemote::DoDestroy() { Status error; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); LLDB_LOGF(log, "ProcessGDBRemote::DoDestroy()"); // There is a bug in older iOS debugservers where they don't shut down the // process they are debugging properly. If the process is sitting at a // breakpoint or an exception, this can cause problems with restarting. So // we check to see if any of our threads are stopped at a breakpoint, and if // so we remove all the breakpoints, resume the process, and THEN destroy it // again. // // Note, we don't have a good way to test the version of debugserver, but I // happen to know that the set of all the iOS debugservers which don't // support GetThreadSuffixSupported() and that of the debugservers with this // bug are equal. There really should be a better way to test this! // // We also use m_destroy_tried_resuming to make sure we only do this once, if // we resume and then halt and get called here to destroy again and we're // still at a breakpoint or exception, then we should just do the straight- // forward kill. // // And of course, if we weren't able to stop the process by the time we get // here, it isn't necessary (or helpful) to do any of this. if (!m_gdb_comm.GetThreadSuffixSupported() && m_public_state.GetValue() != eStateRunning) { PlatformSP platform_sp = GetTarget().GetPlatform(); // FIXME: These should be ConstStrings so we aren't doing strcmp'ing. if (platform_sp && platform_sp->GetName() && platform_sp->GetName() == PlatformRemoteiOS::GetPluginNameStatic()) { if (m_destroy_tried_resuming) { if (log) log->PutCString("ProcessGDBRemote::DoDestroy() - Tried resuming to " "destroy once already, not doing it again."); } else { // At present, the plans are discarded and the breakpoints disabled // Process::Destroy, but we really need it to happen here and it // doesn't matter if we do it twice. m_thread_list.DiscardThreadPlans(); DisableAllBreakpointSites(); bool stop_looks_like_crash = false; ThreadList &threads = GetThreadList(); { std::lock_guard guard(threads.GetMutex()); size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) { ThreadSP thread_sp = threads.GetThreadAtIndex(i); StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo(); StopReason reason = eStopReasonInvalid; if (stop_info_sp) reason = stop_info_sp->GetStopReason(); if (reason == eStopReasonBreakpoint || reason == eStopReasonException) { LLDB_LOGF(log, "ProcessGDBRemote::DoDestroy() - thread: 0x%4.4" PRIx64 " stopped with reason: %s.", thread_sp->GetProtocolID(), stop_info_sp->GetDescription()); stop_looks_like_crash = true; break; } } } if (stop_looks_like_crash) { if (log) log->PutCString("ProcessGDBRemote::DoDestroy() - Stopped at a " "breakpoint, continue and then kill."); m_destroy_tried_resuming = true; // If we are going to run again before killing, it would be good to // suspend all the threads before resuming so they won't get into // more trouble. Sadly, for the threads stopped with the breakpoint // or exception, the exception doesn't get cleared if it is // suspended, so we do have to run the risk of letting those threads // proceed a bit. { std::lock_guard guard(threads.GetMutex()); size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) { ThreadSP thread_sp = threads.GetThreadAtIndex(i); StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo(); StopReason reason = eStopReasonInvalid; if (stop_info_sp) reason = stop_info_sp->GetStopReason(); if (reason != eStopReasonBreakpoint && reason != eStopReasonException) { LLDB_LOGF(log, "ProcessGDBRemote::DoDestroy() - Suspending " "thread: 0x%4.4" PRIx64 " before running.", thread_sp->GetProtocolID()); thread_sp->SetResumeState(eStateSuspended); } } } Resume(); return Destroy(false); } } } } // Interrupt if our inferior is running... int exit_status = SIGABRT; std::string exit_string; if (m_gdb_comm.IsConnected()) { if (m_public_state.GetValue() != eStateAttaching) { StringExtractorGDBRemote response; bool send_async = true; GDBRemoteCommunication::ScopedTimeout(m_gdb_comm, std::chrono::seconds(3)); if (m_gdb_comm.SendPacketAndWaitForResponse("k", response, send_async) == GDBRemoteCommunication::PacketResult::Success) { char packet_cmd = response.GetChar(0); if (packet_cmd == 'W' || packet_cmd == 'X') { #if defined(__APPLE__) // For Native processes on Mac OS X, we launch through the Host // Platform, then hand the process off to debugserver, which becomes // the parent process through "PT_ATTACH". Then when we go to kill // the process on Mac OS X we call ptrace(PT_KILL) to kill it, then // we call waitpid which returns with no error and the correct // status. But amusingly enough that doesn't seem to actually reap // the process, but instead it is left around as a Zombie. Probably // the kernel is in the process of switching ownership back to lldb // which was the original parent, and gets confused in the handoff. // Anyway, so call waitpid here to finally reap it. PlatformSP platform_sp(GetTarget().GetPlatform()); if (platform_sp && platform_sp->IsHost()) { int status; ::pid_t reap_pid; reap_pid = waitpid(GetID(), &status, WNOHANG); LLDB_LOGF(log, "Reaped pid: %d, status: %d.\n", reap_pid, status); } #endif SetLastStopPacket(response); ClearThreadIDList(); exit_status = response.GetHexU8(); } else { LLDB_LOGF(log, "ProcessGDBRemote::DoDestroy - got unexpected response " "to k packet: %s", response.GetStringRef().data()); exit_string.assign("got unexpected response to k packet: "); exit_string.append(std::string(response.GetStringRef())); } } else { LLDB_LOGF(log, "ProcessGDBRemote::DoDestroy - failed to send k packet"); exit_string.assign("failed to send the k packet"); } } else { LLDB_LOGF(log, "ProcessGDBRemote::DoDestroy - killed or interrupted while " "attaching"); exit_string.assign("killed or interrupted while attaching."); } } else { // If we missed setting the exit status on the way out, do it here. // NB set exit status can be called multiple times, the first one sets the // status. exit_string.assign("destroying when not connected to debugserver"); } SetExitStatus(exit_status, exit_string.c_str()); StopAsyncThread(); KillDebugserverProcess(); return error; } void ProcessGDBRemote::SetLastStopPacket( const StringExtractorGDBRemote &response) { const bool did_exec = response.GetStringRef().find(";reason:exec;") != std::string::npos; if (did_exec) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); LLDB_LOGF(log, "ProcessGDBRemote::SetLastStopPacket () - detected exec"); m_thread_list_real.Clear(); m_thread_list.Clear(); BuildDynamicRegisterInfo(true); m_gdb_comm.ResetDiscoverableSettings(did_exec); } // Scope the lock { // Lock the thread stack while we access it std::lock_guard guard(m_last_stop_packet_mutex); // We are are not using non-stop mode, there can only be one last stop // reply packet, so clear the list. if (!GetTarget().GetNonStopModeEnabled()) m_stop_packet_stack.clear(); // Add this stop packet to the stop packet stack This stack will get popped // and examined when we switch to the Stopped state m_stop_packet_stack.push_back(response); } } void ProcessGDBRemote::SetUnixSignals(const UnixSignalsSP &signals_sp) { Process::SetUnixSignals(std::make_shared(signals_sp)); } // Process Queries bool ProcessGDBRemote::IsAlive() { return m_gdb_comm.IsConnected() && Process::IsAlive(); } addr_t ProcessGDBRemote::GetImageInfoAddress() { // request the link map address via the $qShlibInfoAddr packet lldb::addr_t addr = m_gdb_comm.GetShlibInfoAddr(); // the loaded module list can also provides a link map address if (addr == LLDB_INVALID_ADDRESS) { llvm::Expected list = GetLoadedModuleList(); if (!list) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); LLDB_LOG_ERROR(log, list.takeError(), "Failed to read module list: {0}"); } else { addr = list->m_link_map; } } return addr; } void ProcessGDBRemote::WillPublicStop() { // See if the GDB remote client supports the JSON threads info. If so, we // gather stop info for all threads, expedited registers, expedited memory, // runtime queue information (iOS and MacOSX only), and more. Expediting // memory will help stack backtracing be much faster. Expediting registers // will make sure we don't have to read the thread registers for GPRs. m_jthreadsinfo_sp = m_gdb_comm.GetThreadsInfo(); if (m_jthreadsinfo_sp) { // Now set the stop info for each thread and also expedite any registers // and memory that was in the jThreadsInfo response. StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray(); if (thread_infos) { const size_t n = thread_infos->GetSize(); for (size_t i = 0; i < n; ++i) { StructuredData::Dictionary *thread_dict = thread_infos->GetItemAtIndex(i)->GetAsDictionary(); if (thread_dict) SetThreadStopInfo(thread_dict); } } } } // Process Memory size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size, Status &error) { GetMaxMemorySize(); bool binary_memory_read = m_gdb_comm.GetxPacketSupported(); // M and m packets take 2 bytes for 1 byte of memory size_t max_memory_size = binary_memory_read ? m_max_memory_size : m_max_memory_size / 2; if (size > max_memory_size) { // Keep memory read sizes down to a sane limit. This function will be // called multiple times in order to complete the task by // lldb_private::Process so it is ok to do this. size = max_memory_size; } char packet[64]; int packet_len; packet_len = ::snprintf(packet, sizeof(packet), "%c%" PRIx64 ",%" PRIx64, binary_memory_read ? 'x' : 'm', (uint64_t)addr, (uint64_t)size); assert(packet_len + 1 < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, true) == GDBRemoteCommunication::PacketResult::Success) { if (response.IsNormalResponse()) { error.Clear(); if (binary_memory_read) { // The lower level GDBRemoteCommunication packet receive layer has // already de-quoted any 0x7d character escaping that was present in // the packet size_t data_received_size = response.GetBytesLeft(); if (data_received_size > size) { // Don't write past the end of BUF if the remote debug server gave us // too much data for some reason. data_received_size = size; } memcpy(buf, response.GetStringRef().data(), data_received_size); return data_received_size; } else { return response.GetHexBytes( llvm::MutableArrayRef((uint8_t *)buf, size), '\xdd'); } } else if (response.IsErrorResponse()) error.SetErrorStringWithFormat("memory read failed for 0x%" PRIx64, addr); else if (response.IsUnsupportedResponse()) error.SetErrorStringWithFormat( "GDB server does not support reading memory"); else error.SetErrorStringWithFormat( "unexpected response to GDB server memory read packet '%s': '%s'", packet, response.GetStringRef().data()); } else { error.SetErrorStringWithFormat("failed to send packet: '%s'", packet); } return 0; } Status ProcessGDBRemote::WriteObjectFile( std::vector entries) { Status error; // Sort the entries by address because some writes, like those to flash // memory, must happen in order of increasing address. std::stable_sort( std::begin(entries), std::end(entries), [](const ObjectFile::LoadableData a, const ObjectFile::LoadableData b) { return a.Dest < b.Dest; }); m_allow_flash_writes = true; error = Process::WriteObjectFile(entries); if (error.Success()) error = FlashDone(); else // Even though some of the writing failed, try to send a flash done if some // of the writing succeeded so the flash state is reset to normal, but // don't stomp on the error status that was set in the write failure since // that's the one we want to report back. FlashDone(); m_allow_flash_writes = false; return error; } bool ProcessGDBRemote::HasErased(FlashRange range) { auto size = m_erased_flash_ranges.GetSize(); for (size_t i = 0; i < size; ++i) if (m_erased_flash_ranges.GetEntryAtIndex(i)->Contains(range)) return true; return false; } Status ProcessGDBRemote::FlashErase(lldb::addr_t addr, size_t size) { Status status; MemoryRegionInfo region; status = GetMemoryRegionInfo(addr, region); if (!status.Success()) return status; // The gdb spec doesn't say if erasures are allowed across multiple regions, // but we'll disallow it to be safe and to keep the logic simple by worring // about only one region's block size. DoMemoryWrite is this function's // primary user, and it can easily keep writes within a single memory region if (addr + size > region.GetRange().GetRangeEnd()) { status.SetErrorString("Unable to erase flash in multiple regions"); return status; } uint64_t blocksize = region.GetBlocksize(); if (blocksize == 0) { status.SetErrorString("Unable to erase flash because blocksize is 0"); return status; } // Erasures can only be done on block boundary adresses, so round down addr // and round up size lldb::addr_t block_start_addr = addr - (addr % blocksize); size += (addr - block_start_addr); if ((size % blocksize) != 0) size += (blocksize - size % blocksize); FlashRange range(block_start_addr, size); if (HasErased(range)) return status; // We haven't erased the entire range, but we may have erased part of it. // (e.g., block A is already erased and range starts in A and ends in B). So, // adjust range if necessary to exclude already erased blocks. if (!m_erased_flash_ranges.IsEmpty()) { // Assuming that writes and erasures are done in increasing addr order, // because that is a requirement of the vFlashWrite command. Therefore, we // only need to look at the last range in the list for overlap. const auto &last_range = *m_erased_flash_ranges.Back(); if (range.GetRangeBase() < last_range.GetRangeEnd()) { auto overlap = last_range.GetRangeEnd() - range.GetRangeBase(); // overlap will be less than range.GetByteSize() or else HasErased() // would have been true range.SetByteSize(range.GetByteSize() - overlap); range.SetRangeBase(range.GetRangeBase() + overlap); } } StreamString packet; packet.Printf("vFlashErase:%" PRIx64 ",%" PRIx64, range.GetRangeBase(), (uint64_t)range.GetByteSize()); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, true) == GDBRemoteCommunication::PacketResult::Success) { if (response.IsOKResponse()) { m_erased_flash_ranges.Insert(range, true); } else { if (response.IsErrorResponse()) status.SetErrorStringWithFormat("flash erase failed for 0x%" PRIx64, addr); else if (response.IsUnsupportedResponse()) status.SetErrorStringWithFormat("GDB server does not support flashing"); else status.SetErrorStringWithFormat( "unexpected response to GDB server flash erase packet '%s': '%s'", packet.GetData(), response.GetStringRef().data()); } } else { status.SetErrorStringWithFormat("failed to send packet: '%s'", packet.GetData()); } return status; } Status ProcessGDBRemote::FlashDone() { Status status; // If we haven't erased any blocks, then we must not have written anything // either, so there is no need to actually send a vFlashDone command if (m_erased_flash_ranges.IsEmpty()) return status; StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse("vFlashDone", response, true) == GDBRemoteCommunication::PacketResult::Success) { if (response.IsOKResponse()) { m_erased_flash_ranges.Clear(); } else { if (response.IsErrorResponse()) status.SetErrorStringWithFormat("flash done failed"); else if (response.IsUnsupportedResponse()) status.SetErrorStringWithFormat("GDB server does not support flashing"); else status.SetErrorStringWithFormat( "unexpected response to GDB server flash done packet: '%s'", response.GetStringRef().data()); } } else { status.SetErrorStringWithFormat("failed to send flash done packet"); } return status; } size_t ProcessGDBRemote::DoWriteMemory(addr_t addr, const void *buf, size_t size, Status &error) { GetMaxMemorySize(); // M and m packets take 2 bytes for 1 byte of memory size_t max_memory_size = m_max_memory_size / 2; if (size > max_memory_size) { // Keep memory read sizes down to a sane limit. This function will be // called multiple times in order to complete the task by // lldb_private::Process so it is ok to do this. size = max_memory_size; } StreamGDBRemote packet; MemoryRegionInfo region; Status region_status = GetMemoryRegionInfo(addr, region); bool is_flash = region_status.Success() && region.GetFlash() == MemoryRegionInfo::eYes; if (is_flash) { if (!m_allow_flash_writes) { error.SetErrorString("Writing to flash memory is not allowed"); return 0; } // Keep the write within a flash memory region if (addr + size > region.GetRange().GetRangeEnd()) size = region.GetRange().GetRangeEnd() - addr; // Flash memory must be erased before it can be written error = FlashErase(addr, size); if (!error.Success()) return 0; packet.Printf("vFlashWrite:%" PRIx64 ":", addr); packet.PutEscapedBytes(buf, size); } else { packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size); packet.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(), endian::InlHostByteOrder()); } StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, true) == GDBRemoteCommunication::PacketResult::Success) { if (response.IsOKResponse()) { error.Clear(); return size; } else if (response.IsErrorResponse()) error.SetErrorStringWithFormat("memory write failed for 0x%" PRIx64, addr); else if (response.IsUnsupportedResponse()) error.SetErrorStringWithFormat( "GDB server does not support writing memory"); else error.SetErrorStringWithFormat( "unexpected response to GDB server memory write packet '%s': '%s'", packet.GetData(), response.GetStringRef().data()); } else { error.SetErrorStringWithFormat("failed to send packet: '%s'", packet.GetData()); } return 0; } lldb::addr_t ProcessGDBRemote::DoAllocateMemory(size_t size, uint32_t permissions, Status &error) { Log *log( GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_EXPRESSIONS)); addr_t allocated_addr = LLDB_INVALID_ADDRESS; if (m_gdb_comm.SupportsAllocDeallocMemory() != eLazyBoolNo) { allocated_addr = m_gdb_comm.AllocateMemory(size, permissions); if (allocated_addr != LLDB_INVALID_ADDRESS || m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolYes) return allocated_addr; } if (m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolNo) { // Call mmap() to create memory in the inferior.. unsigned prot = 0; if (permissions & lldb::ePermissionsReadable) prot |= eMmapProtRead; if (permissions & lldb::ePermissionsWritable) prot |= eMmapProtWrite; if (permissions & lldb::ePermissionsExecutable) prot |= eMmapProtExec; if (InferiorCallMmap(this, allocated_addr, 0, size, prot, eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) m_addr_to_mmap_size[allocated_addr] = size; else { allocated_addr = LLDB_INVALID_ADDRESS; LLDB_LOGF(log, "ProcessGDBRemote::%s no direct stub support for memory " "allocation, and InferiorCallMmap also failed - is stub " "missing register context save/restore capability?", __FUNCTION__); } } if (allocated_addr == LLDB_INVALID_ADDRESS) error.SetErrorStringWithFormat( "unable to allocate %" PRIu64 " bytes of memory with permissions %s", (uint64_t)size, GetPermissionsAsCString(permissions)); else error.Clear(); return allocated_addr; } Status ProcessGDBRemote::GetMemoryRegionInfo(addr_t load_addr, MemoryRegionInfo ®ion_info) { Status error(m_gdb_comm.GetMemoryRegionInfo(load_addr, region_info)); return error; } Status ProcessGDBRemote::GetWatchpointSupportInfo(uint32_t &num) { Status error(m_gdb_comm.GetWatchpointSupportInfo(num)); return error; } Status ProcessGDBRemote::GetWatchpointSupportInfo(uint32_t &num, bool &after) { Status error(m_gdb_comm.GetWatchpointSupportInfo( num, after, GetTarget().GetArchitecture())); return error; } Status ProcessGDBRemote::DoDeallocateMemory(lldb::addr_t addr) { Status error; LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); switch (supported) { case eLazyBoolCalculate: // We should never be deallocating memory without allocating memory first // so we should never get eLazyBoolCalculate error.SetErrorString( "tried to deallocate memory without ever allocating memory"); break; case eLazyBoolYes: if (!m_gdb_comm.DeallocateMemory(addr)) error.SetErrorStringWithFormat( "unable to deallocate memory at 0x%" PRIx64, addr); break; case eLazyBoolNo: // Call munmap() to deallocate memory in the inferior.. { MMapMap::iterator pos = m_addr_to_mmap_size.find(addr); if (pos != m_addr_to_mmap_size.end() && InferiorCallMunmap(this, addr, pos->second)) m_addr_to_mmap_size.erase(pos); else error.SetErrorStringWithFormat( "unable to deallocate memory at 0x%" PRIx64, addr); } break; } return error; } // Process STDIO size_t ProcessGDBRemote::PutSTDIN(const char *src, size_t src_len, Status &error) { if (m_stdio_communication.IsConnected()) { ConnectionStatus status; m_stdio_communication.Write(src, src_len, status, nullptr); } else if (m_stdin_forward) { m_gdb_comm.SendStdinNotification(src, src_len); } return 0; } Status ProcessGDBRemote::EnableBreakpointSite(BreakpointSite *bp_site) { Status error; assert(bp_site != nullptr); // Get logging info Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS)); user_id_t site_id = bp_site->GetID(); // Get the breakpoint address const addr_t addr = bp_site->GetLoadAddress(); // Log that a breakpoint was requested LLDB_LOGF(log, "ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 ") address = 0x%" PRIx64, site_id, (uint64_t)addr); // Breakpoint already exists and is enabled if (bp_site->IsEnabled()) { LLDB_LOGF(log, "ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 ") address = 0x%" PRIx64 " -- SUCCESS (already enabled)", site_id, (uint64_t)addr); return error; } // Get the software breakpoint trap opcode size const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site); // SupportsGDBStoppointPacket() simply checks a boolean, indicating if this // breakpoint type is supported by the remote stub. These are set to true by // default, and later set to false only after we receive an unimplemented // response when sending a breakpoint packet. This means initially that // unless we were specifically instructed to use a hardware breakpoint, LLDB // will attempt to set a software breakpoint. HardwareRequired() also queries // a boolean variable which indicates if the user specifically asked for // hardware breakpoints. If true then we will skip over software // breakpoints. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware) && (!bp_site->HardwareRequired())) { // Try to send off a software breakpoint packet ($Z0) uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket( eBreakpointSoftware, true, addr, bp_op_size); if (error_no == 0) { // The breakpoint was placed successfully bp_site->SetEnabled(true); bp_site->SetType(BreakpointSite::eExternal); return error; } // SendGDBStoppointTypePacket() will return an error if it was unable to // set this breakpoint. We need to differentiate between a error specific // to placing this breakpoint or if we have learned that this breakpoint // type is unsupported. To do this, we must test the support boolean for // this breakpoint type to see if it now indicates that this breakpoint // type is unsupported. If they are still supported then we should return // with the error code. If they are now unsupported, then we would like to // fall through and try another form of breakpoint. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) { if (error_no != UINT8_MAX) error.SetErrorStringWithFormat( "error: %d sending the breakpoint request", error_no); else error.SetErrorString("error sending the breakpoint request"); return error; } // We reach here when software breakpoints have been found to be // unsupported. For future calls to set a breakpoint, we will not attempt // to set a breakpoint with a type that is known not to be supported. LLDB_LOGF(log, "Software breakpoints are unsupported"); // So we will fall through and try a hardware breakpoint } // The process of setting a hardware breakpoint is much the same as above. // We check the supported boolean for this breakpoint type, and if it is // thought to be supported then we will try to set this breakpoint with a // hardware breakpoint. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { // Try to send off a hardware breakpoint packet ($Z1) uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket( eBreakpointHardware, true, addr, bp_op_size); if (error_no == 0) { // The breakpoint was placed successfully bp_site->SetEnabled(true); bp_site->SetType(BreakpointSite::eHardware); return error; } // Check if the error was something other then an unsupported breakpoint // type if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { // Unable to set this hardware breakpoint if (error_no != UINT8_MAX) error.SetErrorStringWithFormat( "error: %d sending the hardware breakpoint request " "(hardware breakpoint resources might be exhausted or unavailable)", error_no); else error.SetErrorString("error sending the hardware breakpoint request " "(hardware breakpoint resources " "might be exhausted or unavailable)"); return error; } // We will reach here when the stub gives an unsupported response to a // hardware breakpoint LLDB_LOGF(log, "Hardware breakpoints are unsupported"); // Finally we will falling through to a #trap style breakpoint } // Don't fall through when hardware breakpoints were specifically requested if (bp_site->HardwareRequired()) { error.SetErrorString("hardware breakpoints are not supported"); return error; } // As a last resort we want to place a manual breakpoint. An instruction is // placed into the process memory using memory write packets. return EnableSoftwareBreakpoint(bp_site); } Status ProcessGDBRemote::DisableBreakpointSite(BreakpointSite *bp_site) { Status error; assert(bp_site != nullptr); addr_t addr = bp_site->GetLoadAddress(); user_id_t site_id = bp_site->GetID(); Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS)); LLDB_LOGF(log, "ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 ") addr = 0x%8.8" PRIx64, site_id, (uint64_t)addr); if (bp_site->IsEnabled()) { const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site); BreakpointSite::Type bp_type = bp_site->GetType(); switch (bp_type) { case BreakpointSite::eSoftware: error = DisableSoftwareBreakpoint(bp_site); break; case BreakpointSite::eHardware: if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointHardware, false, addr, bp_op_size)) error.SetErrorToGenericError(); break; case BreakpointSite::eExternal: { GDBStoppointType stoppoint_type; if (bp_site->IsHardware()) stoppoint_type = eBreakpointHardware; else stoppoint_type = eBreakpointSoftware; if (m_gdb_comm.SendGDBStoppointTypePacket(stoppoint_type, false, addr, bp_op_size)) error.SetErrorToGenericError(); } break; } if (error.Success()) bp_site->SetEnabled(false); } else { LLDB_LOGF(log, "ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", site_id, (uint64_t)addr); return error; } if (error.Success()) error.SetErrorToGenericError(); return error; } // Pre-requisite: wp != NULL. static GDBStoppointType GetGDBStoppointType(Watchpoint *wp) { assert(wp); bool watch_read = wp->WatchpointRead(); bool watch_write = wp->WatchpointWrite(); // watch_read and watch_write cannot both be false. assert(watch_read || watch_write); if (watch_read && watch_write) return eWatchpointReadWrite; else if (watch_read) return eWatchpointRead; else // Must be watch_write, then. return eWatchpointWrite; } Status ProcessGDBRemote::EnableWatchpoint(Watchpoint *wp, bool notify) { Status error; if (wp) { user_id_t watchID = wp->GetID(); addr_t addr = wp->GetLoadAddress(); Log *log( ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS)); LLDB_LOGF(log, "ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ")", watchID); if (wp->IsEnabled()) { LLDB_LOGF(log, "ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.", watchID, (uint64_t)addr); return error; } GDBStoppointType type = GetGDBStoppointType(wp); // Pass down an appropriate z/Z packet... if (m_gdb_comm.SupportsGDBStoppointPacket(type)) { if (m_gdb_comm.SendGDBStoppointTypePacket(type, true, addr, wp->GetByteSize()) == 0) { wp->SetEnabled(true, notify); return error; } else error.SetErrorString("sending gdb watchpoint packet failed"); } else error.SetErrorString("watchpoints not supported"); } else { error.SetErrorString("Watchpoint argument was NULL."); } if (error.Success()) error.SetErrorToGenericError(); return error; } Status ProcessGDBRemote::DisableWatchpoint(Watchpoint *wp, bool notify) { Status error; if (wp) { user_id_t watchID = wp->GetID(); Log *log( ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS)); addr_t addr = wp->GetLoadAddress(); LLDB_LOGF(log, "ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64, watchID, (uint64_t)addr); if (!wp->IsEnabled()) { LLDB_LOGF(log, "ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", watchID, (uint64_t)addr); // See also 'class WatchpointSentry' within StopInfo.cpp. This disabling // attempt might come from the user-supplied actions, we'll route it in // order for the watchpoint object to intelligently process this action. wp->SetEnabled(false, notify); return error; } if (wp->IsHardware()) { GDBStoppointType type = GetGDBStoppointType(wp); // Pass down an appropriate z/Z packet... if (m_gdb_comm.SendGDBStoppointTypePacket(type, false, addr, wp->GetByteSize()) == 0) { wp->SetEnabled(false, notify); return error; } else error.SetErrorString("sending gdb watchpoint packet failed"); } // TODO: clear software watchpoints if we implement them } else { error.SetErrorString("Watchpoint argument was NULL."); } if (error.Success()) error.SetErrorToGenericError(); return error; } void ProcessGDBRemote::Clear() { m_thread_list_real.Clear(); m_thread_list.Clear(); } Status ProcessGDBRemote::DoSignal(int signo) { Status error; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); LLDB_LOGF(log, "ProcessGDBRemote::DoSignal (signal = %d)", signo); if (!m_gdb_comm.SendAsyncSignal(signo)) error.SetErrorStringWithFormat("failed to send signal %i", signo); return error; } Status ProcessGDBRemote::ConnectToReplayServer() { Status status = m_gdb_replay_server.Connect(m_gdb_comm); if (status.Fail()) return status; // Enable replay mode. m_replay_mode = true; // Start server thread. m_gdb_replay_server.StartAsyncThread(); // Start client thread. StartAsyncThread(); // Do the usual setup. return ConnectToDebugserver(""); } Status ProcessGDBRemote::EstablishConnectionIfNeeded(const ProcessInfo &process_info) { // Make sure we aren't already connected? if (m_gdb_comm.IsConnected()) return Status(); PlatformSP platform_sp(GetTarget().GetPlatform()); if (platform_sp && !platform_sp->IsHost()) return Status("Lost debug server connection"); if (repro::Reproducer::Instance().IsReplaying()) return ConnectToReplayServer(); auto error = LaunchAndConnectToDebugserver(process_info); if (error.Fail()) { const char *error_string = error.AsCString(); if (error_string == nullptr) error_string = "unable to launch " DEBUGSERVER_BASENAME; } return error; } #if !defined(_WIN32) #define USE_SOCKETPAIR_FOR_LOCAL_CONNECTION 1 #endif #ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION static bool SetCloexecFlag(int fd) { #if defined(FD_CLOEXEC) int flags = ::fcntl(fd, F_GETFD); if (flags == -1) return false; return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0); #else return false; #endif } #endif Status ProcessGDBRemote::LaunchAndConnectToDebugserver( const ProcessInfo &process_info) { using namespace std::placeholders; // For _1, _2, etc. Status error; if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID) { // If we locate debugserver, keep that located version around static FileSpec g_debugserver_file_spec; ProcessLaunchInfo debugserver_launch_info; // Make debugserver run in its own session so signals generated by special // terminal key sequences (^C) don't affect debugserver. debugserver_launch_info.SetLaunchInSeparateProcessGroup(true); const std::weak_ptr this_wp = std::static_pointer_cast(shared_from_this()); debugserver_launch_info.SetMonitorProcessCallback( std::bind(MonitorDebugserverProcess, this_wp, _1, _2, _3, _4), false); debugserver_launch_info.SetUserID(process_info.GetUserID()); #if defined(__APPLE__) // On macOS 11, we need to support x86_64 applications translated to // arm64. We check whether a binary is translated and spawn the correct // debugserver accordingly. int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, static_cast(process_info.GetProcessID()) }; struct kinfo_proc processInfo; size_t bufsize = sizeof(processInfo); if (sysctl(mib, (unsigned)(sizeof(mib)/sizeof(int)), &processInfo, &bufsize, NULL, 0) == 0 && bufsize > 0) { if (processInfo.kp_proc.p_flag & P_TRANSLATED) { FileSpec rosetta_debugserver("/Library/Apple/usr/libexec/oah/debugserver"); debugserver_launch_info.SetExecutableFile(rosetta_debugserver, false); } } #endif int communication_fd = -1; #ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION // Use a socketpair on non-Windows systems for security and performance // reasons. int sockets[2]; /* the pair of socket descriptors */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) { error.SetErrorToErrno(); return error; } int our_socket = sockets[0]; int gdb_socket = sockets[1]; auto cleanup_our = llvm::make_scope_exit([&]() { close(our_socket); }); auto cleanup_gdb = llvm::make_scope_exit([&]() { close(gdb_socket); }); // Don't let any child processes inherit our communication socket SetCloexecFlag(our_socket); communication_fd = gdb_socket; #endif error = m_gdb_comm.StartDebugserverProcess( nullptr, GetTarget().GetPlatform().get(), debugserver_launch_info, nullptr, nullptr, communication_fd); if (error.Success()) m_debugserver_pid = debugserver_launch_info.GetProcessID(); else m_debugserver_pid = LLDB_INVALID_PROCESS_ID; if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) { #ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION // Our process spawned correctly, we can now set our connection to use // our end of the socket pair cleanup_our.release(); m_gdb_comm.SetConnection( std::make_unique(our_socket, true)); #endif StartAsyncThread(); } if (error.Fail()) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); LLDB_LOGF(log, "failed to start debugserver process: %s", error.AsCString()); return error; } if (m_gdb_comm.IsConnected()) { // Finish the connection process by doing the handshake without // connecting (send NULL URL) error = ConnectToDebugserver(""); } else { error.SetErrorString("connection failed"); } } return error; } bool ProcessGDBRemote::MonitorDebugserverProcess( std::weak_ptr process_wp, lldb::pid_t debugserver_pid, bool exited, // True if the process did exit int signo, // Zero for no signal int exit_status // Exit value of process if signal is zero ) { // "debugserver_pid" argument passed in is the process ID for debugserver // that we are tracking... Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); const bool handled = true; LLDB_LOGF(log, "ProcessGDBRemote::%s(process_wp, pid=%" PRIu64 ", signo=%i (0x%x), exit_status=%i)", __FUNCTION__, debugserver_pid, signo, signo, exit_status); std::shared_ptr process_sp = process_wp.lock(); LLDB_LOGF(log, "ProcessGDBRemote::%s(process = %p)", __FUNCTION__, static_cast(process_sp.get())); if (!process_sp || process_sp->m_debugserver_pid != debugserver_pid) return handled; // Sleep for a half a second to make sure our inferior process has time to // set its exit status before we set it incorrectly when both the debugserver // and the inferior process shut down. std::this_thread::sleep_for(std::chrono::milliseconds(500)); // If our process hasn't yet exited, debugserver might have died. If the // process did exit, then we are reaping it. const StateType state = process_sp->GetState(); if (state != eStateInvalid && state != eStateUnloaded && state != eStateExited && state != eStateDetached) { char error_str[1024]; if (signo) { const char *signal_cstr = process_sp->GetUnixSignals()->GetSignalAsCString(signo); if (signal_cstr) ::snprintf(error_str, sizeof(error_str), DEBUGSERVER_BASENAME " died with signal %s", signal_cstr); else ::snprintf(error_str, sizeof(error_str), DEBUGSERVER_BASENAME " died with signal %i", signo); } else { ::snprintf(error_str, sizeof(error_str), DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x", exit_status); } process_sp->SetExitStatus(-1, error_str); } // Debugserver has exited we need to let our ProcessGDBRemote know that it no // longer has a debugserver instance process_sp->m_debugserver_pid = LLDB_INVALID_PROCESS_ID; return handled; } void ProcessGDBRemote::KillDebugserverProcess() { m_gdb_comm.Disconnect(); if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) { Host::Kill(m_debugserver_pid, SIGINT); m_debugserver_pid = LLDB_INVALID_PROCESS_ID; } } void ProcessGDBRemote::Initialize() { static llvm::once_flag g_once_flag; llvm::call_once(g_once_flag, []() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, DebuggerInitialize); }); } void ProcessGDBRemote::DebuggerInitialize(Debugger &debugger) { if (!PluginManager::GetSettingForProcessPlugin( debugger, PluginProperties::GetSettingName())) { const bool is_global_setting = true; PluginManager::CreateSettingForProcessPlugin( debugger, GetGlobalPluginProperties()->GetValueProperties(), ConstString("Properties for the gdb-remote process plug-in."), is_global_setting); } } bool ProcessGDBRemote::StartAsyncThread() { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); LLDB_LOGF(log, "ProcessGDBRemote::%s ()", __FUNCTION__); std::lock_guard guard(m_async_thread_state_mutex); if (!m_async_thread.IsJoinable()) { // Create a thread that watches our internal state and controls which // events make it to clients (into the DCProcess event queue). llvm::Expected async_thread = ThreadLauncher::LaunchThread( "", ProcessGDBRemote::AsyncThread, this); if (!async_thread) { LLDB_LOG_ERROR(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST), async_thread.takeError(), "failed to launch host thread: {}"); return false; } m_async_thread = *async_thread; } else LLDB_LOGF(log, "ProcessGDBRemote::%s () - Called when Async thread was " "already running.", __FUNCTION__); return m_async_thread.IsJoinable(); } void ProcessGDBRemote::StopAsyncThread() { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); LLDB_LOGF(log, "ProcessGDBRemote::%s ()", __FUNCTION__); std::lock_guard guard(m_async_thread_state_mutex); if (m_async_thread.IsJoinable()) { m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncThreadShouldExit); // This will shut down the async thread. m_gdb_comm.Disconnect(); // Disconnect from the debug server. // Stop the stdio thread m_async_thread.Join(nullptr); m_async_thread.Reset(); } else LLDB_LOGF( log, "ProcessGDBRemote::%s () - Called when Async thread was not running.", __FUNCTION__); } bool ProcessGDBRemote::HandleNotifyPacket(StringExtractorGDBRemote &packet) { // get the packet at a string const std::string &pkt = std::string(packet.GetStringRef()); // skip %stop: StringExtractorGDBRemote stop_info(pkt.c_str() + 5); // pass as a thread stop info packet SetLastStopPacket(stop_info); // check for more stop reasons HandleStopReplySequence(); // if the process is stopped then we need to fake a resume so that we can // stop properly with the new break. This is possible due to // SetPrivateState() broadcasting the state change as a side effect. if (GetPrivateState() == lldb::StateType::eStateStopped) { SetPrivateState(lldb::StateType::eStateRunning); } // since we have some stopped packets we can halt the process SetPrivateState(lldb::StateType::eStateStopped); return true; } thread_result_t ProcessGDBRemote::AsyncThread(void *arg) { ProcessGDBRemote *process = (ProcessGDBRemote *)arg; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); LLDB_LOGF(log, "ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread starting...", __FUNCTION__, arg, process->GetID()); EventSP event_sp; bool done = false; while (!done) { LLDB_LOGF(log, "ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp)...", __FUNCTION__, arg, process->GetID()); if (process->m_async_listener_sp->GetEvent(event_sp, llvm::None)) { const uint32_t event_type = event_sp->GetType(); if (event_sp->BroadcasterIs(&process->m_async_broadcaster)) { LLDB_LOGF(log, "ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") Got an event of type: %d...", __FUNCTION__, arg, process->GetID(), event_type); switch (event_type) { case eBroadcastBitAsyncContinue: { const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event_sp.get()); if (continue_packet) { const char *continue_cstr = (const char *)continue_packet->GetBytes(); const size_t continue_cstr_len = continue_packet->GetByteSize(); LLDB_LOGF(log, "ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got eBroadcastBitAsyncContinue: %s", __FUNCTION__, arg, process->GetID(), continue_cstr); if (::strstr(continue_cstr, "vAttach") == nullptr) process->SetPrivateState(eStateRunning); StringExtractorGDBRemote response; // If in Non-Stop-Mode if (process->GetTarget().GetNonStopModeEnabled()) { // send the vCont packet if (!process->GetGDBRemote().SendvContPacket( llvm::StringRef(continue_cstr, continue_cstr_len), response)) { // Something went wrong done = true; break; } } // If in All-Stop-Mode else { StateType stop_state = process->GetGDBRemote().SendContinuePacketAndWaitForResponse( *process, *process->GetUnixSignals(), llvm::StringRef(continue_cstr, continue_cstr_len), response); // We need to immediately clear the thread ID list so we are sure // to get a valid list of threads. The thread ID list might be // contained within the "response", or the stop reply packet that // caused the stop. So clear it now before we give the stop reply // packet to the process using the // process->SetLastStopPacket()... process->ClearThreadIDList(); switch (stop_state) { case eStateStopped: case eStateCrashed: case eStateSuspended: process->SetLastStopPacket(response); process->SetPrivateState(stop_state); break; case eStateExited: { process->SetLastStopPacket(response); process->ClearThreadIDList(); response.SetFilePos(1); int exit_status = response.GetHexU8(); std::string desc_string; if (response.GetBytesLeft() > 0 && response.GetChar('-') == ';') { llvm::StringRef desc_str; llvm::StringRef desc_token; while (response.GetNameColonValue(desc_token, desc_str)) { if (desc_token != "description") continue; StringExtractor extractor(desc_str); extractor.GetHexByteString(desc_string); } } process->SetExitStatus(exit_status, desc_string.c_str()); done = true; break; } case eStateInvalid: { // Check to see if we were trying to attach and if we got back // the "E87" error code from debugserver -- this indicates that // the process is not debuggable. Return a slightly more // helpful error message about why the attach failed. if (::strstr(continue_cstr, "vAttach") != nullptr && response.GetError() == 0x87) { process->SetExitStatus(-1, "cannot attach to process due to " "System Integrity Protection"); } else if (::strstr(continue_cstr, "vAttach") != nullptr && response.GetStatus().Fail()) { process->SetExitStatus(-1, response.GetStatus().AsCString()); } else { process->SetExitStatus(-1, "lost connection"); } break; } default: process->SetPrivateState(stop_state); break; } // switch(stop_state) } // else // if in All-stop-mode } // if (continue_packet) } // case eBroadcastBitAsyncContinue break; case eBroadcastBitAsyncThreadShouldExit: LLDB_LOGF(log, "ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got eBroadcastBitAsyncThreadShouldExit...", __FUNCTION__, arg, process->GetID()); done = true; break; default: LLDB_LOGF(log, "ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type); done = true; break; } } else if (event_sp->BroadcasterIs(&process->m_gdb_comm)) { switch (event_type) { case Communication::eBroadcastBitReadThreadDidExit: process->SetExitStatus(-1, "lost connection"); done = true; break; case GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify: { lldb_private::Event *event = event_sp.get(); const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event); StringExtractorGDBRemote notify( (const char *)continue_packet->GetBytes()); // Hand this over to the process to handle process->HandleNotifyPacket(notify); break; } default: LLDB_LOGF(log, "ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type); done = true; break; } } } else { LLDB_LOGF(log, "ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp) => false", __FUNCTION__, arg, process->GetID()); done = true; } } LLDB_LOGF(log, "ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread exiting...", __FUNCTION__, arg, process->GetID()); return {}; } // uint32_t // ProcessGDBRemote::ListProcessesMatchingName (const char *name, StringList // &matches, std::vector &pids) //{ // // If we are planning to launch the debugserver remotely, then we need to // fire up a debugserver // // process and ask it for the list of processes. But if we are local, we // can let the Host do it. // if (m_local_debugserver) // { // return Host::ListProcessesMatchingName (name, matches, pids); // } // else // { // // FIXME: Implement talking to the remote debugserver. // return 0; // } // //} // bool ProcessGDBRemote::NewThreadNotifyBreakpointHit( void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) { // I don't think I have to do anything here, just make sure I notice the new // thread when it starts to // run so I can stop it if that's what I want to do. Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); LLDB_LOGF(log, "Hit New Thread Notification breakpoint."); return false; } Status ProcessGDBRemote::UpdateAutomaticSignalFiltering() { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); LLDB_LOG(log, "Check if need to update ignored signals"); // QPassSignals package is not supported by the server, there is no way we // can ignore any signals on server side. if (!m_gdb_comm.GetQPassSignalsSupported()) return Status(); // No signals, nothing to send. if (m_unix_signals_sp == nullptr) return Status(); // Signals' version hasn't changed, no need to send anything. uint64_t new_signals_version = m_unix_signals_sp->GetVersion(); if (new_signals_version == m_last_signals_version) { LLDB_LOG(log, "Signals' version hasn't changed. version={0}", m_last_signals_version); return Status(); } auto signals_to_ignore = m_unix_signals_sp->GetFilteredSignals(false, false, false); Status error = m_gdb_comm.SendSignalsToIgnore(signals_to_ignore); LLDB_LOG(log, "Signals' version changed. old version={0}, new version={1}, " "signals ignored={2}, update result={3}", m_last_signals_version, new_signals_version, signals_to_ignore.size(), error); if (error.Success()) m_last_signals_version = new_signals_version; return error; } bool ProcessGDBRemote::StartNoticingNewThreads() { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (m_thread_create_bp_sp) { if (log && log->GetVerbose()) LLDB_LOGF(log, "Enabled noticing new thread breakpoint."); m_thread_create_bp_sp->SetEnabled(true); } else { PlatformSP platform_sp(GetTarget().GetPlatform()); if (platform_sp) { m_thread_create_bp_sp = platform_sp->SetThreadCreationBreakpoint(GetTarget()); if (m_thread_create_bp_sp) { if (log && log->GetVerbose()) LLDB_LOGF( log, "Successfully created new thread notification breakpoint %i", m_thread_create_bp_sp->GetID()); m_thread_create_bp_sp->SetCallback( ProcessGDBRemote::NewThreadNotifyBreakpointHit, this, true); } else { LLDB_LOGF(log, "Failed to create new thread notification breakpoint."); } } } return m_thread_create_bp_sp.get() != nullptr; } bool ProcessGDBRemote::StopNoticingNewThreads() { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log && log->GetVerbose()) LLDB_LOGF(log, "Disabling new thread notification breakpoint."); if (m_thread_create_bp_sp) m_thread_create_bp_sp->SetEnabled(false); return true; } DynamicLoader *ProcessGDBRemote::GetDynamicLoader() { if (m_dyld_up.get() == nullptr) m_dyld_up.reset(DynamicLoader::FindPlugin(this, nullptr)); return m_dyld_up.get(); } Status ProcessGDBRemote::SendEventData(const char *data) { int return_value; bool was_supported; Status error; return_value = m_gdb_comm.SendLaunchEventDataPacket(data, &was_supported); if (return_value != 0) { if (!was_supported) error.SetErrorString("Sending events is not supported for this process."); else error.SetErrorStringWithFormat("Error sending event data: %d.", return_value); } return error; } DataExtractor ProcessGDBRemote::GetAuxvData() { DataBufferSP buf; if (m_gdb_comm.GetQXferAuxvReadSupported()) { std::string response_string; if (m_gdb_comm.SendPacketsAndConcatenateResponses("qXfer:auxv:read::", response_string) == GDBRemoteCommunication::PacketResult::Success) buf = std::make_shared(response_string.c_str(), response_string.length()); } return DataExtractor(buf, GetByteOrder(), GetAddressByteSize()); } StructuredData::ObjectSP ProcessGDBRemote::GetExtendedInfoForThread(lldb::tid_t tid) { StructuredData::ObjectSP object_sp; if (m_gdb_comm.GetThreadExtendedInfoSupported()) { StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); SystemRuntime *runtime = GetSystemRuntime(); if (runtime) { runtime->AddThreadExtendedInfoPacketHints(args_dict); } args_dict->GetAsDictionary()->AddIntegerItem("thread", tid); StreamString packet; packet << "jThreadExtendedInfo:"; args_dict->Dump(packet, false); // FIXME the final character of a JSON dictionary, '}', is the escape // character in gdb-remote binary mode. lldb currently doesn't escape // these characters in its packet output -- so we add the quoted version of // the } character here manually in case we talk to a debugserver which un- // escapes the characters at packet read time. packet << (char)(0x7d ^ 0x20); StringExtractorGDBRemote response; response.SetResponseValidatorToJSON(); if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, false) == GDBRemoteCommunication::PacketResult::Success) { StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { if (!response.Empty()) { object_sp = StructuredData::ParseJSON(std::string(response.GetStringRef())); } } } } return object_sp; } StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos( lldb::addr_t image_list_address, lldb::addr_t image_count) { StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); args_dict->GetAsDictionary()->AddIntegerItem("image_list_address", image_list_address); args_dict->GetAsDictionary()->AddIntegerItem("image_count", image_count); return GetLoadedDynamicLibrariesInfos_sender(args_dict); } StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos() { StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); args_dict->GetAsDictionary()->AddBooleanItem("fetch_all_solibs", true); return GetLoadedDynamicLibrariesInfos_sender(args_dict); } StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos( const std::vector &load_addresses) { StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); StructuredData::ArraySP addresses(new StructuredData::Array); for (auto addr : load_addresses) { StructuredData::ObjectSP addr_sp(new StructuredData::Integer(addr)); addresses->AddItem(addr_sp); } args_dict->GetAsDictionary()->AddItem("solib_addresses", addresses); return GetLoadedDynamicLibrariesInfos_sender(args_dict); } StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos_sender( StructuredData::ObjectSP args_dict) { StructuredData::ObjectSP object_sp; if (m_gdb_comm.GetLoadedDynamicLibrariesInfosSupported()) { // Scope for the scoped timeout object GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm, std::chrono::seconds(10)); StreamString packet; packet << "jGetLoadedDynamicLibrariesInfos:"; args_dict->Dump(packet, false); // FIXME the final character of a JSON dictionary, '}', is the escape // character in gdb-remote binary mode. lldb currently doesn't escape // these characters in its packet output -- so we add the quoted version of // the } character here manually in case we talk to a debugserver which un- // escapes the characters at packet read time. packet << (char)(0x7d ^ 0x20); StringExtractorGDBRemote response; response.SetResponseValidatorToJSON(); if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, false) == GDBRemoteCommunication::PacketResult::Success) { StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { if (!response.Empty()) { object_sp = StructuredData::ParseJSON(std::string(response.GetStringRef())); } } } } return object_sp; } StructuredData::ObjectSP ProcessGDBRemote::GetSharedCacheInfo() { StructuredData::ObjectSP object_sp; StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); if (m_gdb_comm.GetSharedCacheInfoSupported()) { StreamString packet; packet << "jGetSharedCacheInfo:"; args_dict->Dump(packet, false); // FIXME the final character of a JSON dictionary, '}', is the escape // character in gdb-remote binary mode. lldb currently doesn't escape // these characters in its packet output -- so we add the quoted version of // the } character here manually in case we talk to a debugserver which un- // escapes the characters at packet read time. packet << (char)(0x7d ^ 0x20); StringExtractorGDBRemote response; response.SetResponseValidatorToJSON(); if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, false) == GDBRemoteCommunication::PacketResult::Success) { StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { if (!response.Empty()) { object_sp = StructuredData::ParseJSON(std::string(response.GetStringRef())); } } } } return object_sp; } Status ProcessGDBRemote::ConfigureStructuredData( ConstString type_name, const StructuredData::ObjectSP &config_sp) { return m_gdb_comm.ConfigureRemoteStructuredData(type_name, config_sp); } // Establish the largest memory read/write payloads we should use. If the // remote stub has a max packet size, stay under that size. // // If the remote stub's max packet size is crazy large, use a reasonable // largeish default. // // If the remote stub doesn't advertise a max packet size, use a conservative // default. void ProcessGDBRemote::GetMaxMemorySize() { const uint64_t reasonable_largeish_default = 128 * 1024; const uint64_t conservative_default = 512; if (m_max_memory_size == 0) { uint64_t stub_max_size = m_gdb_comm.GetRemoteMaxPacketSize(); if (stub_max_size != UINT64_MAX && stub_max_size != 0) { // Save the stub's claimed maximum packet size m_remote_stub_max_memory_size = stub_max_size; // Even if the stub says it can support ginormous packets, don't exceed // our reasonable largeish default packet size. if (stub_max_size > reasonable_largeish_default) { stub_max_size = reasonable_largeish_default; } // Memory packet have other overheads too like Maddr,size:#NN Instead of // calculating the bytes taken by size and addr every time, we take a // maximum guess here. if (stub_max_size > 70) stub_max_size -= 32 + 32 + 6; else { // In unlikely scenario that max packet size is less then 70, we will // hope that data being written is small enough to fit. Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet( GDBR_LOG_COMM | GDBR_LOG_MEMORY)); if (log) log->Warning("Packet size is too small. " "LLDB may face problems while writing memory"); } m_max_memory_size = stub_max_size; } else { m_max_memory_size = conservative_default; } } } void ProcessGDBRemote::SetUserSpecifiedMaxMemoryTransferSize( uint64_t user_specified_max) { if (user_specified_max != 0) { GetMaxMemorySize(); if (m_remote_stub_max_memory_size != 0) { if (m_remote_stub_max_memory_size < user_specified_max) { m_max_memory_size = m_remote_stub_max_memory_size; // user specified a // packet size too // big, go as big // as the remote stub says we can go. } else { m_max_memory_size = user_specified_max; // user's packet size is good } } else { m_max_memory_size = user_specified_max; // user's packet size is probably fine } } } bool ProcessGDBRemote::GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, ModuleSpec &module_spec) { Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); const ModuleCacheKey key(module_file_spec.GetPath(), arch.GetTriple().getTriple()); auto cached = m_cached_module_specs.find(key); if (cached != m_cached_module_specs.end()) { module_spec = cached->second; return bool(module_spec); } if (!m_gdb_comm.GetModuleInfo(module_file_spec, arch, module_spec)) { LLDB_LOGF(log, "ProcessGDBRemote::%s - failed to get module info for %s:%s", __FUNCTION__, module_file_spec.GetPath().c_str(), arch.GetTriple().getTriple().c_str()); return false; } if (log) { StreamString stream; module_spec.Dump(stream); LLDB_LOGF(log, "ProcessGDBRemote::%s - got module info for (%s:%s) : %s", __FUNCTION__, module_file_spec.GetPath().c_str(), arch.GetTriple().getTriple().c_str(), stream.GetData()); } m_cached_module_specs[key] = module_spec; return true; } void ProcessGDBRemote::PrefetchModuleSpecs( llvm::ArrayRef module_file_specs, const llvm::Triple &triple) { auto module_specs = m_gdb_comm.GetModulesInfo(module_file_specs, triple); if (module_specs) { for (const FileSpec &spec : module_file_specs) m_cached_module_specs[ModuleCacheKey(spec.GetPath(), triple.getTriple())] = ModuleSpec(); for (const ModuleSpec &spec : *module_specs) m_cached_module_specs[ModuleCacheKey(spec.GetFileSpec().GetPath(), triple.getTriple())] = spec; } } llvm::VersionTuple ProcessGDBRemote::GetHostOSVersion() { return m_gdb_comm.GetOSVersion(); } llvm::VersionTuple ProcessGDBRemote::GetHostMacCatalystVersion() { return m_gdb_comm.GetMacCatalystVersion(); } namespace { typedef std::vector stringVec; typedef std::vector GDBServerRegisterVec; struct RegisterSetInfo { ConstString name; }; typedef std::map RegisterSetMap; struct GdbServerTargetInfo { std::string arch; std::string osabi; stringVec includes; RegisterSetMap reg_set_map; }; bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemoteDynamicRegisterInfo &dyn_reg_info, ABISP abi_sp, uint32_t &cur_reg_num, uint32_t ®_offset) { if (!feature_node) return false; feature_node.ForEachChildElementWithName( "reg", [&target_info, &dyn_reg_info, &cur_reg_num, ®_offset, &abi_sp](const XMLNode ®_node) -> bool { std::string gdb_group; std::string gdb_type; ConstString reg_name; ConstString alt_name; ConstString set_name; std::vector value_regs; std::vector invalidate_regs; std::vector dwarf_opcode_bytes; bool encoding_set = false; bool format_set = false; RegisterInfo reg_info = { nullptr, // Name nullptr, // Alt name 0, // byte size reg_offset, // offset eEncodingUint, // encoding eFormatHex, // format { LLDB_INVALID_REGNUM, // eh_frame reg num LLDB_INVALID_REGNUM, // DWARF reg num LLDB_INVALID_REGNUM, // generic reg num cur_reg_num, // process plugin reg num cur_reg_num // native register number }, nullptr, nullptr, nullptr, // Dwarf Expression opcode bytes pointer 0 // Dwarf Expression opcode bytes length }; reg_node.ForEachAttribute([&target_info, &gdb_group, &gdb_type, ®_name, &alt_name, &set_name, &value_regs, &invalidate_regs, &encoding_set, &format_set, ®_info, ®_offset, &dwarf_opcode_bytes]( const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "name") { reg_name.SetString(value); } else if (name == "bitsize") { reg_info.byte_size = StringConvert::ToUInt32(value.data(), 0, 0) / CHAR_BIT; } else if (name == "type") { gdb_type = value.str(); } else if (name == "group") { gdb_group = value.str(); } else if (name == "regnum") { const uint32_t regnum = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); if (regnum != LLDB_INVALID_REGNUM) { reg_info.kinds[eRegisterKindProcessPlugin] = regnum; } } else if (name == "offset") { reg_offset = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); } else if (name == "altname") { alt_name.SetString(value); } else if (name == "encoding") { encoding_set = true; reg_info.encoding = Args::StringToEncoding(value, eEncodingUint); } else if (name == "format") { format_set = true; Format format = eFormatInvalid; if (OptionArgParser::ToFormat(value.data(), format, nullptr) .Success()) reg_info.format = format; else if (value == "vector-sint8") reg_info.format = eFormatVectorOfSInt8; else if (value == "vector-uint8") reg_info.format = eFormatVectorOfUInt8; else if (value == "vector-sint16") reg_info.format = eFormatVectorOfSInt16; else if (value == "vector-uint16") reg_info.format = eFormatVectorOfUInt16; else if (value == "vector-sint32") reg_info.format = eFormatVectorOfSInt32; else if (value == "vector-uint32") reg_info.format = eFormatVectorOfUInt32; else if (value == "vector-float32") reg_info.format = eFormatVectorOfFloat32; else if (value == "vector-uint64") reg_info.format = eFormatVectorOfUInt64; else if (value == "vector-uint128") reg_info.format = eFormatVectorOfUInt128; } else if (name == "group_id") { const uint32_t set_id = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); RegisterSetMap::const_iterator pos = target_info.reg_set_map.find(set_id); if (pos != target_info.reg_set_map.end()) set_name = pos->second.name; } else if (name == "gcc_regnum" || name == "ehframe_regnum") { reg_info.kinds[eRegisterKindEHFrame] = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); } else if (name == "dwarf_regnum") { reg_info.kinds[eRegisterKindDWARF] = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); } else if (name == "generic") { reg_info.kinds[eRegisterKindGeneric] = Args::StringToGenericRegister(value); } else if (name == "value_regnums") { SplitCommaSeparatedRegisterNumberString(value, value_regs, 0); } else if (name == "invalidate_regnums") { SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 0); } else if (name == "dynamic_size_dwarf_expr_bytes") { std::string opcode_string = value.str(); size_t dwarf_opcode_len = opcode_string.length() / 2; assert(dwarf_opcode_len > 0); dwarf_opcode_bytes.resize(dwarf_opcode_len); reg_info.dynamic_size_dwarf_len = dwarf_opcode_len; StringExtractor opcode_extractor(opcode_string); uint32_t ret_val = opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes); assert(dwarf_opcode_len == ret_val); UNUSED_IF_ASSERT_DISABLED(ret_val); reg_info.dynamic_size_dwarf_expr_bytes = dwarf_opcode_bytes.data(); } else { printf("unhandled attribute %s = %s\n", name.data(), value.data()); } return true; // Keep iterating through all attributes }); if (!gdb_type.empty() && !(encoding_set || format_set)) { if (llvm::StringRef(gdb_type).startswith("int")) { reg_info.format = eFormatHex; reg_info.encoding = eEncodingUint; } else if (gdb_type == "data_ptr" || gdb_type == "code_ptr") { reg_info.format = eFormatAddressInfo; reg_info.encoding = eEncodingUint; } else if (gdb_type == "i387_ext" || gdb_type == "float") { reg_info.format = eFormatFloat; reg_info.encoding = eEncodingIEEE754; } } // Only update the register set name if we didn't get a "reg_set" // attribute. "set_name" will be empty if we didn't have a "reg_set" // attribute. if (!set_name) { if (!gdb_group.empty()) { set_name.SetCString(gdb_group.c_str()); } else { // If no register group name provided anywhere, // we'll create a 'general' register set set_name.SetCString("general"); } } reg_info.byte_offset = reg_offset; assert(reg_info.byte_size != 0); reg_offset += reg_info.byte_size; if (!value_regs.empty()) { value_regs.push_back(LLDB_INVALID_REGNUM); reg_info.value_regs = value_regs.data(); } if (!invalidate_regs.empty()) { invalidate_regs.push_back(LLDB_INVALID_REGNUM); reg_info.invalidate_regs = invalidate_regs.data(); } ++cur_reg_num; reg_info.name = reg_name.AsCString(); if (abi_sp) abi_sp->AugmentRegisterInfo(reg_info); dyn_reg_info.AddRegister(reg_info, reg_name, alt_name, set_name); return true; // Keep iterating through all "reg" elements }); return true; } } // namespace // This method fetches a register description feature xml file from // the remote stub and adds registers/register groupsets/architecture // information to the current process. It will call itself recursively // for nested register definition files. It returns true if it was able // to fetch and parse an xml file. bool ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess( ArchSpec &arch_to_use, std::string xml_filename, uint32_t &cur_reg_num, uint32_t ®_offset) { // request the target xml file std::string raw; lldb_private::Status lldberr; if (!m_gdb_comm.ReadExtFeature(ConstString("features"), ConstString(xml_filename.c_str()), raw, lldberr)) { return false; } XMLDocument xml_document; if (xml_document.ParseMemory(raw.c_str(), raw.size(), xml_filename.c_str())) { GdbServerTargetInfo target_info; std::vector feature_nodes; // The top level feature XML file will start with a tag. XMLNode target_node = xml_document.GetRootElement("target"); if (target_node) { target_node.ForEachChildElement([&target_info, &feature_nodes]( const XMLNode &node) -> bool { llvm::StringRef name = node.GetName(); if (name == "architecture") { node.GetElementText(target_info.arch); } else if (name == "osabi") { node.GetElementText(target_info.osabi); } else if (name == "xi:include" || name == "include") { llvm::StringRef href = node.GetAttributeValue("href"); if (!href.empty()) target_info.includes.push_back(href.str()); } else if (name == "feature") { feature_nodes.push_back(node); } else if (name == "groups") { node.ForEachChildElementWithName( "group", [&target_info](const XMLNode &node) -> bool { uint32_t set_id = UINT32_MAX; RegisterSetInfo set_info; node.ForEachAttribute( [&set_id, &set_info](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "id") set_id = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); if (name == "name") set_info.name = ConstString(value); return true; // Keep iterating through all attributes }); if (set_id != UINT32_MAX) target_info.reg_set_map[set_id] = set_info; return true; // Keep iterating through all "group" elements }); } return true; // Keep iterating through all children of the target_node }); } else { // In an included XML feature file, we're already "inside" the // tag of the initial XML file; this included file will likely only have // a tag. Need to check for any more included files in this // element. XMLNode feature_node = xml_document.GetRootElement("feature"); if (feature_node) { feature_nodes.push_back(feature_node); feature_node.ForEachChildElement([&target_info]( const XMLNode &node) -> bool { llvm::StringRef name = node.GetName(); if (name == "xi:include" || name == "include") { llvm::StringRef href = node.GetAttributeValue("href"); if (!href.empty()) target_info.includes.push_back(href.str()); } return true; }); } } // If the target.xml includes an architecture entry like // i386:x86-64 (seen from VMWare ESXi) // arm (seen from Segger JLink on unspecified arm board) // use that if we don't have anything better. if (!arch_to_use.IsValid() && !target_info.arch.empty()) { if (target_info.arch == "i386:x86-64") { // We don't have any information about vendor or OS. arch_to_use.SetTriple("x86_64--"); GetTarget().MergeArchitecture(arch_to_use); } // SEGGER J-Link jtag boards send this very-generic arch name, // we'll need to use this if we have absolutely nothing better // to work with or the register definitions won't be accepted. if (target_info.arch == "arm") { arch_to_use.SetTriple("arm--"); GetTarget().MergeArchitecture(arch_to_use); } } if (arch_to_use.IsValid()) { // Don't use Process::GetABI, this code gets called from DidAttach, and // in that context we haven't set the Target's architecture yet, so the // ABI is also potentially incorrect. ABISP abi_to_use_sp = ABI::FindPlugin(shared_from_this(), arch_to_use); for (auto &feature_node : feature_nodes) { ParseRegisters(feature_node, target_info, this->m_register_info, abi_to_use_sp, cur_reg_num, reg_offset); } for (const auto &include : target_info.includes) { GetGDBServerRegisterInfoXMLAndProcess(arch_to_use, include, cur_reg_num, reg_offset); } } } else { return false; } return true; } // query the target of gdb-remote for extended target information returns // true on success (got register definitions), false on failure (did not). bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { // Make sure LLDB has an XML parser it can use first if (!XMLDocument::XMLEnabled()) return false; // check that we have extended feature read support if (!m_gdb_comm.GetQXferFeaturesReadSupported()) return false; uint32_t cur_reg_num = 0; uint32_t reg_offset = 0; if (GetGDBServerRegisterInfoXMLAndProcess (arch_to_use, "target.xml", cur_reg_num, reg_offset)) this->m_register_info.Finalize(arch_to_use); return m_register_info.GetNumRegisters() > 0; } llvm::Expected ProcessGDBRemote::GetLoadedModuleList() { // Make sure LLDB has an XML parser it can use first if (!XMLDocument::XMLEnabled()) return llvm::createStringError(llvm::inconvertibleErrorCode(), "XML parsing not available"); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS); LLDB_LOGF(log, "ProcessGDBRemote::%s", __FUNCTION__); LoadedModuleInfoList list; GDBRemoteCommunicationClient &comm = m_gdb_comm; bool can_use_svr4 = GetGlobalPluginProperties()->GetUseSVR4(); // check that we have extended feature read support if (can_use_svr4 && comm.GetQXferLibrariesSVR4ReadSupported()) { // request the loaded library list std::string raw; lldb_private::Status lldberr; if (!comm.ReadExtFeature(ConstString("libraries-svr4"), ConstString(""), raw, lldberr)) return llvm::createStringError(llvm::inconvertibleErrorCode(), "Error in libraries-svr4 packet"); // parse the xml file in memory LLDB_LOGF(log, "parsing: %s", raw.c_str()); XMLDocument doc; if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) return llvm::createStringError(llvm::inconvertibleErrorCode(), "Error reading noname.xml"); XMLNode root_element = doc.GetRootElement("library-list-svr4"); if (!root_element) return llvm::createStringError( llvm::inconvertibleErrorCode(), "Error finding library-list-svr4 xml element"); // main link map structure llvm::StringRef main_lm = root_element.GetAttributeValue("main-lm"); if (!main_lm.empty()) { list.m_link_map = StringConvert::ToUInt64(main_lm.data(), LLDB_INVALID_ADDRESS, 0); } root_element.ForEachChildElementWithName( "library", [log, &list](const XMLNode &library) -> bool { LoadedModuleInfoList::LoadedModuleInfo module; library.ForEachAttribute( [&module](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "name") module.set_name(value.str()); else if (name == "lm") { // the address of the link_map struct. module.set_link_map(StringConvert::ToUInt64( value.data(), LLDB_INVALID_ADDRESS, 0)); } else if (name == "l_addr") { // the displacement as read from the field 'l_addr' of the // link_map struct. module.set_base(StringConvert::ToUInt64( value.data(), LLDB_INVALID_ADDRESS, 0)); // base address is always a displacement, not an absolute // value. module.set_base_is_offset(true); } else if (name == "l_ld") { // the memory address of the libraries PT_DYNAMIC section. module.set_dynamic(StringConvert::ToUInt64( value.data(), LLDB_INVALID_ADDRESS, 0)); } return true; // Keep iterating over all properties of "library" }); if (log) { std::string name; lldb::addr_t lm = 0, base = 0, ld = 0; bool base_is_offset; module.get_name(name); module.get_link_map(lm); module.get_base(base); module.get_base_is_offset(base_is_offset); module.get_dynamic(ld); LLDB_LOGF(log, "found (link_map:0x%08" PRIx64 ", base:0x%08" PRIx64 "[%s], ld:0x%08" PRIx64 ", name:'%s')", lm, base, (base_is_offset ? "offset" : "absolute"), ld, name.c_str()); } list.add(module); return true; // Keep iterating over all "library" elements in the root // node }); if (log) LLDB_LOGF(log, "found %" PRId32 " modules in total", (int)list.m_list.size()); return list; } else if (comm.GetQXferLibrariesReadSupported()) { // request the loaded library list std::string raw; lldb_private::Status lldberr; if (!comm.ReadExtFeature(ConstString("libraries"), ConstString(""), raw, lldberr)) return llvm::createStringError(llvm::inconvertibleErrorCode(), "Error in libraries packet"); LLDB_LOGF(log, "parsing: %s", raw.c_str()); XMLDocument doc; if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) return llvm::createStringError(llvm::inconvertibleErrorCode(), "Error reading noname.xml"); XMLNode root_element = doc.GetRootElement("library-list"); if (!root_element) return llvm::createStringError(llvm::inconvertibleErrorCode(), "Error finding library-list xml element"); root_element.ForEachChildElementWithName( "library", [log, &list](const XMLNode &library) -> bool { LoadedModuleInfoList::LoadedModuleInfo module; llvm::StringRef name = library.GetAttributeValue("name"); module.set_name(name.str()); // The base address of a given library will be the address of its // first section. Most remotes send only one section for Windows // targets for example. const XMLNode §ion = library.FindFirstChildElementWithName("section"); llvm::StringRef address = section.GetAttributeValue("address"); module.set_base( StringConvert::ToUInt64(address.data(), LLDB_INVALID_ADDRESS, 0)); // These addresses are absolute values. module.set_base_is_offset(false); if (log) { std::string name; lldb::addr_t base = 0; bool base_is_offset; module.get_name(name); module.get_base(base); module.get_base_is_offset(base_is_offset); LLDB_LOGF(log, "found (base:0x%08" PRIx64 "[%s], name:'%s')", base, (base_is_offset ? "offset" : "absolute"), name.c_str()); } list.add(module); return true; // Keep iterating over all "library" elements in the root // node }); if (log) LLDB_LOGF(log, "found %" PRId32 " modules in total", (int)list.m_list.size()); return list; } else { return llvm::createStringError(llvm::inconvertibleErrorCode(), "Remote libraries not supported"); } } lldb::ModuleSP ProcessGDBRemote::LoadModuleAtAddress(const FileSpec &file, lldb::addr_t link_map, lldb::addr_t base_addr, bool value_is_offset) { DynamicLoader *loader = GetDynamicLoader(); if (!loader) return nullptr; return loader->LoadModuleAtAddress(file, link_map, base_addr, value_is_offset); } llvm::Error ProcessGDBRemote::LoadModules() { using lldb_private::process_gdb_remote::ProcessGDBRemote; // request a list of loaded libraries from GDBServer llvm::Expected module_list = GetLoadedModuleList(); if (!module_list) return module_list.takeError(); // get a list of all the modules ModuleList new_modules; for (LoadedModuleInfoList::LoadedModuleInfo &modInfo : module_list->m_list) { std::string mod_name; lldb::addr_t mod_base; lldb::addr_t link_map; bool mod_base_is_offset; bool valid = true; valid &= modInfo.get_name(mod_name); valid &= modInfo.get_base(mod_base); valid &= modInfo.get_base_is_offset(mod_base_is_offset); if (!valid) continue; if (!modInfo.get_link_map(link_map)) link_map = LLDB_INVALID_ADDRESS; FileSpec file(mod_name); FileSystem::Instance().Resolve(file); lldb::ModuleSP module_sp = LoadModuleAtAddress(file, link_map, mod_base, mod_base_is_offset); if (module_sp.get()) new_modules.Append(module_sp); } if (new_modules.GetSize() > 0) { ModuleList removed_modules; Target &target = GetTarget(); ModuleList &loaded_modules = m_process->GetTarget().GetImages(); for (size_t i = 0; i < loaded_modules.GetSize(); ++i) { const lldb::ModuleSP loaded_module = loaded_modules.GetModuleAtIndex(i); bool found = false; for (size_t j = 0; j < new_modules.GetSize(); ++j) { if (new_modules.GetModuleAtIndex(j).get() == loaded_module.get()) found = true; } // The main executable will never be included in libraries-svr4, don't // remove it if (!found && loaded_module.get() != target.GetExecutableModulePointer()) { removed_modules.Append(loaded_module); } } loaded_modules.Remove(removed_modules); m_process->GetTarget().ModulesDidUnload(removed_modules, false); new_modules.ForEach([&target](const lldb::ModuleSP module_sp) -> bool { lldb_private::ObjectFile *obj = module_sp->GetObjectFile(); if (!obj) return true; if (obj->GetType() != ObjectFile::Type::eTypeExecutable) return true; lldb::ModuleSP module_copy_sp = module_sp; target.SetExecutableModule(module_copy_sp, eLoadDependentsNo); return false; }); loaded_modules.AppendIfNeeded(new_modules); m_process->GetTarget().ModulesDidLoad(new_modules); } return llvm::ErrorSuccess(); } Status ProcessGDBRemote::GetFileLoadAddress(const FileSpec &file, bool &is_loaded, lldb::addr_t &load_addr) { is_loaded = false; load_addr = LLDB_INVALID_ADDRESS; std::string file_path = file.GetPath(false); if (file_path.empty()) return Status("Empty file name specified"); StreamString packet; packet.PutCString("qFileLoadAddress:"); packet.PutStringAsRawHex8(file_path); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, false) != GDBRemoteCommunication::PacketResult::Success) return Status("Sending qFileLoadAddress packet failed"); if (response.IsErrorResponse()) { if (response.GetError() == 1) { // The file is not loaded into the inferior is_loaded = false; load_addr = LLDB_INVALID_ADDRESS; return Status(); } return Status( "Fetching file load address from remote server returned an error"); } if (response.IsNormalResponse()) { is_loaded = true; load_addr = response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); return Status(); } return Status( "Unknown error happened during sending the load address packet"); } void ProcessGDBRemote::ModulesDidLoad(ModuleList &module_list) { // We must call the lldb_private::Process::ModulesDidLoad () first before we // do anything Process::ModulesDidLoad(module_list); // After loading shared libraries, we can ask our remote GDB server if it // needs any symbols. m_gdb_comm.ServeSymbolLookups(this); } void ProcessGDBRemote::HandleAsyncStdout(llvm::StringRef out) { AppendSTDOUT(out.data(), out.size()); } static const char *end_delimiter = "--end--;"; static const int end_delimiter_len = 8; void ProcessGDBRemote::HandleAsyncMisc(llvm::StringRef data) { std::string input = data.str(); // '1' to move beyond 'A' if (m_partial_profile_data.length() > 0) { m_partial_profile_data.append(input); input = m_partial_profile_data; m_partial_profile_data.clear(); } size_t found, pos = 0, len = input.length(); while ((found = input.find(end_delimiter, pos)) != std::string::npos) { StringExtractorGDBRemote profileDataExtractor( input.substr(pos, found).c_str()); std::string profile_data = HarmonizeThreadIdsForProfileData(profileDataExtractor); BroadcastAsyncProfileData(profile_data); pos = found + end_delimiter_len; } if (pos < len) { // Last incomplete chunk. m_partial_profile_data = input.substr(pos); } } std::string ProcessGDBRemote::HarmonizeThreadIdsForProfileData( StringExtractorGDBRemote &profileDataExtractor) { std::map new_thread_id_to_used_usec_map; std::string output; llvm::raw_string_ostream output_stream(output); llvm::StringRef name, value; // Going to assuming thread_used_usec comes first, else bail out. while (profileDataExtractor.GetNameColonValue(name, value)) { if (name.compare("thread_used_id") == 0) { StringExtractor threadIDHexExtractor(value); uint64_t thread_id = threadIDHexExtractor.GetHexMaxU64(false, 0); bool has_used_usec = false; uint32_t curr_used_usec = 0; llvm::StringRef usec_name, usec_value; uint32_t input_file_pos = profileDataExtractor.GetFilePos(); if (profileDataExtractor.GetNameColonValue(usec_name, usec_value)) { if (usec_name.equals("thread_used_usec")) { has_used_usec = true; usec_value.getAsInteger(0, curr_used_usec); } else { // We didn't find what we want, it is probably an older version. Bail // out. profileDataExtractor.SetFilePos(input_file_pos); } } if (has_used_usec) { uint32_t prev_used_usec = 0; std::map::iterator iterator = m_thread_id_to_used_usec_map.find(thread_id); if (iterator != m_thread_id_to_used_usec_map.end()) { prev_used_usec = m_thread_id_to_used_usec_map[thread_id]; } uint32_t real_used_usec = curr_used_usec - prev_used_usec; // A good first time record is one that runs for at least 0.25 sec bool good_first_time = (prev_used_usec == 0) && (real_used_usec > 250000); bool good_subsequent_time = (prev_used_usec > 0) && ((real_used_usec > 0) || (HasAssignedIndexIDToThread(thread_id))); if (good_first_time || good_subsequent_time) { // We try to avoid doing too many index id reservation, resulting in // fast increase of index ids. output_stream << name << ":"; int32_t index_id = AssignIndexIDToThread(thread_id); output_stream << index_id << ";"; output_stream << usec_name << ":" << usec_value << ";"; } else { // Skip past 'thread_used_name'. llvm::StringRef local_name, local_value; profileDataExtractor.GetNameColonValue(local_name, local_value); } // Store current time as previous time so that they can be compared // later. new_thread_id_to_used_usec_map[thread_id] = curr_used_usec; } else { // Bail out and use old string. output_stream << name << ":" << value << ";"; } } else { output_stream << name << ":" << value << ";"; } } output_stream << end_delimiter; m_thread_id_to_used_usec_map = new_thread_id_to_used_usec_map; return output_stream.str(); } void ProcessGDBRemote::HandleStopReply() { if (GetStopID() != 0) return; if (GetID() == LLDB_INVALID_PROCESS_ID) { lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); if (pid != LLDB_INVALID_PROCESS_ID) SetID(pid); } BuildDynamicRegisterInfo(true); } static const char *const s_async_json_packet_prefix = "JSON-async:"; static StructuredData::ObjectSP ParseStructuredDataPacket(llvm::StringRef packet) { Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (!packet.consume_front(s_async_json_packet_prefix)) { if (log) { LLDB_LOGF( log, "GDBRemoteCommunicationClientBase::%s() received $J packet " "but was not a StructuredData packet: packet starts with " "%s", __FUNCTION__, packet.slice(0, strlen(s_async_json_packet_prefix)).str().c_str()); } return StructuredData::ObjectSP(); } // This is an asynchronous JSON packet, destined for a StructuredDataPlugin. StructuredData::ObjectSP json_sp = StructuredData::ParseJSON(std::string(packet)); if (log) { if (json_sp) { StreamString json_str; json_sp->Dump(json_str, true); json_str.Flush(); LLDB_LOGF(log, "ProcessGDBRemote::%s() " "received Async StructuredData packet: %s", __FUNCTION__, json_str.GetData()); } else { LLDB_LOGF(log, "ProcessGDBRemote::%s" "() received StructuredData packet:" " parse failure", __FUNCTION__); } } return json_sp; } void ProcessGDBRemote::HandleAsyncStructuredDataPacket(llvm::StringRef data) { auto structured_data_sp = ParseStructuredDataPacket(data); if (structured_data_sp) RouteAsyncStructuredData(structured_data_sp); } class CommandObjectProcessGDBRemoteSpeedTest : public CommandObjectParsed { public: CommandObjectProcessGDBRemoteSpeedTest(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process plugin packet speed-test", "Tests packet speeds of various sizes to determine " "the performance characteristics of the GDB remote " "connection. ", nullptr), m_option_group(), m_num_packets(LLDB_OPT_SET_1, false, "count", 'c', 0, eArgTypeCount, "The number of packets to send of each varying size " "(default is 1000).", 1000), m_max_send(LLDB_OPT_SET_1, false, "max-send", 's', 0, eArgTypeCount, "The maximum number of bytes to send in a packet. Sizes " "increase in powers of 2 while the size is less than or " "equal to this option value. (default 1024).", 1024), m_max_recv(LLDB_OPT_SET_1, false, "max-receive", 'r', 0, eArgTypeCount, "The maximum number of bytes to receive in a packet. Sizes " "increase in powers of 2 while the size is less than or " "equal to this option value. (default 1024).", 1024), m_json(LLDB_OPT_SET_1, false, "json", 'j', "Print the output as JSON data for easy parsing.", false, true) { m_option_group.Append(&m_num_packets, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_max_send, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_max_recv, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_json, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectProcessGDBRemoteSpeedTest() override {} Options *GetOptions() override { return &m_option_group; } bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext() .GetProcessPtr(); if (process) { StreamSP output_stream_sp( m_interpreter.GetDebugger().GetAsyncOutputStream()); result.SetImmediateOutputStream(output_stream_sp); const uint32_t num_packets = (uint32_t)m_num_packets.GetOptionValue().GetCurrentValue(); const uint64_t max_send = m_max_send.GetOptionValue().GetCurrentValue(); const uint64_t max_recv = m_max_recv.GetOptionValue().GetCurrentValue(); const bool json = m_json.GetOptionValue().GetCurrentValue(); const uint64_t k_recv_amount = 4 * 1024 * 1024; // Receive amount in bytes process->GetGDBRemote().TestPacketSpeed( num_packets, max_send, max_recv, k_recv_amount, json, output_stream_sp ? *output_stream_sp : result.GetOutputStream()); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } else { result.AppendErrorWithFormat("'%s' takes no arguments", m_cmd_name.c_str()); } result.SetStatus(eReturnStatusFailed); return false; } protected: OptionGroupOptions m_option_group; OptionGroupUInt64 m_num_packets; OptionGroupUInt64 m_max_send; OptionGroupUInt64 m_max_recv; OptionGroupBoolean m_json; }; class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed { private: public: CommandObjectProcessGDBRemotePacketHistory(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process plugin packet history", "Dumps the packet history buffer. ", nullptr) {} ~CommandObjectProcessGDBRemotePacketHistory() override {} bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext() .GetProcessPtr(); if (process) { process->GetGDBRemote().DumpHistory(result.GetOutputStream()); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } else { result.AppendErrorWithFormat("'%s' takes no arguments", m_cmd_name.c_str()); } result.SetStatus(eReturnStatusFailed); return false; } }; class CommandObjectProcessGDBRemotePacketXferSize : public CommandObjectParsed { private: public: CommandObjectProcessGDBRemotePacketXferSize(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "process plugin packet xfer-size", "Maximum size that lldb will try to read/write one one chunk.", nullptr) {} ~CommandObjectProcessGDBRemotePacketXferSize() override {} bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { result.AppendErrorWithFormat("'%s' takes an argument to specify the max " "amount to be transferred when " "reading/writing", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { const char *packet_size = command.GetArgumentAtIndex(0); errno = 0; uint64_t user_specified_max = strtoul(packet_size, nullptr, 10); if (errno == 0 && user_specified_max != 0) { process->SetUserSpecifiedMaxMemoryTransferSize(user_specified_max); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } result.SetStatus(eReturnStatusFailed); return false; } }; class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed { private: public: CommandObjectProcessGDBRemotePacketSend(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process plugin packet send", "Send a custom packet through the GDB remote " "protocol and print the answer. " "The packet header and footer will automatically " "be added to the packet prior to sending and " "stripped from the result.", nullptr) {} ~CommandObjectProcessGDBRemotePacketSend() override {} bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { result.AppendErrorWithFormat( "'%s' takes a one or more packet content arguments", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { for (size_t i = 0; i < argc; ++i) { const char *packet_cstr = command.GetArgumentAtIndex(0); bool send_async = true; StringExtractorGDBRemote response; process->GetGDBRemote().SendPacketAndWaitForResponse( packet_cstr, response, send_async); result.SetStatus(eReturnStatusSuccessFinishResult); Stream &output_strm = result.GetOutputStream(); output_strm.Printf(" packet: %s\n", packet_cstr); std::string response_str = std::string(response.GetStringRef()); if (strstr(packet_cstr, "qGetProfileData") != nullptr) { response_str = process->HarmonizeThreadIdsForProfileData(response); } if (response_str.empty()) output_strm.PutCString("response: \nerror: UNIMPLEMENTED\n"); else output_strm.Printf("response: %s\n", response.GetStringRef().data()); } } return true; } }; class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw { private: public: CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter) : CommandObjectRaw(interpreter, "process plugin packet monitor", "Send a qRcmd packet through the GDB remote protocol " "and print the response." "The argument passed to this command will be hex " "encoded into a valid 'qRcmd' packet, sent and the " "response will be printed.") {} ~CommandObjectProcessGDBRemotePacketMonitor() override {} bool DoExecute(llvm::StringRef command, CommandReturnObject &result) override { if (command.empty()) { result.AppendErrorWithFormat("'%s' takes a command string argument", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { StreamString packet; packet.PutCString("qRcmd,"); packet.PutBytesAsRawHex8(command.data(), command.size()); bool send_async = true; StringExtractorGDBRemote response; Stream &output_strm = result.GetOutputStream(); process->GetGDBRemote().SendPacketAndReceiveResponseWithOutputSupport( packet.GetString(), response, send_async, [&output_strm](llvm::StringRef output) { output_strm << output; }); result.SetStatus(eReturnStatusSuccessFinishResult); output_strm.Printf(" packet: %s\n", packet.GetData()); const std::string &response_str = std::string(response.GetStringRef()); if (response_str.empty()) output_strm.PutCString("response: \nerror: UNIMPLEMENTED\n"); else output_strm.Printf("response: %s\n", response.GetStringRef().data()); } return true; } }; class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword { private: public: CommandObjectProcessGDBRemotePacket(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "process plugin packet", "Commands that deal with GDB remote packets.", nullptr) { LoadSubCommand( "history", CommandObjectSP( new CommandObjectProcessGDBRemotePacketHistory(interpreter))); LoadSubCommand( "send", CommandObjectSP( new CommandObjectProcessGDBRemotePacketSend(interpreter))); LoadSubCommand( "monitor", CommandObjectSP( new CommandObjectProcessGDBRemotePacketMonitor(interpreter))); LoadSubCommand( "xfer-size", CommandObjectSP( new CommandObjectProcessGDBRemotePacketXferSize(interpreter))); LoadSubCommand("speed-test", CommandObjectSP(new CommandObjectProcessGDBRemoteSpeedTest( interpreter))); } ~CommandObjectProcessGDBRemotePacket() override {} }; class CommandObjectMultiwordProcessGDBRemote : public CommandObjectMultiword { public: CommandObjectMultiwordProcessGDBRemote(CommandInterpreter &interpreter) : CommandObjectMultiword( interpreter, "process plugin", "Commands for operating on a ProcessGDBRemote process.", "process plugin []") { LoadSubCommand( "packet", CommandObjectSP(new CommandObjectProcessGDBRemotePacket(interpreter))); } ~CommandObjectMultiwordProcessGDBRemote() override {} }; CommandObject *ProcessGDBRemote::GetPluginCommandObject() { if (!m_command_sp) m_command_sp = std::make_shared( GetTarget().GetDebugger().GetCommandInterpreter()); return m_command_sp.get(); } diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index 22d86d6cdd75..ba967727ae3b 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -1,460 +1,460 @@ //===-- ProcessGDBRemote.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_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_PROCESSGDBREMOTE_H #define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_PROCESSGDBREMOTE_H #include #include #include #include #include #include "lldb/Core/LoadedModuleInfoList.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/ThreadSafeValue.h" #include "lldb/Host/HostThread.h" #include "lldb/Target/Process.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/Broadcaster.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/GDBRemote.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/StringExtractor.h" #include "lldb/Utility/StringList.h" #include "lldb/Utility/StructuredData.h" #include "lldb/lldb-private-forward.h" #include "GDBRemoteCommunicationClient.h" #include "GDBRemoteCommunicationReplayServer.h" #include "GDBRemoteRegisterContext.h" #include "llvm/ADT/DenseMap.h" namespace lldb_private { namespace repro { class Loader; } namespace process_gdb_remote { class ThreadGDBRemote; class ProcessGDBRemote : public Process, private GDBRemoteClientBase::ContinueDelegate { public: ProcessGDBRemote(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); ~ProcessGDBRemote() override; static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const FileSpec *crash_file_path); static void Initialize(); static void DebuggerInitialize(Debugger &debugger); static void Terminate(); static ConstString GetPluginNameStatic(); static const char *GetPluginDescriptionStatic(); // Check if a given Process bool CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) override; CommandObject *GetPluginCommandObject() override; // Creating a new process, or attaching to an existing one Status WillLaunch(Module *module) override; Status DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) override; void DidLaunch() override; Status WillAttachToProcessWithID(lldb::pid_t pid) override; Status WillAttachToProcessWithName(const char *process_name, bool wait_for_launch) override; - Status DoConnectRemote(Stream *strm, llvm::StringRef remote_url) override; + Status DoConnectRemote(llvm::StringRef remote_url) override; Status WillLaunchOrAttach(); Status DoAttachToProcessWithID(lldb::pid_t pid, const ProcessAttachInfo &attach_info) override; Status DoAttachToProcessWithName(const char *process_name, const ProcessAttachInfo &attach_info) override; void DidAttach(ArchSpec &process_arch) override; // PluginInterface protocol ConstString GetPluginName() override; uint32_t GetPluginVersion() override; // Process Control Status WillResume() override; Status DoResume() override; Status DoHalt(bool &caused_stop) override; Status DoDetach(bool keep_stopped) override; bool DetachRequiresHalt() override { return true; } Status DoSignal(int signal) override; Status DoDestroy() override; void RefreshStateAfterStop() override; void SetUnixSignals(const lldb::UnixSignalsSP &signals_sp); // Process Queries bool IsAlive() override; lldb::addr_t GetImageInfoAddress() override; void WillPublicStop() override; // Process Memory size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Status &error) override; Status WriteObjectFile(std::vector entries) override; size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size, Status &error) override; lldb::addr_t DoAllocateMemory(size_t size, uint32_t permissions, Status &error) override; Status GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo ®ion_info) override; Status DoDeallocateMemory(lldb::addr_t ptr) override; // Process STDIO size_t PutSTDIN(const char *buf, size_t buf_size, Status &error) override; // Process Breakpoints Status EnableBreakpointSite(BreakpointSite *bp_site) override; Status DisableBreakpointSite(BreakpointSite *bp_site) override; // Process Watchpoints Status EnableWatchpoint(Watchpoint *wp, bool notify = true) override; Status DisableWatchpoint(Watchpoint *wp, bool notify = true) override; Status GetWatchpointSupportInfo(uint32_t &num) override; lldb::user_id_t StartTrace(const TraceOptions &options, Status &error) override; Status StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) override; Status GetData(lldb::user_id_t uid, lldb::tid_t thread_id, llvm::MutableArrayRef &buffer, size_t offset = 0) override; Status GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id, llvm::MutableArrayRef &buffer, size_t offset = 0) override; Status GetTraceConfig(lldb::user_id_t uid, TraceOptions &options) override; Status GetWatchpointSupportInfo(uint32_t &num, bool &after) override; bool StartNoticingNewThreads() override; bool StopNoticingNewThreads() override; GDBRemoteCommunicationClient &GetGDBRemote() { return m_gdb_comm; } Status SendEventData(const char *data) override; // Override DidExit so we can disconnect from the remote GDB server void DidExit() override; void SetUserSpecifiedMaxMemoryTransferSize(uint64_t user_specified_max); bool GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, ModuleSpec &module_spec) override; void PrefetchModuleSpecs(llvm::ArrayRef module_file_specs, const llvm::Triple &triple) override; llvm::VersionTuple GetHostOSVersion() override; llvm::VersionTuple GetHostMacCatalystVersion() override; llvm::Error LoadModules() override; llvm::Expected GetLoadedModuleList() override; Status GetFileLoadAddress(const FileSpec &file, bool &is_loaded, lldb::addr_t &load_addr) override; void ModulesDidLoad(ModuleList &module_list) override; StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos(lldb::addr_t image_list_address, lldb::addr_t image_count) override; Status ConfigureStructuredData(ConstString type_name, const StructuredData::ObjectSP &config_sp) override; StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos() override; StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos( const std::vector &load_addresses) override; StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos_sender(StructuredData::ObjectSP args); StructuredData::ObjectSP GetSharedCacheInfo() override; std::string HarmonizeThreadIdsForProfileData( StringExtractorGDBRemote &inputStringExtractor); protected: friend class ThreadGDBRemote; friend class GDBRemoteCommunicationClient; friend class GDBRemoteRegisterContext; /// Broadcaster event bits definitions. enum { eBroadcastBitAsyncContinue = (1 << 0), eBroadcastBitAsyncThreadShouldExit = (1 << 1), eBroadcastBitAsyncThreadDidExit = (1 << 2) }; GDBRemoteCommunicationClient m_gdb_comm; GDBRemoteCommunicationReplayServer m_gdb_replay_server; std::atomic m_debugserver_pid; std::vector m_stop_packet_stack; // The stop packet // stack replaces // the last stop // packet variable std::recursive_mutex m_last_stop_packet_mutex; GDBRemoteDynamicRegisterInfo m_register_info; Broadcaster m_async_broadcaster; lldb::ListenerSP m_async_listener_sp; HostThread m_async_thread; std::recursive_mutex m_async_thread_state_mutex; typedef std::vector tid_collection; typedef std::vector> tid_sig_collection; typedef std::map MMapMap; typedef std::map ExpeditedRegisterMap; tid_collection m_thread_ids; // Thread IDs for all threads. This list gets // updated after stopping std::vector m_thread_pcs; // PC values for all the threads. StructuredData::ObjectSP m_jstopinfo_sp; // Stop info only for any threads // that have valid stop infos StructuredData::ObjectSP m_jthreadsinfo_sp; // Full stop info, expedited // registers and memory for all // threads if "jThreadsInfo" // packet is supported tid_collection m_continue_c_tids; // 'c' for continue tid_sig_collection m_continue_C_tids; // 'C' for continue with signal tid_collection m_continue_s_tids; // 's' for step tid_sig_collection m_continue_S_tids; // 'S' for step with signal uint64_t m_max_memory_size; // The maximum number of bytes to read/write when // reading and writing memory uint64_t m_remote_stub_max_memory_size; // The maximum memory size the remote // gdb stub can handle MMapMap m_addr_to_mmap_size; lldb::BreakpointSP m_thread_create_bp_sp; bool m_waiting_for_attach; bool m_destroy_tried_resuming; lldb::CommandObjectSP m_command_sp; int64_t m_breakpoint_pc_offset; lldb::tid_t m_initial_tid; // The initial thread ID, given by stub on attach bool m_use_g_packet_for_reading; bool m_replay_mode; bool m_allow_flash_writes; using FlashRangeVector = lldb_private::RangeVector; using FlashRange = FlashRangeVector::Entry; FlashRangeVector m_erased_flash_ranges; // Accessors bool IsRunning(lldb::StateType state) { return state == lldb::eStateRunning || IsStepping(state); } bool IsStepping(lldb::StateType state) { return state == lldb::eStateStepping; } bool CanResume(lldb::StateType state) { return state == lldb::eStateStopped; } bool HasExited(lldb::StateType state) { return state == lldb::eStateExited; } bool ProcessIDIsValid() const; void Clear(); bool UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) override; Status ConnectToReplayServer(); Status EstablishConnectionIfNeeded(const ProcessInfo &process_info); Status LaunchAndConnectToDebugserver(const ProcessInfo &process_info); void KillDebugserverProcess(); void BuildDynamicRegisterInfo(bool force); void SetLastStopPacket(const StringExtractorGDBRemote &response); bool ParsePythonTargetDefinition(const FileSpec &target_definition_fspec); DataExtractor GetAuxvData() override; StructuredData::ObjectSP GetExtendedInfoForThread(lldb::tid_t tid); void GetMaxMemorySize(); bool CalculateThreadStopInfo(ThreadGDBRemote *thread); size_t UpdateThreadPCsFromStopReplyThreadsValue(std::string &value); size_t UpdateThreadIDsFromStopReplyThreadsValue(std::string &value); bool HandleNotifyPacket(StringExtractorGDBRemote &packet); bool StartAsyncThread(); void StopAsyncThread(); static lldb::thread_result_t AsyncThread(void *arg); static bool MonitorDebugserverProcess(std::weak_ptr process_wp, lldb::pid_t pid, bool exited, int signo, int exit_status); lldb::StateType SetThreadStopInfo(StringExtractor &stop_packet); bool GetThreadStopInfoFromJSON(ThreadGDBRemote *thread, const StructuredData::ObjectSP &thread_infos_sp); lldb::ThreadSP SetThreadStopInfo(StructuredData::Dictionary *thread_dict); lldb::ThreadSP SetThreadStopInfo(lldb::tid_t tid, ExpeditedRegisterMap &expedited_register_map, uint8_t signo, const std::string &thread_name, const std::string &reason, const std::string &description, uint32_t exc_type, const std::vector &exc_data, lldb::addr_t thread_dispatch_qaddr, bool queue_vars_valid, lldb_private::LazyBool associated_with_libdispatch_queue, lldb::addr_t dispatch_queue_t, std::string &queue_name, lldb::QueueKind queue_kind, uint64_t queue_serial); void HandleStopReplySequence(); void ClearThreadIDList(); bool UpdateThreadIDList(); void DidLaunchOrAttach(ArchSpec &process_arch); void MaybeLoadExecutableModule(); Status ConnectToDebugserver(llvm::StringRef host_port); const char *GetDispatchQueueNameForThread(lldb::addr_t thread_dispatch_qaddr, std::string &dispatch_queue_name); DynamicLoader *GetDynamicLoader() override; bool GetGDBServerRegisterInfoXMLAndProcess(ArchSpec &arch_to_use, std::string xml_filename, uint32_t &cur_reg_num, uint32_t ®_offset); // Query remote GDBServer for register information bool GetGDBServerRegisterInfo(ArchSpec &arch); lldb::ModuleSP LoadModuleAtAddress(const FileSpec &file, lldb::addr_t link_map, lldb::addr_t base_addr, bool value_is_offset); Status UpdateAutomaticSignalFiltering() override; Status FlashErase(lldb::addr_t addr, size_t size); Status FlashDone(); bool HasErased(FlashRange range); private: // For ProcessGDBRemote only std::string m_partial_profile_data; std::map m_thread_id_to_used_usec_map; uint64_t m_last_signals_version = 0; static bool NewThreadNotifyBreakpointHit(void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id); // ContinueDelegate interface void HandleAsyncStdout(llvm::StringRef out) override; void HandleAsyncMisc(llvm::StringRef data) override; void HandleStopReply() override; void HandleAsyncStructuredDataPacket(llvm::StringRef data) override; void SetThreadPc(const lldb::ThreadSP &thread_sp, uint64_t index); using ModuleCacheKey = std::pair; // KeyInfo for the cached module spec DenseMap. // The invariant is that all real keys will have the file and architecture // set. // The empty key has an empty file and an empty arch. // The tombstone key has an invalid arch and an empty file. // The comparison and hash functions take the file name and architecture // triple into account. struct ModuleCacheInfo { static ModuleCacheKey getEmptyKey() { return ModuleCacheKey(); } static ModuleCacheKey getTombstoneKey() { return ModuleCacheKey("", "T"); } static unsigned getHashValue(const ModuleCacheKey &key) { return llvm::hash_combine(key.first, key.second); } static bool isEqual(const ModuleCacheKey &LHS, const ModuleCacheKey &RHS) { return LHS == RHS; } }; llvm::DenseMap m_cached_module_specs; ProcessGDBRemote(const ProcessGDBRemote &) = delete; const ProcessGDBRemote &operator=(const ProcessGDBRemote &) = delete; }; } // namespace process_gdb_remote } // namespace lldb_private #endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_PROCESSGDBREMOTE_H diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp index 8d4bea214814..95c35ea826a0 100644 --- a/lldb/source/Target/Platform.cpp +++ b/lldb/source/Target/Platform.cpp @@ -1,1930 +1,1930 @@ //===-- Platform.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 #include #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "lldb/Breakpoint/BreakpointIDList.h" #include "lldb/Breakpoint/BreakpointLocation.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/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/OptionParser.h" #include "lldb/Interpreter/OptionValueFileSpec.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/Property.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/ModuleCache.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StructuredData.h" #include "llvm/Support/FileSystem.h" // Define these constants from POSIX mman.h rather than include the file so // that they will be correct even when compiled on Linux. #define MAP_PRIVATE 2 #define MAP_ANON 0x1000 using namespace lldb; using namespace lldb_private; static uint32_t g_initialize_count = 0; // Use a singleton function for g_local_platform_sp to avoid init constructors // since LLDB is often part of a shared library static PlatformSP &GetHostPlatformSP() { static PlatformSP g_platform_sp; return g_platform_sp; } const char *Platform::GetHostPlatformName() { return "host"; } namespace { #define LLDB_PROPERTIES_platform #include "TargetProperties.inc" enum { #define LLDB_PROPERTIES_platform #include "TargetPropertiesEnum.inc" }; } // namespace ConstString PlatformProperties::GetSettingName() { static ConstString g_setting_name("platform"); return g_setting_name; } PlatformProperties::PlatformProperties() { m_collection_sp = std::make_shared(GetSettingName()); m_collection_sp->Initialize(g_platform_properties); auto module_cache_dir = GetModuleCacheDirectory(); if (module_cache_dir) return; llvm::SmallString<64> user_home_dir; if (!llvm::sys::path::home_directory(user_home_dir)) return; module_cache_dir = FileSpec(user_home_dir.c_str()); module_cache_dir.AppendPathComponent(".lldb"); module_cache_dir.AppendPathComponent("module_cache"); SetDefaultModuleCacheDirectory(module_cache_dir); SetModuleCacheDirectory(module_cache_dir); } bool PlatformProperties::GetUseModuleCache() const { const auto idx = ePropertyUseModuleCache; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_platform_properties[idx].default_uint_value != 0); } bool PlatformProperties::SetUseModuleCache(bool use_module_cache) { return m_collection_sp->SetPropertyAtIndexAsBoolean( nullptr, ePropertyUseModuleCache, use_module_cache); } FileSpec PlatformProperties::GetModuleCacheDirectory() const { return m_collection_sp->GetPropertyAtIndexAsFileSpec( nullptr, ePropertyModuleCacheDirectory); } bool PlatformProperties::SetModuleCacheDirectory(const FileSpec &dir_spec) { return m_collection_sp->SetPropertyAtIndexAsFileSpec( nullptr, ePropertyModuleCacheDirectory, dir_spec); } void PlatformProperties::SetDefaultModuleCacheDirectory( const FileSpec &dir_spec) { auto f_spec_opt = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpec( nullptr, false, ePropertyModuleCacheDirectory); assert(f_spec_opt); f_spec_opt->SetDefaultValue(dir_spec); } /// Get the native host platform plug-in. /// /// There should only be one of these for each host that LLDB runs /// upon that should be statically compiled in and registered using /// preprocessor macros or other similar build mechanisms. /// /// This platform will be used as the default platform when launching /// or attaching to processes unless another platform is specified. PlatformSP Platform::GetHostPlatform() { return GetHostPlatformSP(); } static std::vector &GetPlatformList() { static std::vector g_platform_list; return g_platform_list; } static std::recursive_mutex &GetPlatformListMutex() { static std::recursive_mutex g_mutex; return g_mutex; } void Platform::Initialize() { g_initialize_count++; } void Platform::Terminate() { if (g_initialize_count > 0) { if (--g_initialize_count == 0) { std::lock_guard guard(GetPlatformListMutex()); GetPlatformList().clear(); } } } const PlatformPropertiesSP &Platform::GetGlobalPlatformProperties() { static const auto g_settings_sp(std::make_shared()); return g_settings_sp; } void Platform::SetHostPlatform(const lldb::PlatformSP &platform_sp) { // The native platform should use its static void Platform::Initialize() // function to register itself as the native platform. GetHostPlatformSP() = platform_sp; if (platform_sp) { std::lock_guard guard(GetPlatformListMutex()); GetPlatformList().push_back(platform_sp); } } Status Platform::GetFileWithUUID(const FileSpec &platform_file, const UUID *uuid_ptr, FileSpec &local_file) { // Default to the local case local_file = platform_file; return Status(); } FileSpecList Platform::LocateExecutableScriptingResources(Target *target, Module &module, Stream *feedback_stream) { return FileSpecList(); } // PlatformSP // Platform::FindPlugin (Process *process, ConstString plugin_name) //{ // PlatformCreateInstance create_callback = nullptr; // if (plugin_name) // { // create_callback = // PluginManager::GetPlatformCreateCallbackForPluginName (plugin_name); // if (create_callback) // { // ArchSpec arch; // if (process) // { // arch = process->GetTarget().GetArchitecture(); // } // PlatformSP platform_sp(create_callback(process, &arch)); // if (platform_sp) // return platform_sp; // } // } // else // { // for (uint32_t idx = 0; (create_callback = // PluginManager::GetPlatformCreateCallbackAtIndex(idx)) != nullptr; // ++idx) // { // PlatformSP platform_sp(create_callback(process, nullptr)); // if (platform_sp) // return platform_sp; // } // } // return PlatformSP(); //} Status Platform::GetSharedModule(const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp, const FileSpecList *module_search_paths_ptr, ModuleSP *old_module_sp_ptr, bool *did_create_ptr) { if (IsHost()) return ModuleList::GetSharedModule( module_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr, false); // Module resolver lambda. auto resolver = [&](const ModuleSpec &spec) { Status error(eErrorTypeGeneric); ModuleSpec resolved_spec; // Check if we have sysroot set. if (m_sdk_sysroot) { // Prepend sysroot to module spec. resolved_spec = spec; resolved_spec.GetFileSpec().PrependPathComponent( m_sdk_sysroot.GetStringRef()); // Try to get shared module with resolved spec. error = ModuleList::GetSharedModule( resolved_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr, false); } // If we don't have sysroot or it didn't work then // try original module spec. if (!error.Success()) { resolved_spec = spec; error = ModuleList::GetSharedModule( resolved_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr, false); } if (error.Success() && module_sp) module_sp->SetPlatformFileSpec(resolved_spec.GetFileSpec()); return error; }; return GetRemoteSharedModule(module_spec, process, module_sp, resolver, did_create_ptr); } bool Platform::GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, ModuleSpec &module_spec) { ModuleSpecList module_specs; if (ObjectFile::GetModuleSpecifications(module_file_spec, 0, 0, module_specs) == 0) return false; ModuleSpec matched_module_spec; return module_specs.FindMatchingModuleSpec(ModuleSpec(module_file_spec, arch), module_spec); } PlatformSP Platform::Find(ConstString name) { if (name) { static ConstString g_host_platform_name("host"); if (name == g_host_platform_name) return GetHostPlatform(); std::lock_guard guard(GetPlatformListMutex()); for (const auto &platform_sp : GetPlatformList()) { if (platform_sp->GetName() == name) return platform_sp; } } return PlatformSP(); } PlatformSP Platform::Create(ConstString name, Status &error) { PlatformCreateInstance create_callback = nullptr; lldb::PlatformSP platform_sp; if (name) { static ConstString g_host_platform_name("host"); if (name == g_host_platform_name) return GetHostPlatform(); create_callback = PluginManager::GetPlatformCreateCallbackForPluginName(name); if (create_callback) platform_sp = create_callback(true, nullptr); else error.SetErrorStringWithFormat( "unable to find a plug-in for the platform named \"%s\"", name.GetCString()); } else error.SetErrorString("invalid platform name"); if (platform_sp) { std::lock_guard guard(GetPlatformListMutex()); GetPlatformList().push_back(platform_sp); } return platform_sp; } PlatformSP Platform::Create(const ArchSpec &arch, ArchSpec *platform_arch_ptr, Status &error) { lldb::PlatformSP platform_sp; if (arch.IsValid()) { // Scope for locker { // First try exact arch matches across all platforms already created std::lock_guard guard(GetPlatformListMutex()); for (const auto &platform_sp : GetPlatformList()) { if (platform_sp->IsCompatibleArchitecture(arch, true, platform_arch_ptr)) return platform_sp; } // Next try compatible arch matches across all platforms already created for (const auto &platform_sp : GetPlatformList()) { if (platform_sp->IsCompatibleArchitecture(arch, false, platform_arch_ptr)) return platform_sp; } } PlatformCreateInstance create_callback; // First try exact arch matches across all platform plug-ins uint32_t idx; for (idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex(idx)); ++idx) { if (create_callback) { platform_sp = create_callback(false, &arch); if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, true, platform_arch_ptr)) { std::lock_guard guard(GetPlatformListMutex()); GetPlatformList().push_back(platform_sp); return platform_sp; } } } // Next try compatible arch matches across all platform plug-ins for (idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex(idx)); ++idx) { if (create_callback) { platform_sp = create_callback(false, &arch); if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, false, platform_arch_ptr)) { std::lock_guard guard(GetPlatformListMutex()); GetPlatformList().push_back(platform_sp); return platform_sp; } } } } else error.SetErrorString("invalid platform name"); if (platform_arch_ptr) platform_arch_ptr->Clear(); platform_sp.reset(); return platform_sp; } ArchSpec Platform::GetAugmentedArchSpec(Platform *platform, llvm::StringRef triple) { if (platform) return platform->GetAugmentedArchSpec(triple); return HostInfo::GetAugmentedArchSpec(triple); } /// Default Constructor Platform::Platform(bool is_host) : m_is_host(is_host), m_os_version_set_while_connected(false), m_system_arch_set_while_connected(false), m_sdk_sysroot(), m_sdk_build(), m_working_dir(), m_remote_url(), m_name(), m_system_arch(), m_mutex(), m_max_uid_name_len(0), m_max_gid_name_len(0), m_supports_rsync(false), m_rsync_opts(), m_rsync_prefix(), m_supports_ssh(false), m_ssh_opts(), m_ignores_remote_hostname(false), m_trap_handlers(), m_calculated_trap_handlers(false), m_module_cache(std::make_unique()) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); LLDB_LOGF(log, "%p Platform::Platform()", static_cast(this)); } /// Destructor. /// /// The destructor is virtual since this class is designed to be /// inherited from by the plug-in instance. Platform::~Platform() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); LLDB_LOGF(log, "%p Platform::~Platform()", static_cast(this)); } void Platform::GetStatus(Stream &strm) { std::string s; strm.Printf(" Platform: %s\n", GetPluginName().GetCString()); ArchSpec arch(GetSystemArchitecture()); if (arch.IsValid()) { if (!arch.GetTriple().str().empty()) { strm.Printf(" Triple: "); arch.DumpTriple(strm.AsRawOstream()); strm.EOL(); } } llvm::VersionTuple os_version = GetOSVersion(); if (!os_version.empty()) { strm.Format("OS Version: {0}", os_version.getAsString()); if (GetOSBuildString(s)) strm.Printf(" (%s)", s.c_str()); strm.EOL(); } if (IsHost()) { strm.Printf(" Hostname: %s\n", GetHostname()); } else { const bool is_connected = IsConnected(); if (is_connected) strm.Printf(" Hostname: %s\n", GetHostname()); strm.Printf(" Connected: %s\n", is_connected ? "yes" : "no"); } if (GetWorkingDirectory()) { strm.Printf("WorkingDir: %s\n", GetWorkingDirectory().GetCString()); } if (!IsConnected()) return; std::string specific_info(GetPlatformSpecificConnectionInformation()); if (!specific_info.empty()) strm.Printf("Platform-specific connection: %s\n", specific_info.c_str()); if (GetOSKernelDescription(s)) strm.Printf(" Kernel: %s\n", s.c_str()); } llvm::VersionTuple Platform::GetOSVersion(Process *process) { std::lock_guard guard(m_mutex); if (IsHost()) { if (m_os_version.empty()) { // We have a local host platform m_os_version = HostInfo::GetOSVersion(); m_os_version_set_while_connected = !m_os_version.empty(); } } else { // We have a remote platform. We can only fetch the remote // OS version if we are connected, and we don't want to do it // more than once. const bool is_connected = IsConnected(); bool fetch = false; if (!m_os_version.empty()) { // We have valid OS version info, check to make sure it wasn't manually // set prior to connecting. If it was manually set prior to connecting, // then lets fetch the actual OS version info if we are now connected. if (is_connected && !m_os_version_set_while_connected) fetch = true; } else { // We don't have valid OS version info, fetch it if we are connected fetch = is_connected; } if (fetch) m_os_version_set_while_connected = GetRemoteOSVersion(); } if (!m_os_version.empty()) return m_os_version; if (process) { // Check with the process in case it can answer the question if a process // was provided return process->GetHostOSVersion(); } return llvm::VersionTuple(); } bool Platform::GetOSBuildString(std::string &s) { s.clear(); if (IsHost()) #if !defined(__linux__) return HostInfo::GetOSBuildString(s); #else return false; #endif else return GetRemoteOSBuildString(s); } bool Platform::GetOSKernelDescription(std::string &s) { if (IsHost()) #if !defined(__linux__) return HostInfo::GetOSKernelDescription(s); #else return false; #endif else return GetRemoteOSKernelDescription(s); } void Platform::AddClangModuleCompilationOptions( Target *target, std::vector &options) { std::vector default_compilation_options = { "-x", "c++", "-Xclang", "-nostdsysteminc", "-Xclang", "-nostdsysteminc"}; options.insert(options.end(), default_compilation_options.begin(), default_compilation_options.end()); } FileSpec Platform::GetWorkingDirectory() { if (IsHost()) { llvm::SmallString<64> cwd; if (llvm::sys::fs::current_path(cwd)) return {}; else { FileSpec file_spec(cwd); FileSystem::Instance().Resolve(file_spec); return file_spec; } } else { if (!m_working_dir) m_working_dir = GetRemoteWorkingDirectory(); return m_working_dir; } } struct RecurseCopyBaton { const FileSpec &dst; Platform *platform_ptr; Status error; }; static FileSystem::EnumerateDirectoryResult RecurseCopy_Callback(void *baton, llvm::sys::fs::file_type ft, llvm::StringRef path) { RecurseCopyBaton *rc_baton = (RecurseCopyBaton *)baton; FileSpec src(path); namespace fs = llvm::sys::fs; switch (ft) { case fs::file_type::fifo_file: case fs::file_type::socket_file: // we have no way to copy pipes and sockets - ignore them and continue return FileSystem::eEnumerateDirectoryResultNext; break; case fs::file_type::directory_file: { // make the new directory and get in there FileSpec dst_dir = rc_baton->dst; if (!dst_dir.GetFilename()) dst_dir.GetFilename() = src.GetLastPathComponent(); Status error = rc_baton->platform_ptr->MakeDirectory( dst_dir, lldb::eFilePermissionsDirectoryDefault); if (error.Fail()) { rc_baton->error.SetErrorStringWithFormat( "unable to setup directory %s on remote end", dst_dir.GetCString()); return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out } // now recurse std::string src_dir_path(src.GetPath()); // Make a filespec that only fills in the directory of a FileSpec so when // we enumerate we can quickly fill in the filename for dst copies FileSpec recurse_dst; recurse_dst.GetDirectory().SetCString(dst_dir.GetPath().c_str()); RecurseCopyBaton rc_baton2 = {recurse_dst, rc_baton->platform_ptr, Status()}; FileSystem::Instance().EnumerateDirectory(src_dir_path, true, true, true, RecurseCopy_Callback, &rc_baton2); if (rc_baton2.error.Fail()) { rc_baton->error.SetErrorString(rc_baton2.error.AsCString()); return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out } return FileSystem::eEnumerateDirectoryResultNext; } break; case fs::file_type::symlink_file: { // copy the file and keep going FileSpec dst_file = rc_baton->dst; if (!dst_file.GetFilename()) dst_file.GetFilename() = src.GetFilename(); FileSpec src_resolved; rc_baton->error = FileSystem::Instance().Readlink(src, src_resolved); if (rc_baton->error.Fail()) return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out rc_baton->error = rc_baton->platform_ptr->CreateSymlink(dst_file, src_resolved); if (rc_baton->error.Fail()) return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out return FileSystem::eEnumerateDirectoryResultNext; } break; case fs::file_type::regular_file: { // copy the file and keep going FileSpec dst_file = rc_baton->dst; if (!dst_file.GetFilename()) dst_file.GetFilename() = src.GetFilename(); Status err = rc_baton->platform_ptr->PutFile(src, dst_file); if (err.Fail()) { rc_baton->error.SetErrorString(err.AsCString()); return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out } return FileSystem::eEnumerateDirectoryResultNext; } break; default: rc_baton->error.SetErrorStringWithFormat( "invalid file detected during copy: %s", src.GetPath().c_str()); return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out break; } llvm_unreachable("Unhandled file_type!"); } Status Platform::Install(const FileSpec &src, const FileSpec &dst) { Status error; Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); LLDB_LOGF(log, "Platform::Install (src='%s', dst='%s')", src.GetPath().c_str(), dst.GetPath().c_str()); FileSpec fixed_dst(dst); if (!fixed_dst.GetFilename()) fixed_dst.GetFilename() = src.GetFilename(); FileSpec working_dir = GetWorkingDirectory(); if (dst) { if (dst.GetDirectory()) { const char first_dst_dir_char = dst.GetDirectory().GetCString()[0]; if (first_dst_dir_char == '/' || first_dst_dir_char == '\\') { fixed_dst.GetDirectory() = dst.GetDirectory(); } // If the fixed destination file doesn't have a directory yet, then we // must have a relative path. We will resolve this relative path against // the platform's working directory if (!fixed_dst.GetDirectory()) { FileSpec relative_spec; std::string path; if (working_dir) { relative_spec = working_dir; relative_spec.AppendPathComponent(dst.GetPath()); fixed_dst.GetDirectory() = relative_spec.GetDirectory(); } else { error.SetErrorStringWithFormat( "platform working directory must be valid for relative path '%s'", dst.GetPath().c_str()); return error; } } } else { if (working_dir) { fixed_dst.GetDirectory().SetCString(working_dir.GetCString()); } else { error.SetErrorStringWithFormat( "platform working directory must be valid for relative path '%s'", dst.GetPath().c_str()); return error; } } } else { if (working_dir) { fixed_dst.GetDirectory().SetCString(working_dir.GetCString()); } else { error.SetErrorStringWithFormat("platform working directory must be valid " "when destination directory is empty"); return error; } } LLDB_LOGF(log, "Platform::Install (src='%s', dst='%s') fixed_dst='%s'", src.GetPath().c_str(), dst.GetPath().c_str(), fixed_dst.GetPath().c_str()); if (GetSupportsRSync()) { error = PutFile(src, dst); } else { namespace fs = llvm::sys::fs; switch (fs::get_file_type(src.GetPath(), false)) { case fs::file_type::directory_file: { llvm::sys::fs::remove(fixed_dst.GetPath()); uint32_t permissions = FileSystem::Instance().GetPermissions(src); if (permissions == 0) permissions = eFilePermissionsDirectoryDefault; error = MakeDirectory(fixed_dst, permissions); if (error.Success()) { // Make a filespec that only fills in the directory of a FileSpec so // when we enumerate we can quickly fill in the filename for dst copies FileSpec recurse_dst; recurse_dst.GetDirectory().SetCString(fixed_dst.GetCString()); std::string src_dir_path(src.GetPath()); RecurseCopyBaton baton = {recurse_dst, this, Status()}; FileSystem::Instance().EnumerateDirectory( src_dir_path, true, true, true, RecurseCopy_Callback, &baton); return baton.error; } } break; case fs::file_type::regular_file: llvm::sys::fs::remove(fixed_dst.GetPath()); error = PutFile(src, fixed_dst); break; case fs::file_type::symlink_file: { llvm::sys::fs::remove(fixed_dst.GetPath()); FileSpec src_resolved; error = FileSystem::Instance().Readlink(src, src_resolved); if (error.Success()) error = CreateSymlink(dst, src_resolved); } break; case fs::file_type::fifo_file: error.SetErrorString("platform install doesn't handle pipes"); break; case fs::file_type::socket_file: error.SetErrorString("platform install doesn't handle sockets"); break; default: error.SetErrorString( "platform install doesn't handle non file or directory items"); break; } } return error; } bool Platform::SetWorkingDirectory(const FileSpec &file_spec) { if (IsHost()) { Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); LLDB_LOG(log, "{0}", file_spec); if (std::error_code ec = llvm::sys::fs::set_current_path(file_spec.GetPath())) { LLDB_LOG(log, "error: {0}", ec.message()); return false; } return true; } else { m_working_dir.Clear(); return SetRemoteWorkingDirectory(file_spec); } } Status Platform::MakeDirectory(const FileSpec &file_spec, uint32_t permissions) { if (IsHost()) return llvm::sys::fs::create_directory(file_spec.GetPath(), permissions); else { Status error; error.SetErrorStringWithFormat("remote platform %s doesn't support %s", GetPluginName().GetCString(), LLVM_PRETTY_FUNCTION); return error; } } Status Platform::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions) { if (IsHost()) { auto Value = llvm::sys::fs::getPermissions(file_spec.GetPath()); if (Value) file_permissions = Value.get(); return Status(Value.getError()); } else { Status error; error.SetErrorStringWithFormat("remote platform %s doesn't support %s", GetPluginName().GetCString(), LLVM_PRETTY_FUNCTION); return error; } } Status Platform::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions) { if (IsHost()) { auto Perms = static_cast(file_permissions); return llvm::sys::fs::setPermissions(file_spec.GetPath(), Perms); } else { Status error; error.SetErrorStringWithFormat("remote platform %s doesn't support %s", GetPluginName().GetCString(), LLVM_PRETTY_FUNCTION); return error; } } ConstString Platform::GetName() { return GetPluginName(); } const char *Platform::GetHostname() { if (IsHost()) return "127.0.0.1"; if (m_name.empty()) return nullptr; return m_name.c_str(); } ConstString Platform::GetFullNameForDylib(ConstString basename) { return basename; } bool Platform::SetRemoteWorkingDirectory(const FileSpec &working_dir) { Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); LLDB_LOGF(log, "Platform::SetRemoteWorkingDirectory('%s')", working_dir.GetCString()); m_working_dir = working_dir; return true; } bool Platform::SetOSVersion(llvm::VersionTuple version) { if (IsHost()) { // We don't need anyone setting the OS version for the host platform, we // should be able to figure it out by calling HostInfo::GetOSVersion(...). return false; } else { // We have a remote platform, allow setting the target OS version if we // aren't connected, since if we are connected, we should be able to // request the remote OS version from the connected platform. if (IsConnected()) return false; else { // We aren't connected and we might want to set the OS version ahead of // time before we connect so we can peruse files and use a local SDK or // PDK cache of support files to disassemble or do other things. m_os_version = version; return true; } } return false; } Status Platform::ResolveExecutable(const ModuleSpec &module_spec, lldb::ModuleSP &exe_module_sp, const FileSpecList *module_search_paths_ptr) { Status error; if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) { if (module_spec.GetArchitecture().IsValid()) { error = ModuleList::GetSharedModule(module_spec, exe_module_sp, module_search_paths_ptr, nullptr, nullptr); } else { // No valid architecture was specified, ask the platform for the // architectures that we should be using (in the correct order) and see // if we can find a match that way ModuleSpec arch_module_spec(module_spec); for (uint32_t idx = 0; GetSupportedArchitectureAtIndex( idx, arch_module_spec.GetArchitecture()); ++idx) { error = ModuleList::GetSharedModule(arch_module_spec, exe_module_sp, module_search_paths_ptr, nullptr, nullptr); // Did we find an executable using one of the if (error.Success() && exe_module_sp) break; } } } else { error.SetErrorStringWithFormat("'%s' does not exist", module_spec.GetFileSpec().GetPath().c_str()); } return error; } Status Platform::ResolveSymbolFile(Target &target, const ModuleSpec &sym_spec, FileSpec &sym_file) { Status error; if (FileSystem::Instance().Exists(sym_spec.GetSymbolFileSpec())) sym_file = sym_spec.GetSymbolFileSpec(); else error.SetErrorString("unable to resolve symbol file"); return error; } bool Platform::ResolveRemotePath(const FileSpec &platform_path, FileSpec &resolved_platform_path) { resolved_platform_path = platform_path; FileSystem::Instance().Resolve(resolved_platform_path); return true; } const ArchSpec &Platform::GetSystemArchitecture() { if (IsHost()) { if (!m_system_arch.IsValid()) { // We have a local host platform m_system_arch = HostInfo::GetArchitecture(); m_system_arch_set_while_connected = m_system_arch.IsValid(); } } else { // We have a remote platform. We can only fetch the remote system // architecture if we are connected, and we don't want to do it more than // once. const bool is_connected = IsConnected(); bool fetch = false; if (m_system_arch.IsValid()) { // We have valid OS version info, check to make sure it wasn't manually // set prior to connecting. If it was manually set prior to connecting, // then lets fetch the actual OS version info if we are now connected. if (is_connected && !m_system_arch_set_while_connected) fetch = true; } else { // We don't have valid OS version info, fetch it if we are connected fetch = is_connected; } if (fetch) { m_system_arch = GetRemoteSystemArchitecture(); m_system_arch_set_while_connected = m_system_arch.IsValid(); } } return m_system_arch; } ArchSpec Platform::GetAugmentedArchSpec(llvm::StringRef triple) { if (triple.empty()) return ArchSpec(); llvm::Triple normalized_triple(llvm::Triple::normalize(triple)); if (!ArchSpec::ContainsOnlyArch(normalized_triple)) return ArchSpec(triple); if (auto kind = HostInfo::ParseArchitectureKind(triple)) return HostInfo::GetArchitecture(*kind); ArchSpec compatible_arch; ArchSpec raw_arch(triple); if (!IsCompatibleArchitecture(raw_arch, false, &compatible_arch)) return raw_arch; if (!compatible_arch.IsValid()) return ArchSpec(normalized_triple); const llvm::Triple &compatible_triple = compatible_arch.GetTriple(); if (normalized_triple.getVendorName().empty()) normalized_triple.setVendor(compatible_triple.getVendor()); if (normalized_triple.getOSName().empty()) normalized_triple.setOS(compatible_triple.getOS()); if (normalized_triple.getEnvironmentName().empty()) normalized_triple.setEnvironment(compatible_triple.getEnvironment()); return ArchSpec(normalized_triple); } Status Platform::ConnectRemote(Args &args) { Status error; if (IsHost()) error.SetErrorStringWithFormat("The currently selected platform (%s) is " "the host platform and is always connected.", GetPluginName().GetCString()); else error.SetErrorStringWithFormat( "Platform::ConnectRemote() is not supported by %s", GetPluginName().GetCString()); return error; } Status Platform::DisconnectRemote() { Status error; if (IsHost()) error.SetErrorStringWithFormat("The currently selected platform (%s) is " "the host platform and is always connected.", GetPluginName().GetCString()); else error.SetErrorStringWithFormat( "Platform::DisconnectRemote() is not supported by %s", GetPluginName().GetCString()); return error; } bool Platform::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) { // Take care of the host case so that each subclass can just call this // function to get the host functionality. if (IsHost()) return Host::GetProcessInfo(pid, process_info); return false; } uint32_t Platform::FindProcesses(const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) { // Take care of the host case so that each subclass can just call this // function to get the host functionality. uint32_t match_count = 0; if (IsHost()) match_count = Host::FindProcesses(match_info, process_infos); return match_count; } Status Platform::LaunchProcess(ProcessLaunchInfo &launch_info) { Status error; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); LLDB_LOGF(log, "Platform::%s()", __FUNCTION__); // Take care of the host case so that each subclass can just call this // function to get the host functionality. if (IsHost()) { if (::getenv("LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY")) launch_info.GetFlags().Set(eLaunchFlagLaunchInTTY); if (launch_info.GetFlags().Test(eLaunchFlagLaunchInShell)) { const bool is_localhost = true; const bool will_debug = launch_info.GetFlags().Test(eLaunchFlagDebug); const bool first_arg_is_full_shell_command = false; uint32_t num_resumes = GetResumeCountForLaunchInfo(launch_info); if (log) { const FileSpec &shell = launch_info.GetShell(); std::string shell_str = (shell) ? shell.GetPath() : ""; LLDB_LOGF(log, "Platform::%s GetResumeCountForLaunchInfo() returned %" PRIu32 ", shell is '%s'", __FUNCTION__, num_resumes, shell_str.c_str()); } if (!launch_info.ConvertArgumentsForLaunchingInShell( error, is_localhost, will_debug, first_arg_is_full_shell_command, num_resumes)) return error; } else if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) { error = ShellExpandArguments(launch_info); if (error.Fail()) { error.SetErrorStringWithFormat("shell expansion failed (reason: %s). " "consider launching with 'process " "launch'.", error.AsCString("unknown")); return error; } } LLDB_LOGF(log, "Platform::%s final launch_info resume count: %" PRIu32, __FUNCTION__, launch_info.GetResumeCount()); error = Host::LaunchProcess(launch_info); } else error.SetErrorString( "base lldb_private::Platform class can't launch remote processes"); return error; } Status Platform::ShellExpandArguments(ProcessLaunchInfo &launch_info) { if (IsHost()) return Host::ShellExpandArguments(launch_info); return Status("base lldb_private::Platform class can't expand arguments"); } Status Platform::KillProcess(const lldb::pid_t pid) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); LLDB_LOGF(log, "Platform::%s, pid %" PRIu64, __FUNCTION__, pid); // Try to find a process plugin to handle this Kill request. If we can't, // fall back to the default OS implementation. size_t num_debuggers = Debugger::GetNumDebuggers(); for (size_t didx = 0; didx < num_debuggers; ++didx) { DebuggerSP debugger = Debugger::GetDebuggerAtIndex(didx); lldb_private::TargetList &targets = debugger->GetTargetList(); for (int tidx = 0; tidx < targets.GetNumTargets(); ++tidx) { ProcessSP process = targets.GetTargetAtIndex(tidx)->GetProcessSP(); if (process->GetID() == pid) return process->Destroy(true); } } if (!IsHost()) { return Status( "base lldb_private::Platform class can't kill remote processes unless " "they are controlled by a process plugin"); } Host::Kill(pid, SIGTERM); return Status(); } lldb::ProcessSP Platform::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, // Can be nullptr, if nullptr create a // new target, else use existing one Status &error) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); LLDB_LOGF(log, "Platform::%s entered (target %p)", __FUNCTION__, static_cast(target)); ProcessSP process_sp; // Make sure we stop at the entry point launch_info.GetFlags().Set(eLaunchFlagDebug); // We always launch the process we are going to debug in a separate process // group, since then we can handle ^C interrupts ourselves w/o having to // worry about the target getting them as well. launch_info.SetLaunchInSeparateProcessGroup(true); // Allow any StructuredData process-bound plugins to adjust the launch info // if needed size_t i = 0; bool iteration_complete = false; // Note iteration can't simply go until a nullptr callback is returned, as it // is valid for a plugin to not supply a filter. auto get_filter_func = PluginManager::GetStructuredDataFilterCallbackAtIndex; for (auto filter_callback = get_filter_func(i, iteration_complete); !iteration_complete; filter_callback = get_filter_func(++i, iteration_complete)) { if (filter_callback) { // Give this ProcessLaunchInfo filter a chance to adjust the launch info. error = (*filter_callback)(launch_info, target); if (!error.Success()) { LLDB_LOGF(log, "Platform::%s() StructuredDataPlugin launch " "filter failed.", __FUNCTION__); return process_sp; } } } error = LaunchProcess(launch_info); if (error.Success()) { LLDB_LOGF(log, "Platform::%s LaunchProcess() call succeeded (pid=%" PRIu64 ")", __FUNCTION__, launch_info.GetProcessID()); if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) { ProcessAttachInfo attach_info(launch_info); process_sp = Attach(attach_info, debugger, target, error); if (process_sp) { LLDB_LOGF(log, "Platform::%s Attach() succeeded, Process plugin: %s", __FUNCTION__, process_sp->GetPluginName().AsCString()); launch_info.SetHijackListener(attach_info.GetHijackListener()); // Since we attached to the process, it will think it needs to detach // if the process object just goes away without an explicit call to // Process::Kill() or Process::Detach(), so let it know to kill the // process if this happens. process_sp->SetShouldDetach(false); // If we didn't have any file actions, the pseudo terminal might have // been used where the secondary side was given as the file to open for // stdin/out/err after we have already opened the master so we can // read/write stdin/out/err. int pty_fd = launch_info.GetPTY().ReleasePrimaryFileDescriptor(); if (pty_fd != PseudoTerminal::invalid_fd) { process_sp->SetSTDIOFileDescriptor(pty_fd); } } else { LLDB_LOGF(log, "Platform::%s Attach() failed: %s", __FUNCTION__, error.AsCString()); } } else { LLDB_LOGF(log, "Platform::%s LaunchProcess() returned launch_info with " "invalid process id", __FUNCTION__); } } else { LLDB_LOGF(log, "Platform::%s LaunchProcess() failed: %s", __FUNCTION__, error.AsCString()); } return process_sp; } lldb::PlatformSP Platform::GetPlatformForArchitecture(const ArchSpec &arch, ArchSpec *platform_arch_ptr) { lldb::PlatformSP platform_sp; Status error; if (arch.IsValid()) platform_sp = Platform::Create(arch, platform_arch_ptr, error); return platform_sp; } /// Lets a platform answer if it is compatible with a given /// architecture and the target triple contained within. bool Platform::IsCompatibleArchitecture(const ArchSpec &arch, bool exact_arch_match, ArchSpec *compatible_arch_ptr) { // If the architecture is invalid, we must answer true... if (arch.IsValid()) { ArchSpec platform_arch; // Try for an exact architecture match first. if (exact_arch_match) { for (uint32_t arch_idx = 0; GetSupportedArchitectureAtIndex(arch_idx, platform_arch); ++arch_idx) { if (arch.IsExactMatch(platform_arch)) { if (compatible_arch_ptr) *compatible_arch_ptr = platform_arch; return true; } } } else { for (uint32_t arch_idx = 0; GetSupportedArchitectureAtIndex(arch_idx, platform_arch); ++arch_idx) { if (arch.IsCompatibleMatch(platform_arch)) { if (compatible_arch_ptr) *compatible_arch_ptr = platform_arch; return true; } } } } if (compatible_arch_ptr) compatible_arch_ptr->Clear(); return false; } Status Platform::PutFile(const FileSpec &source, const FileSpec &destination, uint32_t uid, uint32_t gid) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); LLDB_LOGF(log, "[PutFile] Using block by block transfer....\n"); auto source_open_options = File::eOpenOptionRead | File::eOpenOptionCloseOnExec; namespace fs = llvm::sys::fs; if (fs::is_symlink_file(source.GetPath())) source_open_options |= File::eOpenOptionDontFollowSymlinks; auto source_file = FileSystem::Instance().Open(source, source_open_options, lldb::eFilePermissionsUserRW); if (!source_file) return Status(source_file.takeError()); Status error; uint32_t permissions = source_file.get()->GetPermissions(error); if (permissions == 0) permissions = lldb::eFilePermissionsFileDefault; lldb::user_id_t dest_file = OpenFile( destination, File::eOpenOptionCanCreate | File::eOpenOptionWrite | File::eOpenOptionTruncate | File::eOpenOptionCloseOnExec, permissions, error); LLDB_LOGF(log, "dest_file = %" PRIu64 "\n", dest_file); if (error.Fail()) return error; if (dest_file == UINT64_MAX) return Status("unable to open target file"); lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024 * 16, 0)); uint64_t offset = 0; for (;;) { size_t bytes_read = buffer_sp->GetByteSize(); error = source_file.get()->Read(buffer_sp->GetBytes(), bytes_read); if (error.Fail() || bytes_read == 0) break; const uint64_t bytes_written = WriteFile(dest_file, offset, buffer_sp->GetBytes(), bytes_read, error); if (error.Fail()) break; offset += bytes_written; if (bytes_written != bytes_read) { // We didn't write the correct number of bytes, so adjust the file // position in the source file we are reading from... source_file.get()->SeekFromStart(offset); } } CloseFile(dest_file, error); if (uid == UINT32_MAX && gid == UINT32_MAX) return error; // TODO: ChownFile? return error; } Status Platform::GetFile(const FileSpec &source, const FileSpec &destination) { Status error("unimplemented"); return error; } Status Platform::CreateSymlink(const FileSpec &src, // The name of the link is in src const FileSpec &dst) // The symlink points to dst { Status error("unimplemented"); return error; } bool Platform::GetFileExists(const lldb_private::FileSpec &file_spec) { return false; } Status Platform::Unlink(const FileSpec &path) { Status error("unimplemented"); return error; } MmapArgList Platform::GetMmapArgumentList(const ArchSpec &arch, addr_t addr, addr_t length, unsigned prot, unsigned flags, addr_t fd, addr_t offset) { uint64_t flags_platform = 0; if (flags & eMmapFlagsPrivate) flags_platform |= MAP_PRIVATE; if (flags & eMmapFlagsAnon) flags_platform |= MAP_ANON; MmapArgList args({addr, length, prot, flags_platform, fd, offset}); return args; } lldb_private::Status Platform::RunShellCommand( const char *command, // Shouldn't be nullptr const FileSpec & working_dir, // Pass empty FileSpec to use the current working directory int *status_ptr, // Pass nullptr if you don't want the process exit status int *signo_ptr, // Pass nullptr if you don't want the signal that caused the // process to exit std::string *command_output, // Pass nullptr if you don't want the command output const Timeout &timeout) { if (IsHost()) return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout); else return Status("unimplemented"); } bool Platform::CalculateMD5(const FileSpec &file_spec, uint64_t &low, uint64_t &high) { if (!IsHost()) return false; auto Result = llvm::sys::fs::md5_contents(file_spec.GetPath()); if (!Result) return false; std::tie(high, low) = Result->words(); return true; } void Platform::SetLocalCacheDirectory(const char *local) { m_local_cache_directory.assign(local); } const char *Platform::GetLocalCacheDirectory() { return m_local_cache_directory.c_str(); } static constexpr OptionDefinition g_rsync_option_table[] = { {LLDB_OPT_SET_ALL, false, "rsync", 'r', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Enable rsync."}, {LLDB_OPT_SET_ALL, false, "rsync-opts", 'R', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCommandName, "Platform-specific options required for rsync to work."}, {LLDB_OPT_SET_ALL, false, "rsync-prefix", 'P', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCommandName, "Platform-specific rsync prefix put before the remote path."}, {LLDB_OPT_SET_ALL, false, "ignore-remote-hostname", 'i', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Do not automatically fill in the remote hostname when composing the " "rsync command."}, }; static constexpr OptionDefinition g_ssh_option_table[] = { {LLDB_OPT_SET_ALL, false, "ssh", 's', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Enable SSH."}, {LLDB_OPT_SET_ALL, false, "ssh-opts", 'S', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCommandName, "Platform-specific options required for SSH to work."}, }; static constexpr OptionDefinition g_caching_option_table[] = { {LLDB_OPT_SET_ALL, false, "local-cache-dir", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePath, "Path in which to store local copies of files."}, }; llvm::ArrayRef OptionGroupPlatformRSync::GetDefinitions() { return llvm::makeArrayRef(g_rsync_option_table); } void OptionGroupPlatformRSync::OptionParsingStarting( ExecutionContext *execution_context) { m_rsync = false; m_rsync_opts.clear(); m_rsync_prefix.clear(); m_ignores_remote_hostname = false; } lldb_private::Status OptionGroupPlatformRSync::SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) { Status error; char short_option = (char)GetDefinitions()[option_idx].short_option; switch (short_option) { case 'r': m_rsync = true; break; case 'R': m_rsync_opts.assign(std::string(option_arg)); break; case 'P': m_rsync_prefix.assign(std::string(option_arg)); break; case 'i': m_ignores_remote_hostname = true; break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } lldb::BreakpointSP Platform::SetThreadCreationBreakpoint(lldb_private::Target &target) { return lldb::BreakpointSP(); } llvm::ArrayRef OptionGroupPlatformSSH::GetDefinitions() { return llvm::makeArrayRef(g_ssh_option_table); } void OptionGroupPlatformSSH::OptionParsingStarting( ExecutionContext *execution_context) { m_ssh = false; m_ssh_opts.clear(); } lldb_private::Status OptionGroupPlatformSSH::SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) { Status error; char short_option = (char)GetDefinitions()[option_idx].short_option; switch (short_option) { case 's': m_ssh = true; break; case 'S': m_ssh_opts.assign(std::string(option_arg)); break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } llvm::ArrayRef OptionGroupPlatformCaching::GetDefinitions() { return llvm::makeArrayRef(g_caching_option_table); } void OptionGroupPlatformCaching::OptionParsingStarting( ExecutionContext *execution_context) { m_cache_dir.clear(); } lldb_private::Status OptionGroupPlatformCaching::SetOptionValue( uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) { Status error; char short_option = (char)GetDefinitions()[option_idx].short_option; switch (short_option) { case 'c': m_cache_dir.assign(std::string(option_arg)); break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } Environment Platform::GetEnvironment() { return Environment(); } const std::vector &Platform::GetTrapHandlerSymbolNames() { if (!m_calculated_trap_handlers) { std::lock_guard guard(m_mutex); if (!m_calculated_trap_handlers) { CalculateTrapHandlerSymbolNames(); m_calculated_trap_handlers = true; } } return m_trap_handlers; } Status Platform::GetCachedExecutable( ModuleSpec &module_spec, lldb::ModuleSP &module_sp, const FileSpecList *module_search_paths_ptr, Platform &remote_platform) { const auto platform_spec = module_spec.GetFileSpec(); const auto error = LoadCachedExecutable( module_spec, module_sp, module_search_paths_ptr, remote_platform); if (error.Success()) { module_spec.GetFileSpec() = module_sp->GetFileSpec(); module_spec.GetPlatformFileSpec() = platform_spec; } return error; } Status Platform::LoadCachedExecutable( const ModuleSpec &module_spec, lldb::ModuleSP &module_sp, const FileSpecList *module_search_paths_ptr, Platform &remote_platform) { return GetRemoteSharedModule(module_spec, nullptr, module_sp, [&](const ModuleSpec &spec) { return remote_platform.ResolveExecutable( spec, module_sp, module_search_paths_ptr); }, nullptr); } Status Platform::GetRemoteSharedModule(const ModuleSpec &module_spec, Process *process, lldb::ModuleSP &module_sp, const ModuleResolver &module_resolver, bool *did_create_ptr) { // Get module information from a target. ModuleSpec resolved_module_spec; bool got_module_spec = false; if (process) { // Try to get module information from the process if (process->GetModuleSpec(module_spec.GetFileSpec(), module_spec.GetArchitecture(), resolved_module_spec)) { if (!module_spec.GetUUID().IsValid() || module_spec.GetUUID() == resolved_module_spec.GetUUID()) { got_module_spec = true; } } } if (!module_spec.GetArchitecture().IsValid()) { Status error; // No valid architecture was specified, ask the platform for the // architectures that we should be using (in the correct order) and see if // we can find a match that way ModuleSpec arch_module_spec(module_spec); for (uint32_t idx = 0; GetSupportedArchitectureAtIndex( idx, arch_module_spec.GetArchitecture()); ++idx) { error = ModuleList::GetSharedModule(arch_module_spec, module_sp, nullptr, nullptr, nullptr); // Did we find an executable using one of the if (error.Success() && module_sp) break; } if (module_sp) got_module_spec = true; } if (!got_module_spec) { // Get module information from a target. if (!GetModuleSpec(module_spec.GetFileSpec(), module_spec.GetArchitecture(), resolved_module_spec)) { if (!module_spec.GetUUID().IsValid() || module_spec.GetUUID() == resolved_module_spec.GetUUID()) { return module_resolver(module_spec); } } } // If we are looking for a specific UUID, make sure resolved_module_spec has // the same one before we search. if (module_spec.GetUUID().IsValid()) { resolved_module_spec.GetUUID() = module_spec.GetUUID(); } // Trying to find a module by UUID on local file system. const auto error = module_resolver(resolved_module_spec); if (error.Fail()) { if (GetCachedSharedModule(resolved_module_spec, module_sp, did_create_ptr)) return Status(); } return error; } bool Platform::GetCachedSharedModule(const ModuleSpec &module_spec, lldb::ModuleSP &module_sp, bool *did_create_ptr) { if (IsHost() || !GetGlobalPlatformProperties()->GetUseModuleCache() || !GetGlobalPlatformProperties()->GetModuleCacheDirectory()) return false; Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); // Check local cache for a module. auto error = m_module_cache->GetAndPut( GetModuleCacheRoot(), GetCacheHostname(), module_spec, [this](const ModuleSpec &module_spec, const FileSpec &tmp_download_file_spec) { return DownloadModuleSlice( module_spec.GetFileSpec(), module_spec.GetObjectOffset(), module_spec.GetObjectSize(), tmp_download_file_spec); }, [this](const ModuleSP &module_sp, const FileSpec &tmp_download_file_spec) { return DownloadSymbolFile(module_sp, tmp_download_file_spec); }, module_sp, did_create_ptr); if (error.Success()) return true; LLDB_LOGF(log, "Platform::%s - module %s not found in local cache: %s", __FUNCTION__, module_spec.GetUUID().GetAsString().c_str(), error.AsCString()); return false; } Status Platform::DownloadModuleSlice(const FileSpec &src_file_spec, const uint64_t src_offset, const uint64_t src_size, const FileSpec &dst_file_spec) { Status error; std::error_code EC; llvm::raw_fd_ostream dst(dst_file_spec.GetPath(), EC, llvm::sys::fs::OF_None); if (EC) { error.SetErrorStringWithFormat("unable to open destination file: %s", dst_file_spec.GetPath().c_str()); return error; } auto src_fd = OpenFile(src_file_spec, File::eOpenOptionRead, lldb::eFilePermissionsFileDefault, error); if (error.Fail()) { error.SetErrorStringWithFormat("unable to open source file: %s", error.AsCString()); return error; } std::vector buffer(1024); auto offset = src_offset; uint64_t total_bytes_read = 0; while (total_bytes_read < src_size) { const auto to_read = std::min(static_cast(buffer.size()), src_size - total_bytes_read); const uint64_t n_read = ReadFile(src_fd, offset, &buffer[0], to_read, error); if (error.Fail()) break; if (n_read == 0) { error.SetErrorString("read 0 bytes"); break; } offset += n_read; total_bytes_read += n_read; dst.write(&buffer[0], n_read); } Status close_error; CloseFile(src_fd, close_error); // Ignoring close error. return error; } Status Platform::DownloadSymbolFile(const lldb::ModuleSP &module_sp, const FileSpec &dst_file_spec) { return Status( "Symbol file downloading not supported by the default platform."); } FileSpec Platform::GetModuleCacheRoot() { auto dir_spec = GetGlobalPlatformProperties()->GetModuleCacheDirectory(); dir_spec.AppendPathComponent(GetName().AsCString()); return dir_spec; } const char *Platform::GetCacheHostname() { return GetHostname(); } const UnixSignalsSP &Platform::GetRemoteUnixSignals() { static const auto s_default_unix_signals_sp = std::make_shared(); return s_default_unix_signals_sp; } UnixSignalsSP Platform::GetUnixSignals() { if (IsHost()) return UnixSignals::CreateForHost(); return GetRemoteUnixSignals(); } uint32_t Platform::LoadImage(lldb_private::Process *process, const lldb_private::FileSpec &local_file, const lldb_private::FileSpec &remote_file, lldb_private::Status &error) { if (local_file && remote_file) { // Both local and remote file was specified. Install the local file to the // given location. if (IsRemote() || local_file != remote_file) { error = Install(local_file, remote_file); if (error.Fail()) return LLDB_INVALID_IMAGE_TOKEN; } return DoLoadImage(process, remote_file, nullptr, error); } if (local_file) { // Only local file was specified. Install it to the current working // directory. FileSpec target_file = GetWorkingDirectory(); target_file.AppendPathComponent(local_file.GetFilename().AsCString()); if (IsRemote() || local_file != target_file) { error = Install(local_file, target_file); if (error.Fail()) return LLDB_INVALID_IMAGE_TOKEN; } return DoLoadImage(process, target_file, nullptr, error); } if (remote_file) { // Only remote file was specified so we don't have to do any copying return DoLoadImage(process, remote_file, nullptr, error); } error.SetErrorString("Neither local nor remote file was specified"); return LLDB_INVALID_IMAGE_TOKEN; } uint32_t Platform::DoLoadImage(lldb_private::Process *process, const lldb_private::FileSpec &remote_file, const std::vector *paths, lldb_private::Status &error, lldb_private::FileSpec *loaded_image) { error.SetErrorString("LoadImage is not supported on the current platform"); return LLDB_INVALID_IMAGE_TOKEN; } uint32_t Platform::LoadImageUsingPaths(lldb_private::Process *process, const lldb_private::FileSpec &remote_filename, const std::vector &paths, lldb_private::Status &error, lldb_private::FileSpec *loaded_path) { FileSpec file_to_use; if (remote_filename.IsAbsolute()) file_to_use = FileSpec(remote_filename.GetFilename().GetStringRef(), remote_filename.GetPathStyle()); else file_to_use = remote_filename; return DoLoadImage(process, file_to_use, &paths, error, loaded_path); } Status Platform::UnloadImage(lldb_private::Process *process, uint32_t image_token) { return Status("UnloadImage is not supported on the current platform"); } lldb::ProcessSP Platform::ConnectProcess(llvm::StringRef connect_url, llvm::StringRef plugin_name, lldb_private::Debugger &debugger, lldb_private::Target *target, lldb_private::Status &error) { error.Clear(); if (!target) { ArchSpec arch; if (target && target->GetArchitecture().IsValid()) arch = target->GetArchitecture(); else arch = Target::GetDefaultArchitecture(); const char *triple = ""; if (arch.IsValid()) triple = arch.GetTriple().getTriple().c_str(); TargetSP new_target_sp; error = debugger.GetTargetList().CreateTarget( debugger, "", triple, eLoadDependentsNo, nullptr, new_target_sp); target = new_target_sp.get(); } if (!target || error.Fail()) return nullptr; debugger.GetTargetList().SetSelectedTarget(target); lldb::ProcessSP process_sp = target->CreateProcess(debugger.GetListener(), plugin_name, nullptr); if (!process_sp) return nullptr; - error = process_sp->ConnectRemote(&debugger.GetOutputStream(), connect_url); + error = process_sp->ConnectRemote(connect_url); if (error.Fail()) return nullptr; return process_sp; } size_t Platform::ConnectToWaitingProcesses(lldb_private::Debugger &debugger, lldb_private::Status &error) { error.Clear(); return 0; } size_t Platform::GetSoftwareBreakpointTrapOpcode(Target &target, BreakpointSite *bp_site) { ArchSpec arch = target.GetArchitecture(); assert(arch.IsValid()); const uint8_t *trap_opcode = nullptr; size_t trap_opcode_size = 0; switch (arch.GetMachine()) { case llvm::Triple::aarch64_32: case llvm::Triple::aarch64: { static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x20, 0xd4}; trap_opcode = g_aarch64_opcode; trap_opcode_size = sizeof(g_aarch64_opcode); } break; case llvm::Triple::arc: { static const uint8_t g_hex_opcode[] = { 0xff, 0x7f }; trap_opcode = g_hex_opcode; trap_opcode_size = sizeof(g_hex_opcode); } break; // TODO: support big-endian arm and thumb trap codes. case llvm::Triple::arm: { // The ARM reference recommends the use of 0xe7fddefe and 0xdefe but the // linux kernel does otherwise. static const uint8_t g_arm_breakpoint_opcode[] = {0xf0, 0x01, 0xf0, 0xe7}; static const uint8_t g_thumb_breakpoint_opcode[] = {0x01, 0xde}; lldb::BreakpointLocationSP bp_loc_sp(bp_site->GetOwnerAtIndex(0)); AddressClass addr_class = AddressClass::eUnknown; if (bp_loc_sp) { addr_class = bp_loc_sp->GetAddress().GetAddressClass(); if (addr_class == AddressClass::eUnknown && (bp_loc_sp->GetAddress().GetFileAddress() & 1)) addr_class = AddressClass::eCodeAlternateISA; } if (addr_class == AddressClass::eCodeAlternateISA) { trap_opcode = g_thumb_breakpoint_opcode; trap_opcode_size = sizeof(g_thumb_breakpoint_opcode); } else { trap_opcode = g_arm_breakpoint_opcode; trap_opcode_size = sizeof(g_arm_breakpoint_opcode); } } break; case llvm::Triple::avr: { static const uint8_t g_hex_opcode[] = {0x98, 0x95}; trap_opcode = g_hex_opcode; trap_opcode_size = sizeof(g_hex_opcode); } break; case llvm::Triple::mips: case llvm::Triple::mips64: { static const uint8_t g_hex_opcode[] = {0x00, 0x00, 0x00, 0x0d}; trap_opcode = g_hex_opcode; trap_opcode_size = sizeof(g_hex_opcode); } break; case llvm::Triple::mipsel: case llvm::Triple::mips64el: { static const uint8_t g_hex_opcode[] = {0x0d, 0x00, 0x00, 0x00}; trap_opcode = g_hex_opcode; trap_opcode_size = sizeof(g_hex_opcode); } break; case llvm::Triple::systemz: { static const uint8_t g_hex_opcode[] = {0x00, 0x01}; trap_opcode = g_hex_opcode; trap_opcode_size = sizeof(g_hex_opcode); } break; case llvm::Triple::hexagon: { static const uint8_t g_hex_opcode[] = {0x0c, 0xdb, 0x00, 0x54}; trap_opcode = g_hex_opcode; trap_opcode_size = sizeof(g_hex_opcode); } break; case llvm::Triple::ppc: case llvm::Triple::ppc64: { static const uint8_t g_ppc_opcode[] = {0x7f, 0xe0, 0x00, 0x08}; trap_opcode = g_ppc_opcode; trap_opcode_size = sizeof(g_ppc_opcode); } break; case llvm::Triple::ppc64le: { static const uint8_t g_ppc64le_opcode[] = {0x08, 0x00, 0xe0, 0x7f}; // trap trap_opcode = g_ppc64le_opcode; trap_opcode_size = sizeof(g_ppc64le_opcode); } break; case llvm::Triple::x86: case llvm::Triple::x86_64: { static const uint8_t g_i386_opcode[] = {0xCC}; trap_opcode = g_i386_opcode; trap_opcode_size = sizeof(g_i386_opcode); } break; default: return 0; } assert(bp_site); if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) return trap_opcode_size; return 0; } diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 78f75981a94d..d777a2713911 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -1,6194 +1,6194 @@ //===-- 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/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/Log.h" #include "lldb/Utility/NameMatches.h" #include "lldb/Utility/ProcessInfo.h" #include "lldb/Utility/SelectHelper.h" #include "lldb/Utility/State.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 OptionValueProperties { public: ProcessOptionValueProperties(ConstString name) : OptionValueProperties(name) {} // This constructor is used when creating ProcessOptionValueProperties when // it is part of a new lldb_private::Process instance. It will copy all // current global property values as needed ProcessOptionValueProperties(ProcessProperties *global_properties) : OptionValueProperties(*global_properties->GetValueProperties()) {} 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); } }; #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 OptionValueProperties { public: ProcessExperimentalOptionValueProperties() : OptionValueProperties( 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 = std::make_shared( Process::GetGlobalProperties().get()); 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); } 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::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); } 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); } Status ProcessLaunchCommandOptions::SetOptionValue( uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 's': // Stop at program entry point launch_info.GetFlags().Set(eLaunchFlagStopAtEntry); break; case 'i': // STDIN for read only { FileAction action; if (action.Open(STDIN_FILENO, FileSpec(option_arg), true, false)) launch_info.AppendFileAction(action); break; } case 'o': // Open STDOUT for write only { FileAction action; if (action.Open(STDOUT_FILENO, FileSpec(option_arg), false, true)) launch_info.AppendFileAction(action); break; } case 'e': // STDERR for write only { FileAction action; if (action.Open(STDERR_FILENO, FileSpec(option_arg), false, true)) launch_info.AppendFileAction(action); break; } case 'p': // Process plug-in name launch_info.SetProcessPluginName(option_arg); break; case 'n': // Disable STDIO { FileAction action; const FileSpec dev_null(FileSystem::DEV_NULL); if (action.Open(STDIN_FILENO, dev_null, true, false)) launch_info.AppendFileAction(action); if (action.Open(STDOUT_FILENO, dev_null, false, true)) launch_info.AppendFileAction(action); if (action.Open(STDERR_FILENO, dev_null, false, true)) launch_info.AppendFileAction(action); break; } case 'w': launch_info.SetWorkingDirectory(FileSpec(option_arg)); break; case 't': // Open process in new terminal window launch_info.GetFlags().Set(eLaunchFlagLaunchInTTY); break; case 'a': { TargetSP target_sp = execution_context ? execution_context->GetTargetSP() : TargetSP(); PlatformSP platform_sp = target_sp ? target_sp->GetPlatform() : PlatformSP(); launch_info.GetArchitecture() = Platform::GetAugmentedArchSpec(platform_sp.get(), option_arg); } break; case 'A': // Disable ASLR. { bool success; const bool disable_aslr_arg = OptionArgParser::ToBoolean(option_arg, true, &success); if (success) disable_aslr = disable_aslr_arg ? eLazyBoolYes : eLazyBoolNo; else error.SetErrorStringWithFormat( "Invalid boolean value for disable-aslr option: '%s'", option_arg.empty() ? "" : option_arg.str().c_str()); break; } case 'X': // shell expand args. { bool success; const bool expand_args = OptionArgParser::ToBoolean(option_arg, true, &success); if (success) launch_info.SetShellExpandArguments(expand_args); else error.SetErrorStringWithFormat( "Invalid boolean value for shell-expand-args option: '%s'", option_arg.empty() ? "" : option_arg.str().c_str()); break; } case 'c': if (!option_arg.empty()) launch_info.SetShell(FileSpec(option_arg)); else launch_info.SetShell(HostInfo::GetDefaultShell()); break; case 'v': launch_info.GetEnvironment().insert(option_arg); break; default: error.SetErrorStringWithFormat("unrecognized short option character '%c'", short_option); break; } return error; } static constexpr OptionDefinition g_process_launch_options[] = { {LLDB_OPT_SET_ALL, false, "stop-at-entry", 's', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Stop at the entry point of the program when launching a process."}, {LLDB_OPT_SET_ALL, false, "disable-aslr", 'A', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Set whether to disable address space layout randomization when launching " "a process."}, {LLDB_OPT_SET_ALL, false, "plugin", 'p', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePlugin, "Name of the process plugin you want to use."}, {LLDB_OPT_SET_ALL, false, "working-dir", 'w', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeDirectoryName, "Set the current working directory to when running the inferior."}, {LLDB_OPT_SET_ALL, false, "arch", 'a', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeArchitecture, "Set the architecture for the process to launch when ambiguous."}, {LLDB_OPT_SET_ALL, false, "environment", 'v', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNone, "Specify an environment variable name/value string (--environment " "NAME=VALUE). Can be specified multiple times for subsequent environment " "entries."}, {LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "shell", 'c', OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypeFilename, "Run the process in a shell (not supported on all platforms)."}, {LLDB_OPT_SET_1, false, "stdin", 'i', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeFilename, "Redirect stdin for the process to ."}, {LLDB_OPT_SET_1, false, "stdout", 'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeFilename, "Redirect stdout for the process to ."}, {LLDB_OPT_SET_1, false, "stderr", 'e', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeFilename, "Redirect stderr for the process to ."}, {LLDB_OPT_SET_2, false, "tty", 't', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Start the process in a terminal (not supported on all platforms)."}, {LLDB_OPT_SET_3, false, "no-stdio", 'n', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Do not set up for terminal I/O to go to running process."}, {LLDB_OPT_SET_4, false, "shell-expand-args", 'X', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Set whether to shell expand arguments to the process when launching."}, }; llvm::ArrayRef ProcessLaunchCommandOptions::GetDefinitions() { return llvm::makeArrayRef(g_process_launch_options); } ProcessSP Process::FindPlugin(lldb::TargetSP target_sp, llvm::StringRef plugin_name, ListenerSP listener_sp, const FileSpec *crash_file_path) { static uint32_t g_process_unique_id = 0; ProcessSP process_sp; ProcessCreateInstance create_callback = nullptr; if (!plugin_name.empty()) { ConstString const_plugin_name(plugin_name); create_callback = PluginManager::GetProcessCreateCallbackForPluginName(const_plugin_name); if (create_callback) { process_sp = create_callback(target_sp, listener_sp, crash_file_path); 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); 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), UserID(LLDB_INVALID_PROCESS_ID), 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_finalize_called(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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(); } const ProcessPropertiesSP &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 ProcessPropertiesSP *g_settings_sp_ptr = new ProcessPropertiesSP(new ProcessProperties(nullptr)); return *g_settings_sp_ptr; } void Process::Finalize() { m_finalizing = true; // Destroy this process if needed switch (GetPrivateState()) { case eStateConnected: case eStateAttaching: case eStateLaunching: case eStateStopped: case eStateRunning: case eStateStepping: case eStateCrashed: case eStateSuspended: Destroy(false); break; case eStateInvalid: case eStateUnloaded: case eStateDetached: case eStateExited: break; } // 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(); m_finalize_called = true; } 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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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; if (curr_thread) { curr_thread_stop_reason = curr_thread->GetStopReason(); curr_thread_stop_info_sp = curr_thread->GetStopInfo(); } if (!curr_thread || !curr_thread->IsValid() || curr_thread_stop_reason == eStopReasonInvalid || curr_thread_stop_reason == eStopReasonNone) { // 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 eStopReasonThreadExiting: case eStopReasonInstrumentation: 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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | LIBLLDB_LOG_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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_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; } 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 + // 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) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | LIBLLDB_LOG_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); const bool new_state_is_stopped = StateIsStoppedState(new_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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | LIBLLDB_LOG_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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | LIBLLDB_LOG_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_finalize_called) return; Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); 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(); m_mod_id.BumpStopID(); 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()); } // Use our target to get a shared pointer to ourselves... if (m_finalize_called && !PrivateStateThreadIsValid()) BroadcastEvent(event_sp); else 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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_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(); } size_t Process::ReadStringFromMemory(addr_t addr, char *dst, size_t max_bytes, Status &error, size_t type_width) { size_t total_bytes_read = 0; if (dst && max_bytes && type_width && max_bytes >= type_width) { // 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!"); addr_t curr_addr = addr; const size_t cache_line_size = m_memory_cache.GetMemoryCacheLineSize(); char *curr_dst = dst; error.Clear(); while (bytes_left > 0 && error.Success()) { 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) 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; curr_addr += bytes_read; bytes_left -= bytes_read; } } else { if (max_bytes) error.SetErrorString("invalid arguments"); } return total_bytes_read; } // 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) { 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; 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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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 = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(); if (!exe_module) { error.SetErrorString("executable module does not exist"); return error; } char local_exec_file_path[PATH_MAX]; char platform_exec_file_path[PATH_MAX]; exe_module->GetFileSpec().GetPath(local_exec_file_path, sizeof(local_exec_file_path)); exe_module->GetPlatformFileSpec().GetPath(platform_exec_file_path, sizeof(platform_exec_file_path)); if (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; 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. 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 { error.SetErrorStringWithFormat("file doesn't exist: '%s'", local_exec_file_path); } 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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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, nullptr)); return m_dyld_up.get(); } DataExtractor Process::GetAuxvData() { return DataExtractor(); } 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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_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(); if (process_arch.IsValid() && !GetTarget().GetArchitecture().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_LOGF(log, "Process::%s after DynamicLoader::DidAttach(), target " "executable is %s (using %s plugin)", __FUNCTION__, exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str() : "", dyld->GetPluginName().AsCString("")); } } GetJITLoaders().DidAttach(); SystemRuntime *system_runtime = GetSystemRuntime(); if (system_runtime) { system_runtime->DidAttach(); if (log) { ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); LLDB_LOGF(log, "Process::%s after SystemRuntime::DidAttach(), target " "executable is %s (using %s plugin)", __FUNCTION__, exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str() : "", system_runtime->GetPluginName().AsCString("")); } } 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: const ModuleList &target_modules = GetTarget().GetImages(); std::lock_guard guard(target_modules.GetMutex()); size_t num_modules = target_modules.GetSize(); ModuleSP new_executable_module_sp; for (size_t i = 0; i < num_modules; i++) { ModuleSP module_sp(target_modules.GetModuleAtIndexUnlocked(i)); 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(Stream *strm, llvm::StringRef remote_url) { +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(strm, remote_url)); + 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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_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.SetErrorStringWithFormat( "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 10 second for the process to stop. StateType state = WaitForProcessToStop( seconds(10), &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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(seconds(10), &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_finalize_called) return {}; // 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() != eStateRunning) { // 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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EVENTS | LIBLLDB_LOG_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 stop_vote = m_thread_list.ShouldReportStop(event_ptr); LLDB_LOGF(log, "Process::ShouldBroadcastEvent: should_resume: %i state: " "%s was_restarted: %i stop_vote: %d.", should_resume, StateAsCString(state), was_restarted, stop_vote); switch (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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); LLDB_LOGF( log, "Went to stop the private state thread, but it was already invalid."); } } void Process::ControlPrivateStateThread(uint32_t signal) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(), m_state(eStateInvalid), m_restarted(false), m_update_state(0), m_interrupted(false) {} 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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP | LIBLLDB_LOG_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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP | LIBLLDB_LOG_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. process_sp->GetTarget().RunStopHooks(); if (process_sp->GetPrivateState() == eStateRunning) 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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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::eOpenOptionRead, false), m_write_file(write_fd, File::eOpenOptionWrite, 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(); TerminalState terminal_state; terminal_state.Save(read_fd, false); Terminal terminal(read_fd); terminal.SetCanonical(false); 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 terminal_state.Restore(); } 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 master 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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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 master" 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_master = m_thread_plan_sp->IsMasterPlan(); 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->SetIsMasterPlan(m_is_master); 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_master; 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 = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP | LIBLLDB_LOG_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 master 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->SetIsMasterPlan(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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP | LIBLLDB_LOG_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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_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; } void Process::DidExec() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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 { m_resolved_indirect_addresses.insert( std::pair(addr, function_addr)); } } return function_addr; } void Process::ModulesDidLoad(ModuleList &module_list) { SystemRuntime *sys_runtime = GetSystemRuntime(); if (sys_runtime) { sys_runtime->ModulesDidLoad(module_list); } GetJITLoaders().ModulesDidLoad(module_list); // Give runtimes a chance to be created. InstrumentationRuntime::ModulesDidLoad(module_list, this, m_instrumentation_runtimes); // Tell runtimes about new modules. for (auto pos = m_instrumentation_runtimes.begin(); pos != m_instrumentation_runtimes.end(); ++pos) { InstrumentationRuntimeSP runtime = pos->second; runtime->ModulesDidLoad(module_list); } // Let any language runtimes we have already created know about the modules // that loaded. // Iterate over a copy of this language runtime list in case the language // runtime ModulesDidLoad somehow causes the language runtime to be // unloaded. { std::lock_guard guard(m_language_runtimes_mutex); LanguageRuntimeCollection language_runtimes(m_language_runtimes); for (const auto &pair : language_runtimes) { // We must check language_runtime_sp to make sure it is not nullptr as we // might cache the fact that we didn't have a language runtime for a // language. LanguageRuntimeSP language_runtime_sp = pair.second; if (language_runtime_sp) language_runtime_sp->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); // Give structured-data plugins a chance to see 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; auto type_system_or_err = sc.module_sp->GetTypeSystemForLanguage(language); if (auto err = type_system_or_err.takeError()) { llvm::consumeError(std::move(err)); PrintWarning(Process::Warnings::eWarningsUnsupportedLanguage, sc.module_sp.get(), "This version of LLDB has no plugin for the %s language. " "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; const bool prefer_file_cache = true; disassembler_sp = Disassembler::DisassembleRange( target.GetArchitecture(), plugin_name, flavor, GetTarget(), range_bounds, prefer_file_cache); 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, target, 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::GetMemoryRegions(lldb_private::MemoryRegionInfos ®ion_list) { Status error; lldb::addr_t range_end = 0; 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; } range_end = region_info.GetRange().GetRangeEnd(); if (region_info.GetMapped() == MemoryRegionInfo::eYes) { region_list.push_back(std::move(region_info)); } } while (range_end != LLDB_INVALID_ADDRESS); 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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_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_LOGF(log, "Process::%s(): using plugin %s for type name " "%s", __FUNCTION__, plugin_sp->GetPluginName().GetCString(), type_name.GetCString()); } } // 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(); } 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; }