//===- FDRRecords.h - XRay Flight Data Recorder Mode Records --------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Define types and operations on these types that represent the different kinds
// of records we encounter in XRay flight data recorder mode traces.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_XRAY_FDRRECORDS_H_
#define LLVM_LIB_XRAY_FDRRECORDS_H_

#include <cstdint>
#include <string>

#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/Error.h"
#include "llvm/XRay/XRayRecord.h"

namespace llvm {
namespace xray {

class RecordVisitor;
class RecordInitializer;

class Record {
public:
  enum class RecordKind {
    RK_Metadata,
    RK_Metadata_BufferExtents,
    RK_Metadata_WallClockTime,
    RK_Metadata_NewCPUId,
    RK_Metadata_TSCWrap,
    RK_Metadata_CustomEvent,
    RK_Metadata_CustomEventV5,
    RK_Metadata_CallArg,
    RK_Metadata_PIDEntry,
    RK_Metadata_NewBuffer,
    RK_Metadata_EndOfBuffer,
    RK_Metadata_TypedEvent,
    RK_Metadata_LastMetadata,
    RK_Function,
  };

  static StringRef kindToString(RecordKind K);

private:
  const RecordKind T;

public:
  Record(const Record &) = delete;
  Record(Record &&) = delete;
  Record &operator=(const Record &) = delete;
  Record &operator=(Record &&) = delete;
  explicit Record(RecordKind T) : T(T) {}

  RecordKind getRecordType() const { return T; }

  // Each Record should be able to apply an abstract visitor, and choose the
  // appropriate function in the visitor to invoke, given its own type.
  virtual Error apply(RecordVisitor &V) = 0;

  virtual ~Record() = default;
};

class MetadataRecord : public Record {
public:
  enum class MetadataType : unsigned {
    Unknown,
    BufferExtents,
    WallClockTime,
    NewCPUId,
    TSCWrap,
    CustomEvent,
    CallArg,
    PIDEntry,
    NewBuffer,
    EndOfBuffer,
    TypedEvent,
  };

protected:
  static constexpr int kMetadataBodySize = 15;
  friend class RecordInitializer;

private:
  const MetadataType MT;

public:
  explicit MetadataRecord(RecordKind T, MetadataType M) : Record(T), MT(M) {}

  static bool classof(const Record *R) {
    return R->getRecordType() >= RecordKind::RK_Metadata &&
           R->getRecordType() <= RecordKind::RK_Metadata_LastMetadata;
  }

  MetadataType metadataType() const { return MT; }

  virtual ~MetadataRecord() = default;
};

// What follows are specific Metadata record types which encapsulate the
// information associated with specific metadata record types in an FDR mode
// log.
class BufferExtents : public MetadataRecord {
  uint64_t Size = 0;
  friend class RecordInitializer;

public:
  BufferExtents()
      : MetadataRecord(RecordKind::RK_Metadata_BufferExtents,
                       MetadataType::BufferExtents) {}

  explicit BufferExtents(uint64_t S)
      : MetadataRecord(RecordKind::RK_Metadata_BufferExtents,
                       MetadataType::BufferExtents),
        Size(S) {}

  uint64_t size() const { return Size; }

  Error apply(RecordVisitor &V) override;

  static bool classof(const Record *R) {
    return R->getRecordType() == RecordKind::RK_Metadata_BufferExtents;
  }
};

class WallclockRecord : public MetadataRecord {
  uint64_t Seconds = 0;
  uint32_t Nanos = 0;
  friend class RecordInitializer;

public:
  WallclockRecord()
      : MetadataRecord(RecordKind::RK_Metadata_WallClockTime,
                       MetadataType::WallClockTime) {}

  explicit WallclockRecord(uint64_t S, uint32_t N)
      : MetadataRecord(RecordKind::RK_Metadata_WallClockTime,
                       MetadataType::WallClockTime),
        Seconds(S), Nanos(N) {}

  uint64_t seconds() const { return Seconds; }
  uint32_t nanos() const { return Nanos; }

  Error apply(RecordVisitor &V) override;

  static bool classof(const Record *R) {
    return R->getRecordType() == RecordKind::RK_Metadata_WallClockTime;
  }
};

class NewCPUIDRecord : public MetadataRecord {
  uint16_t CPUId = 0;
  uint64_t TSC = 0;
  friend class RecordInitializer;

public:
  NewCPUIDRecord()
      : MetadataRecord(RecordKind::RK_Metadata_NewCPUId,
                       MetadataType::NewCPUId) {}

  NewCPUIDRecord(uint16_t C, uint64_t T)
      : MetadataRecord(RecordKind::RK_Metadata_NewCPUId,
                       MetadataType::NewCPUId),
        CPUId(C), TSC(T) {}

  uint16_t cpuid() const { return CPUId; }

  uint64_t tsc() const { return TSC; }

  Error apply(RecordVisitor &V) override;

  static bool classof(const Record *R) {
    return R->getRecordType() == RecordKind::RK_Metadata_NewCPUId;
  }
};

class TSCWrapRecord : public MetadataRecord {
  uint64_t BaseTSC = 0;
  friend class RecordInitializer;

public:
  TSCWrapRecord()
      : MetadataRecord(RecordKind::RK_Metadata_TSCWrap, MetadataType::TSCWrap) {
  }

  explicit TSCWrapRecord(uint64_t B)
      : MetadataRecord(RecordKind::RK_Metadata_TSCWrap, MetadataType::TSCWrap),
        BaseTSC(B) {}

  uint64_t tsc() const { return BaseTSC; }

  Error apply(RecordVisitor &V) override;

  static bool classof(const Record *R) {
    return R->getRecordType() == RecordKind::RK_Metadata_TSCWrap;
  }
};

class CustomEventRecord : public MetadataRecord {
  int32_t Size = 0;
  uint64_t TSC = 0;
  uint16_t CPU = 0;
  std::string Data{};
  friend class RecordInitializer;

public:
  CustomEventRecord()
      : MetadataRecord(RecordKind::RK_Metadata_CustomEvent,
                       MetadataType::CustomEvent) {}

  explicit CustomEventRecord(uint64_t S, uint64_t T, uint16_t C, std::string D)
      : MetadataRecord(RecordKind::RK_Metadata_CustomEvent,
                       MetadataType::CustomEvent),
        Size(S), TSC(T), CPU(C), Data(std::move(D)) {}

  int32_t size() const { return Size; }
  uint64_t tsc() const { return TSC; }
  uint16_t cpu() const { return CPU; }
  StringRef data() const { return Data; }

  Error apply(RecordVisitor &V) override;

  static bool classof(const Record *R) {
    return R->getRecordType() == RecordKind::RK_Metadata_CustomEvent;
  }
};

class CustomEventRecordV5 : public MetadataRecord {
  int32_t Size = 0;
  int32_t Delta = 0;
  std::string Data{};
  friend class RecordInitializer;

public:
  CustomEventRecordV5()
      : MetadataRecord(RecordKind::RK_Metadata_CustomEventV5,
                       MetadataType::CustomEvent) {}

  explicit CustomEventRecordV5(int32_t S, int32_t D, std::string P)
      : MetadataRecord(RecordKind::RK_Metadata_CustomEventV5,
                       MetadataType::CustomEvent),
        Size(S), Delta(D), Data(std::move(P)) {}

  int32_t size() const { return Size; }
  int32_t delta() const { return Delta; }
  StringRef data() const { return Data; }

  Error apply(RecordVisitor &V) override;

  static bool classof(const Record *R) {
    return R->getRecordType() == RecordKind::RK_Metadata_CustomEventV5;
  }
};

class TypedEventRecord : public MetadataRecord {
  int32_t Size = 0;
  int32_t Delta = 0;
  uint16_t EventType = 0;
  std::string Data{};
  friend class RecordInitializer;

public:
  TypedEventRecord()
      : MetadataRecord(RecordKind::RK_Metadata_TypedEvent,
                       MetadataType::TypedEvent) {}

  explicit TypedEventRecord(int32_t S, int32_t D, uint16_t E, std::string P)
      : MetadataRecord(RecordKind::RK_Metadata_TypedEvent,
                       MetadataType::TypedEvent),
        Size(S), Delta(D), Data(std::move(P)) {}

  int32_t size() const { return Size; }
  int32_t delta() const { return Delta; }
  uint16_t eventType() const { return EventType; }
  StringRef data() const { return Data; }

  Error apply(RecordVisitor &V) override;

  static bool classof(const Record *R) {
    return R->getRecordType() == RecordKind::RK_Metadata_TypedEvent;
  }
};

class CallArgRecord : public MetadataRecord {
  uint64_t Arg = 0;
  friend class RecordInitializer;

public:
  CallArgRecord()
      : MetadataRecord(RecordKind::RK_Metadata_CallArg, MetadataType::CallArg) {
  }

  explicit CallArgRecord(uint64_t A)
      : MetadataRecord(RecordKind::RK_Metadata_CallArg, MetadataType::CallArg),
        Arg(A) {}

  uint64_t arg() const { return Arg; }

  Error apply(RecordVisitor &V) override;

  static bool classof(const Record *R) {
    return R->getRecordType() == RecordKind::RK_Metadata_CallArg;
  }
};

class PIDRecord : public MetadataRecord {
  int32_t PID = 0;
  friend class RecordInitializer;

public:
  PIDRecord()
      : MetadataRecord(RecordKind::RK_Metadata_PIDEntry,
                       MetadataType::PIDEntry) {}

  explicit PIDRecord(int32_t P)
      : MetadataRecord(RecordKind::RK_Metadata_PIDEntry,
                       MetadataType::PIDEntry),
        PID(P) {}

  int32_t pid() const { return PID; }

  Error apply(RecordVisitor &V) override;

  static bool classof(const Record *R) {
    return R->getRecordType() == RecordKind::RK_Metadata_PIDEntry;
  }
};

class NewBufferRecord : public MetadataRecord {
  int32_t TID = 0;
  friend class RecordInitializer;

public:
  NewBufferRecord()
      : MetadataRecord(RecordKind::RK_Metadata_NewBuffer,
                       MetadataType::NewBuffer) {}

  explicit NewBufferRecord(int32_t T)
      : MetadataRecord(RecordKind::RK_Metadata_NewBuffer,
                       MetadataType::NewBuffer),
        TID(T) {}

  int32_t tid() const { return TID; }

  Error apply(RecordVisitor &V) override;

  static bool classof(const Record *R) {
    return R->getRecordType() == RecordKind::RK_Metadata_NewBuffer;
  }
};

class EndBufferRecord : public MetadataRecord {
public:
  EndBufferRecord()
      : MetadataRecord(RecordKind::RK_Metadata_EndOfBuffer,
                       MetadataType::EndOfBuffer) {}

  Error apply(RecordVisitor &V) override;

  static bool classof(const Record *R) {
    return R->getRecordType() == RecordKind::RK_Metadata_EndOfBuffer;
  }
};

class FunctionRecord : public Record {
  RecordTypes Kind;
  int32_t FuncId = 0;
  uint32_t Delta = 0;
  friend class RecordInitializer;

  static constexpr unsigned kFunctionRecordSize = 8;

public:
  FunctionRecord() : Record(RecordKind::RK_Function) {}

  explicit FunctionRecord(RecordTypes K, int32_t F, uint32_t D)
      : Record(RecordKind::RK_Function), Kind(K), FuncId(F), Delta(D) {}

  // A function record is a concrete record type which has a number of common
  // properties.
  RecordTypes recordType() const { return Kind; }
  int32_t functionId() const { return FuncId; }
  uint32_t delta() const { return Delta; }

  Error apply(RecordVisitor &V) override;

  static bool classof(const Record *R) {
    return R->getRecordType() == RecordKind::RK_Function;
  }
};

class RecordVisitor {
public:
  virtual ~RecordVisitor() = default;

  // Support all specific kinds of records:
  virtual Error visit(BufferExtents &) = 0;
  virtual Error visit(WallclockRecord &) = 0;
  virtual Error visit(NewCPUIDRecord &) = 0;
  virtual Error visit(TSCWrapRecord &) = 0;
  virtual Error visit(CustomEventRecord &) = 0;
  virtual Error visit(CallArgRecord &) = 0;
  virtual Error visit(PIDRecord &) = 0;
  virtual Error visit(NewBufferRecord &) = 0;
  virtual Error visit(EndBufferRecord &) = 0;
  virtual Error visit(FunctionRecord &) = 0;
  virtual Error visit(CustomEventRecordV5 &) = 0;
  virtual Error visit(TypedEventRecord &) = 0;
};

class RecordInitializer : public RecordVisitor {
  DataExtractor &E;
  uint64_t &OffsetPtr;
  uint16_t Version;

public:
  static constexpr uint16_t DefaultVersion = 5u;

  explicit RecordInitializer(DataExtractor &DE, uint64_t &OP, uint16_t V)
      : RecordVisitor(), E(DE), OffsetPtr(OP), Version(V) {}

  explicit RecordInitializer(DataExtractor &DE, uint64_t &OP)
      : RecordInitializer(DE, OP, DefaultVersion) {}

  Error visit(BufferExtents &) override;
  Error visit(WallclockRecord &) override;
  Error visit(NewCPUIDRecord &) override;
  Error visit(TSCWrapRecord &) override;
  Error visit(CustomEventRecord &) override;
  Error visit(CallArgRecord &) override;
  Error visit(PIDRecord &) override;
  Error visit(NewBufferRecord &) override;
  Error visit(EndBufferRecord &) override;
  Error visit(FunctionRecord &) override;
  Error visit(CustomEventRecordV5 &) override;
  Error visit(TypedEventRecord &) override;
};

} // namespace xray
} // namespace llvm

#endif // LLVM_LIB_XRAY_FDRRECORDS_H_
