| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_DEVELOPER_DEBUG_ZXDB_CLIENT_SESSION_H_ |
| #define SRC_DEVELOPER_DEBUG_ZXDB_CLIENT_SESSION_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <vector> |
| |
| #include "lib/fit/function.h" |
| #include "src/developer/debug/zxdb/client/arch_info.h" |
| #include "src/developer/debug/zxdb/client/session_observer.h" |
| #include "src/developer/debug/zxdb/client/system.h" |
| #include "src/developer/debug/zxdb/common/err.h" |
| #include "src/developer/debug/zxdb/expr/abi.h" |
| #include "src/lib/fxl/memory/ref_ptr.h" |
| #include "src/lib/fxl/memory/weak_ptr.h" |
| |
| namespace debug { |
| class BufferedFD; |
| class StreamBuffer; |
| } // namespace debug |
| |
| namespace zxdb { |
| |
| class ArchInfo; |
| class BreakpointObserver; |
| class DownloadObserver; |
| class FilterObserver; |
| class ProcessImpl; |
| class ProcessObserver; |
| class RemoteAPI; |
| class RemoteAPIImpl; |
| class RemoteAPITest; |
| class TargetObserver; |
| class ThreadImpl; |
| class ThreadObserver; |
| |
| enum class SessionConnectionType : uint32_t { |
| kNetwork = 1, |
| kUnix = 2, |
| }; |
| |
| struct SessionConnectionInfo { |
| SessionConnectionType type = SessionConnectionType::kNetwork; |
| |
| // If the connection type is `Network` then `host` is the IP address or URL. |
| // If the connection type is `Unix` then `host` is the file path to the socket. |
| std::string host; |
| |
| // If the connection type is `Network` then `port` is the port address. |
| // If the connection type is `Unix` then `port` is unused. |
| uint16_t port = 0; |
| }; |
| |
| // The session object manages the connection with the remote debug agent. |
| class Session : public SettingStoreObserver { |
| public: |
| // Creates a session with no connection. All sending will fail until the callback associated with |
| // a Connect() call is issued. |
| Session(); |
| |
| // Creates a session using a custom RemoteAPI implementation. Use for tests to mock out sending |
| // IPC messages. |
| Session(std::unique_ptr<RemoteAPI> remote_api, debug::Arch arch); |
| |
| // Creates with a previously-allocated connection. The pointer must outlive this class. In this |
| // mode, the stream can not be disconnected. |
| explicit Session(debug::StreamBuffer* stream); |
| virtual ~Session(); |
| |
| fxl::WeakPtr<Session> GetWeakPtr(); |
| |
| // The RempteAPI for sending messages to the debug_agent. |
| RemoteAPI* remote_api() { return remote_api_.get(); } |
| |
| void AddObserver(SessionObserver* observer); |
| void RemoveObserver(SessionObserver* observer); |
| |
| void AddBreakpointObserver(BreakpointObserver* observer); |
| void RemoveBreakpointObserver(BreakpointObserver* observer); |
| |
| void AddFilterObserver(FilterObserver* observer); |
| void RemoveFilterObserver(FilterObserver* observer); |
| |
| void AddDownloadObserver(DownloadObserver* observer); |
| void RemoveDownloadObserver(DownloadObserver* observer); |
| |
| // Returns information about whether this session is connected to a minidump instead of a live |
| // system. |
| bool is_minidump() const { return is_minidump_; } |
| |
| // Notification about the stream. |
| void OnStreamReadable(); |
| void OnStreamError(); |
| |
| // Returns true if there is currently a connection. |
| bool IsConnected() const; |
| |
| // Information about the current connection. |
| const std::string minidump_path() const { return minidump_path_; } |
| const std::string connected_host() const { return connected_info_.host; } |
| uint16_t connected_port() const { return connected_info_.port; } |
| |
| // Call with an empty host and 0 port to reconnect to the last attempted connection destination. |
| // If there is no previous destination, this will be issue an error. |
| void Connect(const SessionConnectionInfo& info, fit::callback<void(const Err&)> cb); |
| |
| // Disconnects from the remote system. Calling when there is no connection connection will issue |
| // the callback with an error. |
| // |
| // This can also be called when a connection is pending (Connect() has been called but the |
| // callback has not been issued yet) which will cancel the pending connection. The Connect() |
| // callback will still be issued but will indicate failure. |
| void Disconnect(fit::callback<void(const Err&)> callback); |
| |
| // Open a minidump instead of connecting to a running system. The callback will be issued with an |
| // error if the file cannot be opened or if there is already a connection. |
| void OpenMinidump(const std::string& path, fit::callback<void(const Err&)> callback); |
| |
| // Frees all connection-related data. A helper for different modes of cleanup. Returns true if |
| // there was a connection to clear. |
| bool ClearConnectionData(); |
| |
| // Access to the singleton corresponding to the debugged system. |
| System& system() { return system_; } |
| |
| // Architecture of the attached system. Will be "kUnknown" when not connected. |
| debug::Arch arch() const { return arch_; } |
| |
| // Architecture information of the attached system. |
| const ArchInfo& arch_info() const { return *arch_info_; } |
| |
| // Observer list getters. |
| fxl::ObserverList<TargetObserver>& target_observers() { return target_observers_; } |
| fxl::ObserverList<ProcessObserver>& process_observers() { return process_observers_; } |
| fxl::ObserverList<ThreadObserver>& thread_observers() { return thread_observers_; } |
| fxl::ObserverList<BreakpointObserver>& breakpoint_observers() { return breakpoint_observers_; } |
| fxl::ObserverList<FilterObserver>& filter_observers() { return filter_observers_; } |
| fxl::ObserverList<DownloadObserver>& download_observers() { return download_observers_; } |
| |
| // When the client tells the agent to launch a component, it will return an unique id identifying |
| // that launch. Later, when the component effectively starts, the session will use that ID to know |
| // which component it is. |
| void ExpectComponent(uint32_t component_id); |
| |
| // Dispatches these particular notification types from the agent. These are public since tests |
| // will commonly want to synthesize these events. |
| // |
| // Note on DispatchNotifyException: Test code can skip setting the metadata by clearing the |
| // set_metadata flag. This allows them to set up the thread's state manually before issuing an |
| // exception. Production code should always set the set_metadata flag to populate the thread's |
| // state from the data in the exception. |
| void DispatchNotifyThreadStarting(const debug_ipc::NotifyThread& notify); |
| void DispatchNotifyThreadExiting(const debug_ipc::NotifyThread& notify); |
| void DispatchNotifyException(const debug_ipc::NotifyException& notify, bool set_metadata = true); |
| void DispatchNotifyModules(const debug_ipc::NotifyModules& notify); |
| void DispatchProcessStarting(const debug_ipc::NotifyProcessStarting&); |
| void DispatchNotifyIO(const debug_ipc::NotifyIO& notify); |
| |
| // Sends an explicit quit cmd to the agent. |
| void QuitAgent(fit::callback<void(const Err&)> callback); |
| |
| // SettingStoreObserver |
| void OnSettingChanged(const SettingStore&, const std::string& setting_name) override; |
| |
| // For test purposes, so that the Session appears to be connected. |
| void set_stream(debug::StreamBuffer* stream) { stream_ = stream; } |
| |
| protected: |
| fxl::ObserverList<SessionObserver> observers_; |
| |
| private: |
| class PendingConnection; |
| friend PendingConnection; |
| friend RemoteAPIImpl; |
| friend RemoteAPITest; |
| |
| // Nonspecific callback type. Implemented by SessionDispatchCallback (with the type-specific |
| // parameter pre-bound). The uint32_t is the transaction ID. If the error is set, the data will be |
| // invalid and the callback should be issued with the error instead of trying to deserialize. |
| using Callback = fit::callback<void(const Err&, std::vector<char>)>; |
| |
| // Set the arch_ and arch_info_ fields. |
| Err SetArch(debug::Arch arch); |
| |
| // Checks whether it's safe to begin establishing a connection. If not, the callback is invoked |
| // with details. The opening_dump argument indicates whether we are trying to open a dump file |
| // rather than connect to a debug agent. |
| bool ConnectCanProceed(fit::callback<void(const Err&)>& callback, bool opening_dump); |
| |
| // Dispatches unsolicited notifications sent from the agent. |
| void DispatchNotification(const debug_ipc::MsgHeader& header, std::vector<char> data); |
| |
| // Returns the thread object from the given koids, or null. |
| ThreadImpl* ThreadImplFromKoid(const debug_ipc::ProcessThreadId& id); |
| |
| // Callback when a connection has been successful or failed. |
| void ConnectionResolved(fxl::RefPtr<PendingConnection> pending, const Err& err, |
| const debug_ipc::HelloReply& reply, |
| std::unique_ptr<debug::BufferedFD> buffer, |
| fit::callback<void(const Err&)> callback); |
| |
| void ListenForSystemSettings(); |
| |
| void AttachToLimboProcessAndNotify(uint64_t koid, const std::string& process_name); |
| |
| // Configurations -------------------------------------------------------------------------------- |
| |
| // Upon connection, the session will tell the agent of all the configurations it should know |
| // about. |
| void SendAgentConfiguration(); |
| |
| // Pushes the quit agent config action into |actions|. |
| void ConfigQuitAgent(bool quit, std::vector<debug_ipc::ConfigAction>* actions); |
| |
| // Whether we have opened a core dump. Makes much of the connection-related stuff obsolete. |
| bool is_minidump_ = false; |
| |
| // Whether to automatically attach to processes found in Process Limbo upon a |
| // successful connection. |
| bool auto_attach_limbo_ = true; |
| |
| // Cache of koids that have been automatically attached from limbo during this session. If a koid |
| // that has been cached crashes again, it will not be automatically attached to. |
| // |
| // This behavior could be seen when a user detaches from a process in limbo (rather than |
| // explicitly killing it) and it immediately crashes again and ends back up in limbo and would |
| // otherwise attach automatically again. |
| std::set<uint64_t> koid_seen_in_limbo_; |
| |
| // Observers. |
| fxl::ObserverList<TargetObserver> target_observers_; |
| fxl::ObserverList<ProcessObserver> process_observers_; |
| fxl::ObserverList<ThreadObserver> thread_observers_; |
| fxl::ObserverList<BreakpointObserver> breakpoint_observers_; |
| fxl::ObserverList<FilterObserver> filter_observers_; |
| fxl::ObserverList<DownloadObserver> download_observers_; |
| |
| // Non-owning pointer to the connected stream. If this is non-null and connection_storage_ is |
| // null, the connection is persistent (made via the constructor) and can't be disconnected. |
| // |
| // This could be null when the connection_storage_ isn't when we're waiting for the initial |
| // connection. |
| debug::StreamBuffer* stream_ = nullptr; |
| |
| std::unique_ptr<RemoteAPI> remote_api_; |
| |
| // When using non-persistent connections (no connection passed in via the constructor), this will |
| // hold the underlying OS connection that is used to back stream_ as well as the |
| // |
| // Code should use stream_ for sending and receiving. |
| std::unique_ptr<debug::BufferedFD> connection_storage_; |
| |
| // Stores what the session is currently connected to. |
| std::string minidump_path_; |
| SessionConnectionInfo connected_info_; |
| |
| // When a connection has been requested but is being connected on the background thread, this will |
| // hold the pointer. |
| fxl::RefPtr<PendingConnection> pending_connection_; |
| |
| std::map<uint32_t, Callback> pending_; |
| uint32_t next_transaction_id_ = 1; // Reserve 0 for notifications. |
| |
| // Component ids that the session is currently waiting on. See ExpectComponent comments for more |
| // information on these ids. |
| std::set<uint32_t> expected_components_; |
| |
| System system_; |
| |
| debug::Arch arch_ = debug::Arch::kUnknown; |
| std::unique_ptr<ArchInfo> arch_info_; // Guaranteed non-null. |
| |
| // The last connection that was made by the session. Will have an empty host and a 0 port |
| // if there has never been a connection. |
| SessionConnectionInfo last_connection_; |
| |
| fxl::WeakPtrFactory<Session> weak_factory_; |
| }; |
| |
| } // namespace zxdb |
| |
| #endif // SRC_DEVELOPER_DEBUG_ZXDB_CLIENT_SESSION_H_ |