#include "util/jitdump.h"
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "../tests.h"
#ifndef HAVE_GETTID
#include <sys/syscall.h>
static inline pid_t gettid(void)
{
return (pid_t)syscall(SYS_gettid);
}
#endif
#define CHK_BYTE 0x5a
static inline uint64_t get_timestamp(void)
{
#if defined(__x86_64__) || defined(__i386__)
unsigned int low, high;
asm volatile("rdtsc" : "=a"(low), "=d"(high));
return low | ((uint64_t)high) << 32;
#else
struct timespec ts;
int ret;
ret = clock_gettime(CLOCK_MONOTONIC, &ts);
if (ret)
return 0;
return ((uint64_t)ts.tv_sec * 1000000000) + ts.tv_nsec;
#endif
}
static FILE *open_jitdump(void)
{
struct jitheader header = {
.magic = JITHEADER_MAGIC,
.version = JITHEADER_VERSION,
.total_size = sizeof(header),
.pid = getpid(),
.timestamp = get_timestamp(),
.flags =
#if defined(__x86_64__) || defined(__i386__)
JITDUMP_FLAGS_ARCH_TIMESTAMP,
#else
0,
#endif
};
char filename[256];
int fd;
FILE *f;
void *m;
snprintf(filename, sizeof(filename), "jit-%d.dump", getpid());
fd = open(filename, O_CREAT | O_EXCL | O_RDWR, 0644);
if (fd < 0) {
pr_err("Failed to open jitdump '%s': %s\n", filename, strerror(errno));
return NULL;
}
f = fdopen(fd, "w+");
if (!f) {
pr_err("Failed to associate stream with fd for '%s'\n", filename);
close(fd);
unlink(filename);
return NULL;
}
m = mmap(0, getpagesize(), PROT_READ | PROT_EXEC, MAP_PRIVATE, fileno(f), 0);
if (m == MAP_FAILED) {
pr_err("mmap failed: %s\n", strerror(errno));
fclose(f);
unlink(filename);
return NULL;
}
munmap(m, getpagesize());
if (fwrite(&header, sizeof(header), 1, f) != 1) {
pr_err("Error writing jitdump header\n");
fclose(f);
unlink(filename);
return NULL;
}
return f;
}
static int write_jitdump(FILE *f, void *addr, const void *dat, size_t sz, uint64_t *idx)
{
const char *sym = "jit_workload";
size_t sym_len = strlen(sym) + 1;
struct jr_code_load rec = {
.p.id = JIT_CODE_LOAD,
.p.total_size = sizeof(rec) + sym_len + sz,
.p.timestamp = get_timestamp(),
.pid = getpid(),
.tid = gettid(),
.vma = (unsigned long)addr,
.code_addr = (unsigned long)addr,
.code_size = sz,
.code_index = ++*idx,
};
if (fwrite(&rec, sizeof(rec), 1, f) != 1 ||
fwrite(sym, sym_len, 1, f) != 1 ||
fwrite(dat, sz, 1, f) != 1)
return -1;
return 0;
}
static void close_jitdump(FILE *f)
{
fclose(f);
}
static int jitdump(int argc __maybe_unused, const char **argv __maybe_unused)
{
#if defined(__x86_64__) || defined(__i386__)
uint8_t dat[] = { 0xb8, CHK_BYTE, 0x00, 0x00, 0x00, 0xc3 };
#elif defined(__aarch64__)
uint8_t dat[] = {
(CHK_BYTE << 5) & 0xff, (CHK_BYTE >> 3) & 0xff, 0x80, 0x52,
0xc0, 0x03, 0x5f, 0xd6
};
#elif defined(__riscv)
uint8_t dat[] = {
0x13, 0x05, (CHK_BYTE << 4) & 0xff, (CHK_BYTE >> 4) & 0xff,
0x67, 0x80, 0x00, 0x00
};
#elif defined(__powerpc__)
uint32_t dat[] = { 0x38600000 | (CHK_BYTE & 0xffff), 0x4e800020 };
#elif defined(__s390x__)
uint8_t dat[] = { 0xa7, 0x28, (CHK_BYTE >> 8) & 0xff, CHK_BYTE & 0xff, 0x07, 0xfe };
#elif defined(__arm__)
uint8_t dat[] = {
CHK_BYTE & 0xff, 0x00, 0xa0, 0xe3,
0x1e, 0xff, 0x2f, 0xe1
};
#elif defined(__mips__)
uint32_t dat[] = { 0x24020000 | (CHK_BYTE & 0xffff), 0x03e00008, 0x00000000 };
#elif defined(__loongarch__)
uint32_t dat[] = { 0x02800004 | ((CHK_BYTE & 0xfff) << 10), 0x4c000020 };
#else
uint32_t dat[0];
#endif
void *addr;
FILE *f;
uint64_t idx = 0;
int ret = 1;
if (sizeof(dat) == 0) {
pr_err("JITDUMP workload not supported on this architecture\n");
return 1;
}
addr = mmap(0, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (addr == MAP_FAILED) {
pr_err("Failed to map 1 -rwx page\n");
return 1;
}
f = open_jitdump();
if (!f) {
pr_err("Failed to open JITDUMP\n");
munmap(addr, getpagesize());
return 1;
}
memcpy(addr, dat, sizeof(dat));
__builtin___clear_cache(addr, (char *)addr + sizeof(dat));
if (write_jitdump(f, addr, dat, sizeof(dat), &idx) == 0) {
int (*fn)(void) = addr;
ret = fn() - CHK_BYTE;
}
close_jitdump(f);
munmap(addr, getpagesize());
return ret;
}
DEFINE_WORKLOAD(jitdump);