ca96317f创建于 2022年4月4日历史提交
#include <time.h>
#include <unistd.h>

#include <sys/mman.h>
#include <sys/syscall.h>

#include <asm/perf_regs.h>
#include <linux/perf_event.h>

#include <caml/mlvalues.h>
#include <caml/unixsupport.h>

#include "perf_utils.h"

static uint64_t rdtsc(void) {
  uint32_t hi, lo;
  __asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
  return ((uint64_t)lo) | (((uint64_t)hi) << 32);
}

CAMLprim value magic_clock_gettime_perf_ns(void) {
  /*
   * It should be stated that despite any appearances to the contrary, I have no
   * idea what I'm doing here.
   *
   * We need to get the "current time in [perf] units" to line up events with
   * absolute time. Here, we create a fake software event with bogus
   * information, just so we can get a reference to the [perf_event_mmap_page]
   * containing the time_{zero,shift,mult} fields we need to scale [rdtsc] by.
   *
   * I *think* creating a [PERF_TYPE_SOFTWARE] event handle should have no
   * side-effects, but I'm not 100% sure on that.
   */
  struct perf_event_attr attr = {0};
  attr.size = sizeof(attr);
  attr.type = PERF_TYPE_SOFTWARE;

  int fd = sys_perf_event_open(&attr, getpid(), -1, -1, PERF_FLAG_FD_CLOEXEC);
  if (fd < 0) {
    goto error_perf_event_open;
  }

  size_t mmap_size =
      sysconf(_SC_PAGESIZE) * (1 + 1); // one metadata page plus one page buffer
  volatile struct perf_event_mmap_page *perf_mmap =
      mmap(NULL, mmap_size, PROT_READ, MAP_SHARED, fd, 0);
  if (mmap == MAP_FAILED) {
    goto error_mmap;
  }

  uint64_t timestamp = perf_time_of_tsc(perf_mmap, rdtsc());

  munmap(mmap, mmap_size);
  close(fd);
  return Val_long(timestamp);
error_mmap:
  close(fd);
error_perf_event_open:
  uerror("failed to get perf time", Nothing);
}