text: // 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;
    void OnSymbolIndexingInformation(const std::string& msg) override;
  
    // TargetObserver implementation:
    void DidCreateTarget(Target* target) override;
    void WillDestroyTarget(Target* target) override;
  
    // ProcessObserver implementation:
    void DidCreateProcess(Process* process, bool autoattached_to_new_process) override;
    void WillDestroyProcess(Process* process, DestroyReason reason, int exit_code) 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_
  