//===-- ProcessMessage.h ----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef liblldb_ProcessMessage_H_
#define liblldb_ProcessMessage_H_

#include "CrashReason.h"

#include <cassert>
#include <string>

#include "lldb/lldb-defines.h"
#include "lldb/lldb-types.h"

class ProcessMessage {
public:
  /// The type of signal this message can correspond to.
  enum Kind {
    eInvalidMessage,
    eAttachMessage,
    eExitMessage,
    eLimboMessage,
    eSignalMessage,
    eSignalDeliveredMessage,
    eTraceMessage,
    eBreakpointMessage,
    eWatchpointMessage,
    eCrashMessage,
    eNewThreadMessage,
    eExecMessage
  };

  ProcessMessage()
      : m_tid(LLDB_INVALID_PROCESS_ID), m_kind(eInvalidMessage),
        m_crash_reason(CrashReason::eInvalidCrashReason), m_status(0),
        m_addr(0) {}

  Kind GetKind() const { return m_kind; }

  lldb::tid_t GetTID() const { return m_tid; }

  /// Indicates that the process @p pid has successfully attached.
  static ProcessMessage Attach(lldb::pid_t pid) {
    return ProcessMessage(pid, eAttachMessage);
  }

  /// Indicates that the thread @p tid is about to exit with status @p status.
  static ProcessMessage Limbo(lldb::tid_t tid, int status) {
    return ProcessMessage(tid, eLimboMessage, status);
  }

  /// Indicates that the thread @p tid had the signal @p signum delivered.
  static ProcessMessage Signal(lldb::tid_t tid, int signum) {
    return ProcessMessage(tid, eSignalMessage, signum);
  }

  /// Indicates that a signal @p signum generated by the debugging process was
  /// delivered to the thread @p tid.
  static ProcessMessage SignalDelivered(lldb::tid_t tid, int signum) {
    return ProcessMessage(tid, eSignalDeliveredMessage, signum);
  }

  /// Indicates that the thread @p tid encountered a trace point.
  static ProcessMessage Trace(lldb::tid_t tid) {
    return ProcessMessage(tid, eTraceMessage);
  }

  /// Indicates that the thread @p tid encountered a break point.
  static ProcessMessage Break(lldb::tid_t tid) {
    return ProcessMessage(tid, eBreakpointMessage);
  }

  static ProcessMessage Watch(lldb::tid_t tid, lldb::addr_t wp_addr) {
    return ProcessMessage(tid, eWatchpointMessage, 0, wp_addr);
  }

  /// Indicates that the thread @p tid crashed.
  static ProcessMessage Crash(lldb::pid_t pid, CrashReason reason, int signo,
                              lldb::addr_t fault_addr) {
    ProcessMessage message(pid, eCrashMessage, signo, fault_addr);
    message.m_crash_reason = reason;
    return message;
  }

  /// Indicates that the thread @p child_tid was spawned.
  static ProcessMessage NewThread(lldb::tid_t parent_tid,
                                  lldb::tid_t child_tid) {
    return ProcessMessage(parent_tid, eNewThreadMessage, child_tid);
  }

  /// Indicates that the thread @p tid is about to exit with status @p status.
  static ProcessMessage Exit(lldb::tid_t tid, int status) {
    return ProcessMessage(tid, eExitMessage, status);
  }

  /// Indicates that the thread @p pid has exec'd.
  static ProcessMessage Exec(lldb::tid_t tid) {
    return ProcessMessage(tid, eExecMessage);
  }

  int GetExitStatus() const {
    assert(GetKind() == eExitMessage || GetKind() == eLimboMessage);
    return m_status;
  }

  int GetSignal() const {
    assert(GetKind() == eSignalMessage || GetKind() == eCrashMessage ||
           GetKind() == eSignalDeliveredMessage);
    return m_status;
  }

  int GetStopStatus() const {
    assert(GetKind() == eSignalMessage);
    return m_status;
  }

  CrashReason GetCrashReason() const {
    assert(GetKind() == eCrashMessage);
    return m_crash_reason;
  }

  lldb::addr_t GetFaultAddress() const {
    assert(GetKind() == eCrashMessage);
    return m_addr;
  }

  lldb::addr_t GetHWAddress() const {
    assert(GetKind() == eWatchpointMessage || GetKind() == eTraceMessage);
    return m_addr;
  }

  lldb::tid_t GetChildTID() const {
    assert(GetKind() == eNewThreadMessage);
    return m_child_tid;
  }

  const char *PrintCrashReason() const;

  const char *PrintKind() const;

  static const char *PrintKind(Kind);

private:
  ProcessMessage(lldb::tid_t tid, Kind kind, int status = 0,
                 lldb::addr_t addr = 0)
      : m_tid(tid), m_kind(kind),
        m_crash_reason(CrashReason::eInvalidCrashReason), m_status(status),
        m_addr(addr), m_child_tid(0) {}

  ProcessMessage(lldb::tid_t tid, Kind kind, lldb::tid_t child_tid)
      : m_tid(tid), m_kind(kind),
        m_crash_reason(CrashReason::eInvalidCrashReason), m_status(0),
        m_addr(0), m_child_tid(child_tid) {}

  lldb::tid_t m_tid;
  Kind m_kind : 8;
  CrashReason m_crash_reason;
  int m_status;
  lldb::addr_t m_addr;
  lldb::tid_t m_child_tid;
};

#endif // #ifndef liblldb_ProcessMessage_H_
