| // Copyright 2020 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_DEBUG_ADAPTER_CONTEXT_H_ |
| #define SRC_DEVELOPER_DEBUG_ZXDB_DEBUG_ADAPTER_CONTEXT_H_ |
| |
| #include <cstdint> |
| #include <utility> |
| |
| #include <dap/protocol.h> |
| #include <dap/session.h> |
| |
| #include "src/developer/debug/shared/stream_buffer.h" |
| #include "src/developer/debug/zxdb/client/frame.h" |
| #include "src/developer/debug/zxdb/client/process_observer.h" |
| #include "src/developer/debug/zxdb/client/session.h" |
| #include "src/developer/debug/zxdb/client/thread_observer.h" |
| #include "src/developer/debug/zxdb/common/err.h" |
| #include "src/developer/debug/zxdb/expr/format_node.h" |
| #include "src/lib/fxl/memory/weak_ptr.h" |
| |
| namespace zxdb { |
| |
| class Session; |
| class Breakpoint; |
| |
| class DebugAdapterServer; |
| class DebugAdapterReader; |
| class DebugAdapterWriter; |
| |
| // Types of variables reported in variables request. |
| enum class VariablesType { |
| kLocal = 0, |
| kArguments, |
| kRegister, |
| kChildVariable, |
| kVariablesTypeCount, // Keep this in the end always |
| }; |
| |
| struct VariablesRecord { |
| int64_t frame_id; |
| VariablesType type = VariablesType::kVariablesTypeCount; |
| // Fields to store children information corresponding to the record so that subsequent variables |
| // request can be processed. Store the format node in `parent` if children exist. If `parent`'s |
| // child has children, store a weak pointer to it in `child`. |
| std::unique_ptr<FormatNode> parent; |
| fxl::WeakPtr<FormatNode> child; |
| }; |
| |
| // Handles processing requests from debug adapter client with help from zxdb client session and dap |
| // library. |
| // Note: All methods in this class need to be executed on main thread to avoid concurrency bugs. |
| class DebugAdapterContext : public ThreadObserver, ProcessObserver { |
| public: |
| using DestroyConnectionCallback = std::function<void()>; |
| |
| explicit DebugAdapterContext(Session* session, debug::StreamBuffer* stream); |
| virtual ~DebugAdapterContext(); |
| |
| Session* session() { return session_; } |
| dap::Session& dap() { return *dap_; } |
| bool supports_run_in_terminal() { return supports_run_in_terminal_; } |
| |
| // Notification about the stream. |
| void OnStreamReadable(); |
| |
| // Callback to delete the connection and hence this context. This callback will be posted on |
| // message loop. |
| void set_destroy_connection_callback(DestroyConnectionCallback cb) { |
| destroy_connection_cb_ = std::move(cb); |
| } |
| |
| // ThreadObserver implementation: |
| void DidCreateThread(Thread* thread) override; |
| void WillDestroyThread(Thread* thread) override; |
| void OnThreadStopped(Thread* thread, const StopInfo& info) override; |
| void OnThreadFramesInvalidated(Thread* thread) override; |
| |
| // ProcessObserver implementation: |
| void DidCreateProcess(Process* process, bool autoattached_to_new_process, |
| uint64_t timestamp) override; |
| void WillDestroyProcess(Process* process, DestroyReason reason, int exit_code, |
| uint64_t timestamp) override; |
| |
| Thread* GetThread(uint64_t koid); |
| |
| // Checks if thread is in stopped state; returns error if not stopped. |
| // `thread` can be nullptr, in which case an error is returned. |
| Err CheckStoppedThread(Thread* thread); |
| |
| // Helper methods to get/set frame to ID mapping |
| int64_t IdForFrame(Frame* frame, int stack_index); |
| Frame* FrameforId(int64_t id); |
| void DeleteFrameIdsForThread(Thread* thread); |
| |
| // Helper methods to get/set variables references |
| int64_t IdForVariables(int64_t frame_id, VariablesType type, |
| std::unique_ptr<FormatNode> parent = nullptr, |
| fxl::WeakPtr<FormatNode> child = nullptr); |
| VariablesRecord* VariablesRecordForID(int64_t id); |
| void DeleteVariablesIdsForFrameId(int64_t id); |
| |
| // Helper methods to get/set breakpoint to source file mapping. |
| void StoreBreakpointForSource(const std::string& source, Breakpoint* bp); |
| std::vector<fxl::WeakPtr<Breakpoint>>* GetBreakpointsForSource(const std::string& source); |
| |
| // TODO(fxbug.dev/69392): These 2 method deletes all breakpoints added by the debug adapter. |
| // Breakpoints added from console are not deleted. |
| void DeleteBreakpointsForSource(const std::string& source); |
| void DeleteAllBreakpoints(); |
| |
| private: |
| Session* const session_; |
| const std::unique_ptr<dap::Session> dap_; |
| std::shared_ptr<DebugAdapterReader> reader_; |
| std::shared_ptr<DebugAdapterWriter> writer_; |
| |
| bool supports_run_in_terminal_ = false; |
| bool supports_invalidate_event_ = false; |
| bool init_done_ = false; |
| |
| struct FrameRecord { |
| uint64_t thread_koid = 0; |
| int stack_index = 0; |
| }; |
| std::map<int64_t, FrameRecord> id_to_frame_; |
| int64_t next_frame_id_ = 1; |
| |
| std::map<int64_t, VariablesRecord> id_to_variables_; |
| int64_t next_variables_id_ = 1; |
| |
| DestroyConnectionCallback destroy_connection_cb_; |
| |
| // This mapping is temporarily added to store all breakpoints added by debug adapter client. Once |
| // http://fxbug.dev/69392 is fixed, this can removed in favor of using System::GetBreakpoints API |
| // i.e. with breakpoint event, debug adapter client can be made aware of additional breakpoints |
| // (from say zxdb console) and hence breakpoint list maintained by system will be identical to |
| // this map in terms of the entries. One could traverse the entire system breakpoint list to get |
| // breakpoints related to a source file instead of having to maintain a separate map. |
| std::map<std::string, std::vector<fxl::WeakPtr<Breakpoint>>> source_to_bp_; |
| |
| void Init(); |
| }; |
| |
| class DebugAdapterReader : public dap::Reader { |
| public: |
| explicit DebugAdapterReader(debug::StreamBuffer* stream) : stream_(stream) {} |
| size_t read(void* buffer, size_t n) override { |
| if (!stream_) { |
| return 0; |
| } |
| auto ret = stream_->Read(static_cast<char*>(buffer), n); |
| return ret; |
| } |
| bool isOpen() override { return !!stream_; } |
| |
| void close() override { stream_ = nullptr; } |
| |
| private: |
| debug::StreamBuffer* stream_ = nullptr; |
| }; |
| |
| class DebugAdapterWriter : public dap::Writer { |
| public: |
| explicit DebugAdapterWriter(debug::StreamBuffer* stream) : stream_(stream) {} |
| bool write(const void* buffer, size_t n) override { |
| if (!stream_) { |
| return false; |
| } |
| stream_->Write( |
| std::vector<char>(static_cast<const char*>(buffer), static_cast<const char*>(buffer) + n)); |
| return true; |
| } |
| bool isOpen() override { return !!stream_; } |
| |
| void close() override { stream_ = nullptr; } |
| |
| private: |
| debug::StreamBuffer* stream_ = nullptr; |
| }; |
| |
| } // namespace zxdb |
| |
| #endif // SRC_DEVELOPER_DEBUG_ZXDB_DEBUG_ADAPTER_CONTEXT_H_ |