//===-- PTDecoder.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 PTDecoder_h_
#define PTDecoder_h_

// C/C++ Includes
#include <vector>

#include "lldb/API/SBDebugger.h"
#include "lldb/API/SBError.h"
#include "lldb/API/SBProcess.h"
#include "lldb/API/SBStructuredData.h"
#include "lldb/API/SBTraceOptions.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-types.h"

namespace ptdecoder_private {
class Instruction;
class InstructionList;
class TraceOptions;
class Decoder;
} // namespace ptdecoder_private

namespace ptdecoder {

/// \class PTInstruction
/// Represents an assembly instruction containing raw
///     instruction bytes, instruction address along with information
///     regarding execution flow context and Intel(R) Processor Trace
///     context.
class PTInstruction {
public:
  PTInstruction() = default;

  PTInstruction(const std::shared_ptr<ptdecoder_private::Instruction> &ptr);

  ~PTInstruction();

  // Get instruction address in inferior's memory image
  uint64_t GetInsnAddress() const;

  /// Get raw bytes of the instruction in the buffer.
  ///
  /// \param[out] buf
  ///     The buffer where the raw bytes will be written. This buffer should be
  ///     allocated by the caller of this API. Providing an unallocated buffer
  ///     is an error. In case of errors, the content of the buffer is not
  ///     valid.
  ///
  /// \param[in] size
  ///     Number of raw bytes to be written to @buf. Atleast @size bytes of
  ///     memory should be allocated to @buf otherwise the behaviour of the API
  ///     is undefined. Providing 0 for this argument is an error.
  ///
  /// \return
  ///     Number of bytes of the instruction actually written to @buf if API
  ///     succeeds. In case of errors, total number of raw bytes of the
  ///     instruction is returned.
  size_t GetRawBytes(void *buf, size_t size) const;

  // Get error string if it represents an invalid instruction. For a valid
  // instruction, an empty string is returned
  std::string GetError() const;

  // Instruction was executed speculatively or not
  bool GetSpeculative() const;

private:
  std::shared_ptr<ptdecoder_private::Instruction> m_opaque_sp;
};

/// \class PTInstructionList
/// Represents a list of assembly instructions. Each instruction is of
///     type PTInstruction.
class PTInstructionList {
public:
  // Get number of instructions in the list
  size_t GetSize() const;

  // Get instruction at index
  PTInstruction GetInstructionAtIndex(uint32_t idx);

  void Clear();

private:
  friend class PTDecoder;

  void SetSP(const std::shared_ptr<ptdecoder_private::InstructionList> &ptr);

  std::shared_ptr<ptdecoder_private::InstructionList> m_opaque_sp;
};

/// \class PTTraceOptions
/// Provides configuration options like trace type, trace buffer size,
///     meta data buffer size along with other Intel(R) Processor Trace
///     specific options.
class PTTraceOptions {
public:
  lldb::TraceType GetType() const;

  uint64_t GetTraceBufferSize() const;

  uint64_t GetMetaDataBufferSize() const;

  /// Get Intel(R) Processor Trace specific configuration options (apart from
  /// trace buffer size, meta data buffer size and TraceType) formatted as
  /// json text i.e. {"Name":Value,"Name":Value} pairs, where "Value" is a
  /// 64-bit unsigned integer in hex format. For "Name", please refer to
  /// SBProcess::StartTrace API description for setting SBTraceOptions.
  ///
  /// \return
  ///     A string formatted as json text {"Name":Value,"Name":Value}
  lldb::SBStructuredData GetTraceParams(lldb::SBError &error);

private:
  friend class PTDecoder;

  void SetSP(const std::shared_ptr<ptdecoder_private::TraceOptions> &ptr);

  std::shared_ptr<ptdecoder_private::TraceOptions> m_opaque_sp;
};

/// \class PTDecoder
/// This class makes use of Intel(R) Processor Trace hardware feature
///     (implememted inside LLDB) to gather trace data for an inferior (being
///     debugged with LLDB) to provide meaningful information out of it.
///
///     Currently the meaningful information comprises of the execution flow
///     of the inferior (in terms of assembly instructions executed). The class
///     enables user to:
///     - start the trace with configuration options for a thread/process,
///     - stop the trace for a thread/process,
///     - get the execution flow (assembly instructions) for a thread and
///     - get trace specific information for a thread
class PTDecoder {
public:
  PTDecoder(lldb::SBDebugger &sbdebugger);

  /// Start Intel(R) Processor Trace on a thread or complete process with
  /// Intel(R) Processor Trace specific configuration options
  ///
  /// \param[in] sbprocess
  ///     A valid process on which this operation will be performed. An error is
  ///     returned in case of an invalid process.
  ///
  /// \param[in] sbtraceoptions
  ///     Contains thread id information and configuration options:
  ///
  ///     For tracing a single thread, provide a valid thread id. If sbprocess
  ///     doesn't contain this thread id, error will be returned. For tracing
  ///     complete process, set it to lldb::LLDB_INVALID_THREAD_ID
  ///     Configuration options comprises of:
  ///     a) trace buffer size, meta data buffer size, TraceType and
  ///     b) All other possible Intel(R) Processor Trace specific configuration
  ///     options (hereafter collectively referred as CUSTOM_OPTIONS), formatted
  ///     as json text i.e. {"Name":Value,"Name":Value,..} inside
  ///     sbtraceoptions, where "Value" should be a 64-bit unsigned integer in
  ///     hex format. For information regarding what all configuration options
  ///     are currently supported by LLDB and detailed information about
  ///     CUSTOM_OPTIONS usage, please refer to SBProcess::StartTrace() API
  ///     description. To know about all possible configuration options of
  ///     Intel(R) Processor Trace, please refer to Intel(R) 64 and IA-32
  ///     Architectures Software Developer's Manual.
  ///
  ///     TraceType should be set to lldb::TraceType::eTraceTypeProcessorTrace,
  ///     else error is returned. To find out any other requirement to start
  ///     tracing successfully, please refer to SBProcess::StartTrace() API
  ///     description. LLDB's current implementation of Intel(R) Processor Trace
  ///     feature may round off invalid values for configuration options.
  ///     Therefore, the configuration options with which the trace was actually
  ///     started, might be different to the ones with which trace was asked to
  ///     be started by user. The actual used configuration options can be
  ///     obtained from GetProcessorTraceInfo() API.
  ///
  /// \param[out] sberror
  ///     An error with the failure reason if API fails. Else success.
  void StartProcessorTrace(lldb::SBProcess &sbprocess,
                           lldb::SBTraceOptions &sbtraceoptions,
                           lldb::SBError &sberror);

  /// Stop Intel(R) Processor Trace on a thread or complete process.
  ///
  /// \param[in] sbprocess
  ///     A valid process on which this operation will be performed. An error is
  ///     returned in case of an invalid process.
  ///
  /// \param[in] tid
  ///     Case 1: To stop tracing a single thread, provide a valid thread id. If
  ///     sbprocess doesn't contain the thread tid, error will be returned.
  ///     Case 2: To stop tracing complete process, use
  ///     lldb::LLDB_INVALID_THREAD_ID.
  ///
  /// \param[out] sberror
  ///     An error with the failure reason if API fails. Else success.
  void StopProcessorTrace(lldb::SBProcess &sbprocess, lldb::SBError &sberror,
                          lldb::tid_t tid = LLDB_INVALID_THREAD_ID);

  /// Get instruction log containing the execution flow for a thread of a
  /// process in terms of assembly instructions executed.
  ///
  /// \param[in] sbprocess
  ///     A valid process on which this operation will be performed. An error is
  ///     returned in case of an invalid process.
  ///
  /// \param[in] tid
  ///     A valid thread id of the thread for which instruction log is desired.
  ///     If sbprocess doesn't contain the thread tid, error will be returned.
  ///
  /// \param[in] count
  ///     The number of instructions requested by the user to be returned from
  ///     the complete instruction log. Complete instruction log refers to all
  ///     the assembly instructions obtained after decoding the complete raw
  ///     trace data obtained from LLDB. The length of the complete instruction
  ///     log is dependent on the trace buffer size with which processor tracing
  ///     was started for this thread.
  ///     The number of instructions actually returned are dependent on 'count'
  ///     and 'offset' parameters of this API.
  ///
  /// \param[in] offset
  ///     The offset in the complete instruction log from where 'count' number
  ///     of instructions are requested by the user. offset is counted from the
  ///     end of of this complete instruction log (which means the last executed
  ///     instruction is at offset 0 (zero)).
  ///
  /// \param[out] result_list
  ///     Depending upon 'count' and 'offset' values, list will be overwritten
  ///     with the new instructions.
  ///
  /// \param[out] sberror
  ///     An error with the failure reason if API fails. Else success.
  void GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, lldb::tid_t tid,
                                 uint32_t offset, uint32_t count,
                                 PTInstructionList &result_list,
                                 lldb::SBError &sberror);

  /// Get Intel(R) Processor Trace specific information for a thread of a
  /// process. The information contains the actual configuration options with
  /// which the trace was started for this thread.
  ///
  /// \param[in] sbprocess
  ///     A valid process on which this operation will be performed. An error is
  ///     returned in case of an invalid process.
  ///
  /// \param[in] tid
  ///     A valid thread id of the thread for which the trace specific
  ///     information is required. If sbprocess doesn't contain the thread tid,
  ///     an error will be returned.
  ///
  /// \param[out] options
  ///     Contains actual configuration options (they may be different to the
  ///     ones with which tracing was asked to be started for this thread during
  ///     StartProcessorTrace() API call).
  ///
  /// \param[out] sberror
  ///     An error with the failure reason if API fails. Else success.
  void GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid,
                             PTTraceOptions &options, lldb::SBError &sberror);

private:
  std::shared_ptr<ptdecoder_private::Decoder> m_opaque_sp;
};

} // namespace ptdecoder
#endif // PTDecoder_h_
