summaryrefslogtreecommitdiff
path: root/tools/perf/tests/workloads/jitdump.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/tests/workloads/jitdump.c')
-rw-r--r--tools/perf/tests/workloads/jitdump.c210
1 files changed, 210 insertions, 0 deletions
diff --git a/tools/perf/tests/workloads/jitdump.c b/tools/perf/tests/workloads/jitdump.c
new file mode 100644
index 000000000000..01cbbbd564e8
--- /dev/null
+++ b/tools/perf/tests/workloads/jitdump.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0
+#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());
+ /* Securely open using O_CREAT | O_EXCL to prevent symlink attacks. */
+ 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;
+ }
+ /* Create an MMAP event for the jitdump file. That is how perf tool finds it. */
+ 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__)
+ /* Code to execute: mov CHK_BYTE, %eax ; ret */
+ uint8_t dat[] = { 0xb8, CHK_BYTE, 0x00, 0x00, 0x00, 0xc3 };
+#elif defined(__aarch64__)
+ /* Code to execute: mov w0, #CHK_BYTE ; ret */
+ uint8_t dat[] = {
+ (CHK_BYTE << 5) & 0xff, (CHK_BYTE >> 3) & 0xff, 0x80, 0x52,
+ 0xc0, 0x03, 0x5f, 0xd6
+ };
+#elif defined(__riscv)
+ /* Code to execute: li a0, CHK_BYTE ; ret */
+ uint8_t dat[] = {
+ 0x13, 0x05, (CHK_BYTE << 4) & 0xff, (CHK_BYTE >> 4) & 0xff,
+ 0x67, 0x80, 0x00, 0x00
+ };
+#elif defined(__powerpc__)
+ /* Code to execute: li r3, CHK_BYTE ; blr */
+ uint32_t dat[] = { 0x38600000 | (CHK_BYTE & 0xffff), 0x4e800020 };
+#elif defined(__s390x__)
+ /* Code to execute: lhi %r2, CHK_BYTE ; br %r14 */
+ uint8_t dat[] = { 0xa7, 0x28, (CHK_BYTE >> 8) & 0xff, CHK_BYTE & 0xff, 0x07, 0xfe };
+#elif defined(__arm__)
+ /* Code to execute: mov r0, #CHK_BYTE ; bx lr */
+ uint8_t dat[] = {
+ CHK_BYTE & 0xff, 0x00, 0xa0, 0xe3,
+ 0x1e, 0xff, 0x2f, 0xe1
+ };
+#elif defined(__mips__)
+ /* Code to execute: addiu $v0, $zero, CHK_BYTE ; jr $ra ; nop */
+ uint32_t dat[] = { 0x24020000 | (CHK_BYTE & 0xffff), 0x03e00008, 0x00000000 };
+#elif defined(__loongarch__)
+ /* Code to execute: addi.w $a0, $zero, CHK_BYTE ; jirl $zero, $ra, 0 */
+ 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;
+
+ /* Reachable fallback check for unsupported architectures right at start. */
+ if (sizeof(dat) == 0) {
+ pr_err("JITDUMP workload not supported on this architecture\n");
+ return 1;
+ }
+
+ /* Get a memory page to store executable code. */
+ 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;
+ }
+ /* Copy executable code to executable memory page. */
+ memcpy(addr, dat, sizeof(dat));
+ /* Synchronize the Instruction and Data caches. */
+ __builtin___clear_cache(addr, (char *)addr + sizeof(dat));
+
+ /* Record it in the jitdump file */
+ if (write_jitdump(f, addr, dat, sizeof(dat), &idx) == 0) {
+ int (*fn)(void) = addr;
+
+ /* Call the function. */
+ ret = fn() - CHK_BYTE;
+ }
+ close_jitdump(f);
+ munmap(addr, getpagesize());
+ return ret;
+}
+
+DEFINE_WORKLOAD(jitdump);