/* -*- Mode: C++; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */

#ifndef RR_UTIL_H_
#define RR_UTIL_H_

#include <signal.h>
#include <stdio.h>
#include <math.h>

#include <array>
#include <map>
#include <set>
#include <string>
#include <vector>

#include "ScopedFd.h"
#include "TraceFrame.h"
#include "remote_ptr.h"
#include "kernel_supplement.h"

/* This is pretty arbitrary. On Linux SIGPWR is sent to PID 1 (init) on
 * power failure, and it's unlikely rr will be recording that.
 * Note that SIGUNUSED means SIGSYS which actually *is* used (by seccomp),
 * so we can't use it. */
#define SYSCALLBUF_DEFAULT_DESCHED_SIGNAL SIGPWR

#ifndef SOL_NETLINK
#define SOL_NETLINK 270
#endif

namespace rr {

/*
 * This file is a dumping ground for functionality that needs to be shared but
 * has no other obvious place to go.
 *
 * We should minimize the amount of code here. Code that's only needed in one
 * place can move out of this file.
 */

struct Event;
class KernelMapping;
class Task;
class TraceFrame;
class RecordTask;

enum Completion { COMPLETE, INCOMPLETE };

/**
 * Returns a vector containing the raw data you can get from getauxval.
 */
std::vector<uint8_t> read_auxv(Task* t);

/**
 * Returns a vector containing the environment strings.
 */
std::vector<std::string> read_env(Task* t);

void patch_auxv_vdso(RecordTask* t);

/**
 * Create a file named |filename| and dump |buf_len| words in |buf| to
 * that file, starting with a line containing |label|.  |start_addr|
 * is the client address at which |buf| resides, if meaningful.
 * |start_addr| is used to compute the output lines of words, which
 * look like "0xValue | [0xAddr]".
 */
void dump_binary_data(const char* filename, const char* label,
                      const uint32_t* buf, size_t buf_len,
                      remote_ptr<void> start_addr);

/**
 * Format a suitable filename within the trace directory for dumping
 * information about |t| at the current global time, to a file that
 * contains |tag|.  The constructed filename is returned through
 * |filename|.  For example, a filengit logame for a task with tid 12345 at
 * time 111, for a file tagged "foo", would be something like
 * "trace_0/12345_111_foo".  The returned name is not guaranteed to be
 * unique, caveat emptor.
 */
void format_dump_filename(Task* t, FrameTime global_time, const char* tag,
                          char* filename, size_t filename_size);

/**
 * Return true if the user requested memory be dumped at this event/time.
 */
bool should_dump_memory(const Event& event, FrameTime time);
/**
 * Dump all of the memory in |t|'s address to the file
 * "[trace_dir]/[t->tid]_[global_time]_[tag]".
 */
void dump_process_memory(Task* t, FrameTime global_time, const char* tag);

/**
 * Return true if the user has requested |t|'s memory be
 * checksummed at this event/time.
 */
bool should_checksum(const Event& event, FrameTime time);
/**
 * Write a checksum of each mapped region in |t|'s address space to a
 * special log, where it can be read by |validate_process_memory()|
 * during replay.
 */
void checksum_process_memory(Task* t, FrameTime global_time);
/**
 * Validate the checksum of |t|'s address space that was written
 * during recording.
 */
void validate_process_memory(Task* t, FrameTime global_time);

/**
 * Return nonzero if the rr session is probably not interactive (that
 * is, there's probably no user watching or interacting with rr), and
 * so asking for user input or other actions is probably pointless.
 */
bool probably_not_interactive(int fd = STDERR_FILENO);

/**
 * Convert the flags passed to the clone() syscall, |flags_arg|, into
 * the format understood by Task::clone().
 */
int clone_flags_to_task_flags(int flags_arg);

/**
 * Return the argument rounded up to the nearest multiple of the
 * system |page_size()|.
 */
size_t ceil_page_size(size_t sz);
remote_ptr<void> ceil_page_size(remote_ptr<void> addr);

/**
 * Return the argument rounded down to the nearest multiple of the
 * system |page_size()|.
 */
size_t floor_page_size(size_t sz);
remote_ptr<void> floor_page_size(remote_ptr<void> addr);

/** Return the system page size. */
size_t page_size();

/** Return the default action of |sig|. */
enum signal_action { DUMP_CORE, TERMINATE, CONTINUE, STOP, IGNORE };
signal_action default_action(int sig);

SignalDeterministic is_deterministic_signal(Task* t);

/**
 * Return nonzero if a mapping of |mapping| should almost certainly be copied to
 * trace; i.e., the file contents are likely to change in the interval
 * between recording and replay.  Zero is returned /if we think we can
 * get away/ with not copying the region.  That doesn't mean it's
 * necessarily safe to skip copying!
 */
bool should_copy_mmap_region(const KernelMapping& mapping,
                             const std::string &file_name,
                             const struct stat& stat);

/**
 * Ensure that the shmem segment referred to by |fd| has exactly the
 * size |num_bytes|.
 */
void resize_shmem_segment(ScopedFd& fd, uint64_t num_bytes);

enum cpuid_requests {
  CPUID_GETVENDORSTRING,
  CPUID_GETFEATURES,
  CPUID_GETTLB,
  CPUID_GETSERIAL,
  CPUID_GETCACHEPARAMS = 0x04,
  CPUID_GETEXTENDEDFEATURES = 0x07,
  CPUID_GETEXTENDEDTOPOLOGY = 0x0B,
  CPUID_GETXSAVE = 0x0D,
  CPUID_GETRDTMONITORING = 0x0F,
  CPUID_GETRDTALLOCATION = 0x10,
  CPUID_GETSGX = 0x12,
  CPUID_GETPT = 0x14,
  CPUID_GETSOC = 0x17,
  CPUID_HYPERVISOR = 0x40000000,
  CPUID_INTELEXTENDED = 0x80000000,
  CPUID_INTELFEATURES,
  CPUID_INTELBRANDSTRING,
  CPUID_INTELBRANDSTRINGMORE,
  CPUID_INTELBRANDSTRINGEND,
};

const int OSXSAVE_FEATURE_FLAG = 1 << 27;
const int AVX_FEATURE_FLAG = 1 << 28;
const int HLE_FEATURE_FLAG = 1 << 4;
const int XSAVEC_FEATURE_FLAG = 1 << 1;

/** issue a single request to CPUID. Fits 'intel features', for instance
 *  note that even if only "eax" and "edx" are of interest, other registers
 *  will be modified by the operation, so we need to tell the compiler about it.
 *  'code' is placed in EAX. 'subrequest' is placed in ECX.
 *  *a, *c and *d receive EAX, ECX and EDX respectively.
 */
struct CPUIDData {
  uint32_t eax, ebx, ecx, edx;
};
CPUIDData cpuid(uint32_t code, uint32_t subrequest);

/**
 * Check OSXSAVE flag.
 */
bool xsave_enabled();
/**
 * Fetch current XCR0 value using XGETBV instruction.
 */
uint64_t xcr0();

/**
 * Return all CPUID values supported by this CPU.
 */
struct CPUIDRecord {
  uint32_t eax_in;
  // UINT32_MAX means ECX not relevant
  uint32_t ecx_in;
  CPUIDData out;
};
std::vector<CPUIDRecord> all_cpuid_records();

/**
 * Returns true if CPUID faulting is supported by the kernel and hardware and
 * is actually working.
 */
bool cpuid_faulting_works();

/**
 * Locate a CPUID record for the give parameters, or return nullptr if there
 * isn't one.
 */
const CPUIDRecord* find_cpuid_record(const std::vector<CPUIDRecord>& records,
                                     uint32_t eax, uint32_t ecx);

/**
 * Return true if the trace's CPUID values are "compatible enough" with our
 * CPU's CPUID values.
 */
bool cpuid_compatible(const std::vector<CPUIDRecord>& trace_records);

/**
 * Return true if the CPU stores 0 for FIP/FDP in an XSAVE when no x87 exception
 * is pending.
 */
bool cpu_has_xsave_fip_fdp_quirk();

/**
 * CPU only sets FDP when an unmasked x87 exception is generated.
 */
bool cpu_has_fdp_exception_only_quirk();

struct CloneParameters {
  remote_ptr<void> stack;
  remote_ptr<int> ptid;
  remote_ptr<void> tls;
  remote_ptr<int> ctid;
};
/**
 * Extract various clone(2) parameters out of the given Task's registers.
 */
CloneParameters extract_clone_parameters(Task* t);

/**
 * Read the ELF CLASS from the given filename. If it's unable to be read,
 * return ELFCLASSNONE. If it's not an ELF file, return NOT_ELF.
 */
const int NOT_ELF = 0x10000;
int read_elf_class(const std::string& filename);

bool trace_instructions_up_to_event(FrameTime event);

/* Helpful for broken debuggers */

void dump_task_set(const std::set<Task*>& tasks);

void dump_task_map(const std::map<pid_t, Task*>& tasks);

std::string real_path(const std::string& path);

std::string resource_path();

/**
 * Get the current time from the preferred monotonic clock in units of
 * seconds, relative to an unspecific point in the past.
 */
double monotonic_now_sec();

bool running_under_rr(bool cache = true);

std::vector<int> read_all_proc_fds(pid_t tid);

std::vector<std::string> read_proc_status_fields(pid_t tid, const char* name,
                                                 const char* name2 = nullptr,
                                                 const char* name3 = nullptr);

/**
 * Mainline Linux kernels use an invisible (to /proc/<pid>/maps) guard page
 * for stacks. grsecurity kernels don't.
 */
bool uses_invisible_guard_page();

bool copy_file(int dest_fd, int src_fd);

#if defined(__has_feature)
#if __has_feature(memory_sanitizer)
extern "C" void __msan_unpoison(void*, size_t);
inline void msan_unpoison(void* ptr, size_t n) { __msan_unpoison(ptr, n); };
#else
inline void msan_unpoison(void* ptr, size_t n) {
  (void)ptr;
  (void)n;
};
#endif
#else
inline void msan_unpoison(void* ptr, size_t n) {
  (void)ptr;
  (void)n;
};
#endif

/**
 * Determine if the given capabilities are a subset of the process' current
 * active capabilities.
 */
bool has_effective_caps(uint64_t caps);

struct XSaveFeatureLayout {
  uint32_t offset;
  uint32_t size;
};

struct XSaveLayout {
  size_t full_size;
  uint64_t supported_feature_bits;
  std::vector<XSaveFeatureLayout> feature_layouts;
};

/**
 * Determine the layout of the native XSAVE area
 */
const XSaveLayout& xsave_native_layout();

/**
 * Determine the layout of the XSAVE area from a trace
 */
XSaveLayout xsave_layout_from_trace(const std::vector<CPUIDRecord> records);

/**
 * 0 means XSAVE not detected
 */
inline size_t xsave_area_size() { return xsave_native_layout().full_size; }

inline sig_set_t signal_bit(int sig) { return sig_set_t(1) << (sig - 1); }

inline bool is_kernel_trap(int si_code) {
  /* XXX unable to find docs on which of these "should" be
   * right.  The SI_KERNEL code is seen in the int3 test, so we
   * at least need to handle that. */
  return si_code == TRAP_TRACE || si_code == TRAP_BRKPT || si_code == TRAP_HWBKPT || si_code == SI_KERNEL;
}

enum ProbePort { DONT_PROBE = 0, PROBE_PORT };

ScopedFd open_socket(const char* address, unsigned short* port,
                     ProbePort probe);

/**
 * Like `abort`, but tries to wake up test-monitor for a snapshot if possible.
 */
void notifying_abort();

/**
 * Dump the current rr stack
 */
void dump_rr_stack();

/**
 * Check for leaked mappings etc
 */
void check_for_leaks();

/**
 * Create directory `str`, creating parent directories as needed.
 * `dir_type` is printed in error messages. Fails if the resulting directory
 * is not writeable.
 */
void ensure_dir(const std::string& dir, const char* dir_type, mode_t mode);

/**
 * Returns $TMPDIR or "/tmp". We call ensure_dir to make sure the directory
 * exists and is writeable.
 */
const char* tmp_dir();

struct TempFile {
  std::string name;
  ScopedFd fd;
};

/**
 * `pattern is an mkstemp pattern minus any leading path. We'll choose the
 * temp directory ourselves. The file is not automatically deleted, the caller
 * must take care of that.
 */
TempFile create_temporary_file(const char* pattern);

/**
 * Opens a temporary file backed by RAM.
 */
ScopedFd open_memory_file(const std::string &name);

void good_random(void* out, size_t out_len);

std::vector<std::string> current_env();

int get_num_cpus();

enum class TrappedInstruction {
  NONE = 0,
  RDTSC = 1,
  RDTSCP = 2,
  CPUID = 3,
  INT3 = 4,
  PUSHF = 5,
  PUSHF16 = 6,
};

/* If |t->ip()| points at a decoded instruction, return the instruction */
TrappedInstruction trapped_instruction_at(Task* t, remote_code_ptr ip);

/* Return the length of the TrappedInstruction */
size_t trapped_instruction_len(TrappedInstruction insn);

/**
 * Certain instructions generate deterministic signals but also advance pc.
 * Look *backwards* and see if this was one of them.
 */
bool is_advanced_pc_and_signaled_instruction(Task* t, remote_code_ptr ip);

/**
 * BIND_CPU means binding to a randomly chosen CPU.
 * UNBOUND_CPU means not binding to a particular CPU.
 * A non-negative value means binding to the specific CPU number.
 */
enum BindCPU { BIND_CPU = -2, UNBOUND_CPU = -1 };

/* Convert a BindCPU to a specific CPU number. If possible, the cpu_lock_fd_out
   will be set to an fd that holds an advisory fcntl lock for the chosen CPU
   for coordination with other rr processes */
int choose_cpu(BindCPU bind_cpu, ScopedFd& cpu_lock_fd_out);

/* Updates an IEEE 802.3 CRC-32 least significant bit first from each byte in
 * |buf|.  Pre- and post-conditioning is not performed in this function and so
 * should be performed by the caller, as required. */
uint32_t crc32(uint32_t crc, unsigned char* buf, size_t len);

/* Like write(2) but any error or "device full" is treated as fatal. We also
 * ensure that all bytes are written by looping on short writes. */
void write_all(int fd, const void* buf, size_t size);

/* Like pwrite64(2) but we try to write all bytes by looping on short writes. */
ssize_t pwrite_all_fallible(int fd, const void* buf, size_t size, off64_t offset);

/* Returns true if |path| is an accessible directory. Returns false if there
 * was an error.
 */
bool is_directory(const char* path);

/**
 * Read bytes from `fd` into `buf` from `offset` until the read returns an
 * error or 0 or the buffer is full. Returns total bytes read or -1 for error.
 */
ssize_t read_to_end(const ScopedFd& fd, size_t offset, void* buf, size_t size);

/**
 * Raise resource limits, in particular the open file descriptor count.
 */
void raise_resource_limits();

/**
 * Restore the initial resource limits for this process.
 */
void restore_initial_resource_limits();

/**
 * Return the word size for the architecture.
 */
size_t word_size(SupportedArch arch);

/**
 * Print JSON-escaped version of the string, including double-quotes.
 */
std::string json_escape(const std::string& str, size_t pos = 0);

void sleep_time(double t);

/**
 * Normalize a file name by lexically resolving `.`,`..`,`//`
 */
void normalize_file_name(std::string& s);

enum NestedBehavior {
  NESTED_ERROR,
  NESTED_IGNORE,
  NESTED_DETACH
};

std::string find_exec_stub(SupportedArch arch);

static inline struct timeval to_timeval(double t) {
  struct timeval v;
  v.tv_sec = (time_t)floor(t);
  v.tv_usec = (int)floor((t - v.tv_sec) * 1000000);
  return v;
}

/* Slow but simple pop-count implementation. */
int pop_count(uint64_t v);

} // namespace rr

#endif /* RR_UTIL_H_ */
