//===-- NativeProcessProtocol.h ---------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef liblldb_NativeProcessProtocol_h_
#define liblldb_NativeProcessProtocol_h_

#include <mutex>
#include <vector>

#include "lldb/Core/Error.h"
#include "lldb/Host/MainLoop.h"
#include "lldb/lldb-private-forward.h"
#include "lldb/lldb-types.h"
#include "llvm/ADT/StringRef.h"

#include "NativeBreakpointList.h"
#include "NativeWatchpointList.h"

namespace lldb_private {
class MemoryRegionInfo;
class ResumeActionList;

//------------------------------------------------------------------
// NativeProcessProtocol
//------------------------------------------------------------------
class NativeProcessProtocol
    : public std::enable_shared_from_this<NativeProcessProtocol> {
  friend class SoftwareBreakpoint;

public:
  virtual ~NativeProcessProtocol() {}

  virtual Error Resume(const ResumeActionList &resume_actions) = 0;

  virtual Error Halt() = 0;

  virtual Error Detach() = 0;

  //------------------------------------------------------------------
  /// Sends a process a UNIX signal \a signal.
  ///
  /// @return
  ///     Returns an error object.
  //------------------------------------------------------------------
  virtual Error Signal(int signo) = 0;

  //------------------------------------------------------------------
  /// Tells a process to interrupt all operations as if by a Ctrl-C.
  ///
  /// The default implementation will send a local host's equivalent of
  /// a SIGSTOP to the process via the NativeProcessProtocol::Signal()
  /// operation.
  ///
  /// @return
  ///     Returns an error object.
  //------------------------------------------------------------------
  virtual Error Interrupt();

  virtual Error Kill() = 0;

  //----------------------------------------------------------------------
  // Memory and memory region functions
  //----------------------------------------------------------------------

  virtual Error GetMemoryRegionInfo(lldb::addr_t load_addr,
                                    MemoryRegionInfo &range_info);

  virtual Error ReadMemory(lldb::addr_t addr, void *buf, size_t size,
                           size_t &bytes_read) = 0;

  virtual Error ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size,
                                      size_t &bytes_read) = 0;

  virtual Error WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
                            size_t &bytes_written) = 0;

  virtual Error AllocateMemory(size_t size, uint32_t permissions,
                               lldb::addr_t &addr) = 0;

  virtual Error DeallocateMemory(lldb::addr_t addr) = 0;

  virtual lldb::addr_t GetSharedLibraryInfoAddress() = 0;

  virtual bool IsAlive() const;

  virtual size_t UpdateThreads() = 0;

  virtual bool GetArchitecture(ArchSpec &arch) const = 0;

  //----------------------------------------------------------------------
  // Breakpoint functions
  //----------------------------------------------------------------------
  virtual Error SetBreakpoint(lldb::addr_t addr, uint32_t size,
                              bool hardware) = 0;

  virtual Error RemoveBreakpoint(lldb::addr_t addr);

  virtual Error EnableBreakpoint(lldb::addr_t addr);

  virtual Error DisableBreakpoint(lldb::addr_t addr);

  //----------------------------------------------------------------------
  // Watchpoint functions
  //----------------------------------------------------------------------
  virtual const NativeWatchpointList::WatchpointMap &GetWatchpointMap() const;

  virtual uint32_t GetMaxWatchpoints() const;

  virtual Error SetWatchpoint(lldb::addr_t addr, size_t size,
                              uint32_t watch_flags, bool hardware);

  virtual Error RemoveWatchpoint(lldb::addr_t addr);

  //----------------------------------------------------------------------
  // Accessors
  //----------------------------------------------------------------------
  lldb::pid_t GetID() const { return m_pid; }

  lldb::StateType GetState() const;

  bool IsRunning() const {
    return m_state == lldb::eStateRunning || IsStepping();
  }

  bool IsStepping() const { return m_state == lldb::eStateStepping; }

  bool CanResume() const { return m_state == lldb::eStateStopped; }

  bool GetByteOrder(lldb::ByteOrder &byte_order) const;

  //----------------------------------------------------------------------
  // Exit Status
  //----------------------------------------------------------------------
  virtual bool GetExitStatus(lldb_private::ExitType *exit_type, int *status,
                             std::string &exit_description);

  virtual bool SetExitStatus(lldb_private::ExitType exit_type, int status,
                             const char *exit_description,
                             bool bNotifyStateChange);

  //----------------------------------------------------------------------
  // Access to threads
  //----------------------------------------------------------------------
  NativeThreadProtocolSP GetThreadAtIndex(uint32_t idx);

  NativeThreadProtocolSP GetThreadByID(lldb::tid_t tid);

  void SetCurrentThreadID(lldb::tid_t tid) { m_current_thread_id = tid; }

  lldb::tid_t GetCurrentThreadID() { return m_current_thread_id; }

  NativeThreadProtocolSP GetCurrentThread() {
    return GetThreadByID(m_current_thread_id);
  }

  //----------------------------------------------------------------------
  // Access to inferior stdio
  //----------------------------------------------------------------------
  virtual int GetTerminalFileDescriptor() { return m_terminal_fd; }

  //----------------------------------------------------------------------
  // Stop id interface
  //----------------------------------------------------------------------

  uint32_t GetStopID() const;

  // ---------------------------------------------------------------------
  // Callbacks for low-level process state changes
  // ---------------------------------------------------------------------
  class NativeDelegate {
  public:
    virtual ~NativeDelegate() {}

    virtual void InitializeDelegate(NativeProcessProtocol *process) = 0;

    virtual void ProcessStateChanged(NativeProcessProtocol *process,
                                     lldb::StateType state) = 0;

    virtual void DidExec(NativeProcessProtocol *process) = 0;
  };

  //------------------------------------------------------------------
  /// Register a native delegate.
  ///
  /// Clients can register nofication callbacks by passing in a
  /// NativeDelegate impl and passing it into this function.
  ///
  /// Note: it is required that the lifetime of the
  /// native_delegate outlive the NativeProcessProtocol.
  ///
  /// @param[in] native_delegate
  ///     A NativeDelegate impl to be called when certain events
  ///     happen within the NativeProcessProtocol or related threads.
  ///
  /// @return
  ///     true if the delegate was registered successfully;
  ///     false if the delegate was already registered.
  ///
  /// @see NativeProcessProtocol::NativeDelegate.
  //------------------------------------------------------------------
  bool RegisterNativeDelegate(NativeDelegate &native_delegate);

  //------------------------------------------------------------------
  /// Unregister a native delegate previously registered.
  ///
  /// @param[in] native_delegate
  ///     A NativeDelegate impl previously registered with this process.
  ///
  /// @return Returns \b true if the NativeDelegate was
  /// successfully removed from the process, \b false otherwise.
  ///
  /// @see NativeProcessProtocol::NativeDelegate
  //------------------------------------------------------------------
  bool UnregisterNativeDelegate(NativeDelegate &native_delegate);

  virtual Error GetLoadedModuleFileSpec(const char *module_path,
                                        FileSpec &file_spec) = 0;

  virtual Error GetFileLoadAddress(const llvm::StringRef &file_name,
                                   lldb::addr_t &load_addr) = 0;

  //------------------------------------------------------------------
  /// Launch a process for debugging. This method will create an concrete
  /// instance of NativeProcessProtocol, based on the host platform.
  /// (e.g. NativeProcessLinux on linux, etc.)
  ///
  /// @param[in] launch_info
  ///     Information required to launch the process.
  ///
  /// @param[in] native_delegate
  ///     The delegate that will receive messages regarding the
  ///     inferior.  Must outlive the NativeProcessProtocol
  ///     instance.
  ///
  /// @param[in] mainloop
  ///     The mainloop instance with which the process can register
  ///     callbacks. Must outlive the NativeProcessProtocol
  ///     instance.
  ///
  /// @param[out] process_sp
  ///     On successful return from the method, this parameter
  ///     contains the shared pointer to the
  ///     NativeProcessProtocol that can be used to manipulate
  ///     the native process.
  ///
  /// @return
  ///     An error object indicating if the operation succeeded,
  ///     and if not, what error occurred.
  //------------------------------------------------------------------
  static Error Launch(ProcessLaunchInfo &launch_info,
                      NativeDelegate &native_delegate, MainLoop &mainloop,
                      NativeProcessProtocolSP &process_sp);

  //------------------------------------------------------------------
  /// Attach to an existing process. This method will create an concrete
  /// instance of NativeProcessProtocol, based on the host platform.
  /// (e.g. NativeProcessLinux on linux, etc.)
  ///
  /// @param[in] pid
  ///     pid of the process locatable
  ///
  /// @param[in] native_delegate
  ///     The delegate that will receive messages regarding the
  ///     inferior.  Must outlive the NativeProcessProtocol
  ///     instance.
  ///
  /// @param[in] mainloop
  ///     The mainloop instance with which the process can register
  ///     callbacks. Must outlive the NativeProcessProtocol
  ///     instance.
  ///
  /// @param[out] process_sp
  ///     On successful return from the method, this parameter
  ///     contains the shared pointer to the
  ///     NativeProcessProtocol that can be used to manipulate
  ///     the native process.
  ///
  /// @return
  ///     An error object indicating if the operation succeeded,
  ///     and if not, what error occurred.
  //------------------------------------------------------------------
  static Error Attach(lldb::pid_t pid, NativeDelegate &native_delegate,
                      MainLoop &mainloop, NativeProcessProtocolSP &process_sp);

protected:
  lldb::pid_t m_pid;

  std::vector<NativeThreadProtocolSP> m_threads;
  lldb::tid_t m_current_thread_id;
  mutable std::recursive_mutex m_threads_mutex;

  lldb::StateType m_state;
  mutable std::recursive_mutex m_state_mutex;

  lldb_private::ExitType m_exit_type;
  int m_exit_status;
  std::string m_exit_description;
  std::recursive_mutex m_delegates_mutex;
  std::vector<NativeDelegate *> m_delegates;
  NativeBreakpointList m_breakpoint_list;
  NativeWatchpointList m_watchpoint_list;
  int m_terminal_fd;
  uint32_t m_stop_id;

  // lldb_private::Host calls should be used to launch a process for debugging,
  // and
  // then the process should be attached to. When attaching to a process
  // lldb_private::Host calls should be used to locate the process to attach to,
  // and then this function should be called.
  NativeProcessProtocol(lldb::pid_t pid);

  // -----------------------------------------------------------
  // Internal interface for state handling
  // -----------------------------------------------------------
  void SetState(lldb::StateType state, bool notify_delegates = true);

  // Derived classes need not implement this.  It can be used as a
  // hook to clear internal caches that should be invalidated when
  // stop ids change.
  //
  // Note this function is called with the state mutex obtained
  // by the caller.
  virtual void DoStopIDBumped(uint32_t newBumpId);

  // -----------------------------------------------------------
  // Internal interface for software breakpoints
  // -----------------------------------------------------------
  Error SetSoftwareBreakpoint(lldb::addr_t addr, uint32_t size_hint);

  virtual Error
  GetSoftwareBreakpointTrapOpcode(size_t trap_opcode_size_hint,
                                  size_t &actual_opcode_size,
                                  const uint8_t *&trap_opcode_bytes) = 0;

  // -----------------------------------------------------------
  /// Notify the delegate that an exec occurred.
  ///
  /// Provide a mechanism for a delegate to clear out any exec-
  /// sensitive data.
  // -----------------------------------------------------------
  void NotifyDidExec();

  NativeThreadProtocolSP GetThreadByIDUnlocked(lldb::tid_t tid);

  // -----------------------------------------------------------
  // Static helper methods for derived classes.
  // -----------------------------------------------------------
  static Error ResolveProcessArchitecture(lldb::pid_t pid, ArchSpec &arch);

private:
  void SynchronouslyNotifyProcessStateChanged(lldb::StateType state);
};
}

#endif // #ifndef liblldb_NativeProcessProtocol_h_
