| // 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_CONSOLE_CONSOLE_CONTEXT_H_ |
| #define SRC_DEVELOPER_DEBUG_ZXDB_CONSOLE_CONSOLE_CONTEXT_H_ |
| |
| #include "src/developer/debug/zxdb/client/breakpoint_observer.h" |
| #include "src/developer/debug/zxdb/client/download_observer.h" |
| #include "src/developer/debug/zxdb/client/pretty_stack_manager.h" |
| #include "src/developer/debug/zxdb/client/process_observer.h" |
| #include "src/developer/debug/zxdb/client/session_observer.h" |
| #include "src/developer/debug/zxdb/client/system_observer.h" |
| #include "src/developer/debug/zxdb/client/target_observer.h" |
| #include "src/developer/debug/zxdb/client/thread_observer.h" |
| #include "src/developer/debug/zxdb/console/command.h" |
| |
| namespace zxdb { |
| |
| class Breakpoint; |
| class Command; |
| class Filter; |
| class Frame; |
| class OutputBuffer; |
| class Session; |
| |
| // The context for console commands. In a model-view-controller UI, this would |
| // represent the state associated with the view and controller (depending on |
| // how one splits things up). It keeps track of the currently selected |
| // objects and watches for changes. |
| // |
| // This class maintains the mapping between objects and IDs. |
| class ConsoleContext : public ProcessObserver, |
| public SessionObserver, |
| public SystemObserver, |
| public TargetObserver, |
| public ThreadObserver, |
| public BreakpointObserver, |
| public DownloadObserver { |
| public: |
| explicit ConsoleContext(Session* session); |
| ~ConsoleContext(); |
| |
| Session* session() { return session_; } |
| |
| // Returns the ID for the object. Asserts and returns 0 if not found. |
| int IdForTarget(const Target* target) const; |
| int IdForJob(const Job* job) const; |
| int IdForThread(const Thread* thread) const; |
| int IdForFrame(const Frame* frame) const; |
| int IdForBreakpoint(const Breakpoint* breakpoint) const; |
| int IdForFilter(const Filter* filter) const; |
| int IdForSymbolServer(const SymbolServer* symbol_server) const; |
| |
| // The active target will always exist except during setup and teardown. |
| void SetActiveTarget(const Target* target); |
| int GetActiveTargetId() const; |
| Target* GetActiveTarget() const; |
| |
| // The active symbol server may or may not exist. |
| void SetActiveSymbolServer(const SymbolServer* target); |
| int GetActiveSymbolServerId() const; |
| SymbolServer* GetActiveSymbolServer() const; |
| |
| // The active job will always exist except during setup and teardown. |
| void SetActiveJob(const Job* job); |
| int GetActiveJobId() const; |
| Job* GetActiveJob() const; |
| |
| // The active thread for its target. The active target is not affected. The |
| // active thread ID for a target not running will be 0. |
| void SetActiveThreadForTarget(const Thread* thread); |
| int GetActiveThreadIdForTarget(const Target* target); |
| Thread* GetActiveThreadForTarget(const Target* target); |
| |
| // Frames are a little bit different than threads and targets since they |
| // have an intrinsic numbering supplied by the Thread object (the index into |
| // the backtrace). If there are no frames on the thread, the return value |
| // will be 0 (so the return value can't be blindly indexed into the frames |
| // list). |
| void SetActiveFrameForThread(const Frame* frame); |
| void SetActiveFrameIdForThread(const Thread* thread, int id); |
| int GetActiveFrameIdForThread(const Thread* thread) const; |
| |
| // Sets the active breakpoint. Can be null/0 if there is no active breakpoint |
| // (set to null to clear). |
| void SetActiveBreakpoint(const Breakpoint* breakpoint); |
| int GetActiveBreakpointId() const; |
| Breakpoint* GetActiveBreakpoint() const; |
| |
| // Sets the active filter. Can be null/0 if there is no active filter (set to |
| // null to clear). |
| void SetActiveFilter(const Filter* filter); |
| int GetActiveFilterId() const; |
| Filter* GetActiveFilter() const; |
| |
| // Each thread maintains a source affinity which was the last command that |
| // implies either source code or disassembly viewing. This is used to control |
| // what gets displayed by default for the next stop of that thread. Defaults |
| // to kSource for new and unknown threads. Setting SourceAffinity::kNone does |
| // nothing so calling code can unconditionally call for all commands. |
| SourceAffinity GetSourceAffinityForThread(const Thread* thread) const; |
| void SetSourceAffinityForThread(const Thread* thread, SourceAffinity source_affinity); |
| |
| // Outputs to the console information on the given stopped thread with the |
| // given reasons for stopping. |
| void OutputThreadContext(const Thread* thread, const StopInfo& info) const; |
| |
| // Schedules evaluation and subsequent display of the "display" expressions. These are the things |
| // printed out for every thread stop. |
| void ScheduleDisplayExpressions(Thread* thread) const; |
| |
| // Fills the current effective process, thread, etc. into the given Command |
| // structure based on what the command specifies and the current context. |
| // Returns an error if any of the referenced IDs are invalid. |
| Err FillOutCommand(Command* cmd) const; |
| |
| // Returns the PrettyStackManager for this session. |
| // |
| // This object is currently sitting on the ConsoleContext as a convenient place to hold the |
| // singleton for the console frontend. Depending on how this evolve, it might be better to have |
| // the client layer manage this object. |
| const fxl::RefPtr<PrettyStackManager>& pretty_stack_manager() { return pretty_stack_manager_; } |
| |
| private: |
| struct ThreadRecord { |
| Thread* thread = nullptr; |
| |
| // This isn't necessarily valid since the frames could have been changed |
| // out from under us. Be sure to range check before use. |
| int active_frame_id = 0; |
| |
| // Default to showing source code for thread stops. |
| SourceAffinity source_affinity = SourceAffinity::kSource; |
| }; |
| |
| struct TargetRecord { |
| int target_id = 0; |
| Target* target = nullptr; |
| |
| int next_thread_id = 1; |
| |
| // The active ID will be 0 when there is no active thread (the case when |
| // the process is not running). |
| int active_thread_id = 0; |
| |
| std::map<int, ThreadRecord> id_to_thread; |
| std::map<const Thread*, int> thread_to_id; |
| }; |
| |
| struct JobRecord { |
| int job_id = 0; |
| Job* job = nullptr; |
| }; |
| |
| // SessionObserver implementation: |
| void HandleNotification(NotificationType, const std::string&) override; |
| void HandlePreviousConnectedProcesses(const std::vector<debug_ipc::ProcessRecord>&) override; |
| void HandleProcessesInLimbo(const std::vector<debug_ipc::ProcessRecord>&) override; |
| |
| // SystemObserver implementation: |
| void DidCreateJob(Job* job) override; |
| void WillDestroyJob(Job* job) override; |
| void DidCreateBreakpoint(Breakpoint* breakpoint) override; |
| void WillDestroyBreakpoint(Breakpoint* breakpoint) override; |
| void DidCreateFilter(Filter* filter) override; |
| void WillDestroyFilter(Filter* filter) override; |
| void DidCreateSymbolServer(SymbolServer* symbol_server) override; |
| |
| // TargetObserver implementation: |
| void DidCreateTarget(Target* target) override; |
| void WillDestroyTarget(Target* target) 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; |
| void OnSymbolLoadFailure(Process* process, const Err& err) override; |
| |
| // 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; |
| |
| // DownloadObserver implementation: |
| void OnDownloadsStarted() override; |
| void OnDownloadsStopped(size_t success, size_t fail) override; |
| |
| // BreakpointObserver implementation. |
| void OnBreakpointMatched(Breakpoint* breakpoint, bool user_requested) override; |
| void OnBreakpointUpdateFailure(Breakpoint* breakpoint, const Err& err) override; |
| |
| // Returns the record for the given target, or null (+ assertion) if not |
| // found. These pointers are not stable across target list changes. |
| TargetRecord* GetTargetRecord(int target_id); |
| const TargetRecord* GetTargetRecord(int target_id) const; |
| TargetRecord* GetTargetRecord(const Target* target); |
| const TargetRecord* GetTargetRecord(const Target* target) const; |
| |
| ThreadRecord* GetThreadRecord(const Thread* thread); |
| const ThreadRecord* GetThreadRecord(const Thread* thread) const; |
| |
| // Backends for parts of FillOutCommand. |
| // |
| // For the variants that take an input pointer, the pointer may be null if |
| // there is nothing of that type. |
| // |
| // For the variants that take an output pointer, the pointer will be stored |
| // if the corresponding item (target/thread) is found, otherwise it will be |
| // unchanged. |
| Err FillOutTarget(Command* cmd, TargetRecord const** out_target_record) const; |
| Err FillOutJob(Command* cmd) const; |
| Err FillOutThread(Command* cmd, const TargetRecord* target_record, |
| ThreadRecord const** out_thread_record) const; |
| Err FillOutFrame(Command* cmd, const ThreadRecord* thread_record) const; |
| Err FillOutBreakpoint(Command* cmd) const; |
| Err FillOutFilter(Command* cmd) const; |
| Err FillOutSymbolServer(Command* cmd) const; |
| |
| // Generates a string describing the breakpoints that were hit. |
| OutputBuffer DescribeHitBreakpoints(const std::vector<fxl::WeakPtr<Breakpoint>>& hits) const; |
| |
| // When a thread stops on a breakpoint, sets that breakpoint to be the default. |
| void SetActiveBreakpointForStop(const StopInfo& info); |
| |
| Session* const session_; |
| |
| // The ID from a user perspective maps to a Target/Process pair. |
| std::map<int, TargetRecord> id_to_target_; |
| std::map<const Target*, int> target_to_id_; |
| int next_target_id_ = 1; |
| |
| std::map<int, JobRecord> id_to_job_; |
| std::map<const Job*, int> job_to_id_; |
| int next_job_id_ = 1; |
| |
| std::map<int, Breakpoint*> id_to_breakpoint_; |
| std::map<const Breakpoint*, int> breakpoint_to_id_; |
| int next_breakpoint_id_ = 1; |
| |
| std::map<int, Filter*> id_to_filter_; |
| std::map<const Filter*, int> filter_to_id_; |
| int next_filter_id_ = 1; |
| |
| std::map<int, SymbolServer*> id_to_symbol_server_; |
| std::map<const SymbolServer*, int> symbol_server_to_id_; |
| int next_symbol_server_id_ = 1; |
| |
| int active_target_id_ = 0; |
| int active_job_id_ = 0; |
| int active_breakpoint_id_ = 0; |
| int active_filter_id_ = 0; |
| int active_symbol_server_id_ = 0; |
| |
| fxl::RefPtr<PrettyStackManager> pretty_stack_manager_; |
| }; |
| |
| } // namespace zxdb |
| |
| #endif // SRC_DEVELOPER_DEBUG_ZXDB_CONSOLE_CONSOLE_CONTEXT_H_ |