xref: /linux/tools/perf/tests/workloads/jitdump.c (revision 3d5e48944e824bddc20d7b874e784f7b279636fe)
1*a77ecea7SIan Rogers // SPDX-License-Identifier: GPL-2.0
2*a77ecea7SIan Rogers #include "util/jitdump.h"
3*a77ecea7SIan Rogers 
4*a77ecea7SIan Rogers #include <errno.h>
5*a77ecea7SIan Rogers #include <fcntl.h>
6*a77ecea7SIan Rogers #include <stddef.h>
7*a77ecea7SIan Rogers #include <stdint.h>
8*a77ecea7SIan Rogers #include <stdio.h>
9*a77ecea7SIan Rogers #include <string.h>
10*a77ecea7SIan Rogers #include <time.h>
11*a77ecea7SIan Rogers 
12*a77ecea7SIan Rogers #include <sys/mman.h>
13*a77ecea7SIan Rogers #include <sys/stat.h>
14*a77ecea7SIan Rogers #include <sys/types.h>
15*a77ecea7SIan Rogers #include <unistd.h>
16*a77ecea7SIan Rogers 
17*a77ecea7SIan Rogers #include "../tests.h"
18*a77ecea7SIan Rogers 
19*a77ecea7SIan Rogers #ifndef HAVE_GETTID
20*a77ecea7SIan Rogers #include <sys/syscall.h>
21*a77ecea7SIan Rogers static inline pid_t gettid(void)
22*a77ecea7SIan Rogers {
23*a77ecea7SIan Rogers 	return (pid_t)syscall(SYS_gettid);
24*a77ecea7SIan Rogers }
25*a77ecea7SIan Rogers #endif
26*a77ecea7SIan Rogers 
27*a77ecea7SIan Rogers #define CHK_BYTE 0x5a
28*a77ecea7SIan Rogers 
29*a77ecea7SIan Rogers static inline uint64_t get_timestamp(void)
30*a77ecea7SIan Rogers {
31*a77ecea7SIan Rogers #if defined(__x86_64__) || defined(__i386__)
32*a77ecea7SIan Rogers 	unsigned int low, high;
33*a77ecea7SIan Rogers 
34*a77ecea7SIan Rogers 	asm volatile("rdtsc" : "=a"(low), "=d"(high));
35*a77ecea7SIan Rogers 
36*a77ecea7SIan Rogers 	return low | ((uint64_t)high) << 32;
37*a77ecea7SIan Rogers #else
38*a77ecea7SIan Rogers 	struct timespec ts;
39*a77ecea7SIan Rogers 	int ret;
40*a77ecea7SIan Rogers 
41*a77ecea7SIan Rogers 	ret = clock_gettime(CLOCK_MONOTONIC, &ts);
42*a77ecea7SIan Rogers 	if (ret)
43*a77ecea7SIan Rogers 		return 0;
44*a77ecea7SIan Rogers 
45*a77ecea7SIan Rogers 	return ((uint64_t)ts.tv_sec * 1000000000) + ts.tv_nsec;
46*a77ecea7SIan Rogers #endif
47*a77ecea7SIan Rogers }
48*a77ecea7SIan Rogers 
49*a77ecea7SIan Rogers static FILE *open_jitdump(void)
50*a77ecea7SIan Rogers {
51*a77ecea7SIan Rogers 	struct jitheader header = {
52*a77ecea7SIan Rogers 		.magic = JITHEADER_MAGIC,
53*a77ecea7SIan Rogers 		.version = JITHEADER_VERSION,
54*a77ecea7SIan Rogers 		.total_size = sizeof(header),
55*a77ecea7SIan Rogers 		.pid = getpid(),
56*a77ecea7SIan Rogers 		.timestamp = get_timestamp(),
57*a77ecea7SIan Rogers 		.flags =
58*a77ecea7SIan Rogers #if defined(__x86_64__) || defined(__i386__)
59*a77ecea7SIan Rogers 			JITDUMP_FLAGS_ARCH_TIMESTAMP,
60*a77ecea7SIan Rogers #else
61*a77ecea7SIan Rogers 			0,
62*a77ecea7SIan Rogers #endif
63*a77ecea7SIan Rogers 	};
64*a77ecea7SIan Rogers 	char filename[256];
65*a77ecea7SIan Rogers 	int fd;
66*a77ecea7SIan Rogers 	FILE *f;
67*a77ecea7SIan Rogers 	void *m;
68*a77ecea7SIan Rogers 
69*a77ecea7SIan Rogers 	snprintf(filename, sizeof(filename), "jit-%d.dump", getpid());
70*a77ecea7SIan Rogers 	/* Securely open using O_CREAT | O_EXCL to prevent symlink attacks. */
71*a77ecea7SIan Rogers 	fd = open(filename, O_CREAT | O_EXCL | O_RDWR, 0644);
72*a77ecea7SIan Rogers 	if (fd < 0) {
73*a77ecea7SIan Rogers 		pr_err("Failed to open jitdump '%s': %s\n", filename, strerror(errno));
74*a77ecea7SIan Rogers 		return NULL;
75*a77ecea7SIan Rogers 	}
76*a77ecea7SIan Rogers 	f = fdopen(fd, "w+");
77*a77ecea7SIan Rogers 	if (!f) {
78*a77ecea7SIan Rogers 		pr_err("Failed to associate stream with fd for '%s'\n", filename);
79*a77ecea7SIan Rogers 		close(fd);
80*a77ecea7SIan Rogers 		unlink(filename);
81*a77ecea7SIan Rogers 		return NULL;
82*a77ecea7SIan Rogers 	}
83*a77ecea7SIan Rogers 	/* Create an MMAP event for the jitdump file. That is how perf tool finds it. */
84*a77ecea7SIan Rogers 	m = mmap(0, getpagesize(), PROT_READ | PROT_EXEC, MAP_PRIVATE, fileno(f), 0);
85*a77ecea7SIan Rogers 	if (m == MAP_FAILED) {
86*a77ecea7SIan Rogers 		pr_err("mmap failed: %s\n", strerror(errno));
87*a77ecea7SIan Rogers 		fclose(f);
88*a77ecea7SIan Rogers 		unlink(filename);
89*a77ecea7SIan Rogers 		return NULL;
90*a77ecea7SIan Rogers 	}
91*a77ecea7SIan Rogers 	munmap(m, getpagesize());
92*a77ecea7SIan Rogers 
93*a77ecea7SIan Rogers 	if (fwrite(&header, sizeof(header), 1, f) != 1) {
94*a77ecea7SIan Rogers 		pr_err("Error writing jitdump header\n");
95*a77ecea7SIan Rogers 		fclose(f);
96*a77ecea7SIan Rogers 		unlink(filename);
97*a77ecea7SIan Rogers 		return NULL;
98*a77ecea7SIan Rogers 	}
99*a77ecea7SIan Rogers 	return f;
100*a77ecea7SIan Rogers }
101*a77ecea7SIan Rogers 
102*a77ecea7SIan Rogers static int write_jitdump(FILE *f, void *addr, const void *dat, size_t sz, uint64_t *idx)
103*a77ecea7SIan Rogers {
104*a77ecea7SIan Rogers 	const char *sym = "jit_workload";
105*a77ecea7SIan Rogers 	size_t sym_len = strlen(sym) + 1;
106*a77ecea7SIan Rogers 	struct jr_code_load rec = {
107*a77ecea7SIan Rogers 		.p.id = JIT_CODE_LOAD,
108*a77ecea7SIan Rogers 		.p.total_size = sizeof(rec) + sym_len + sz,
109*a77ecea7SIan Rogers 		.p.timestamp = get_timestamp(),
110*a77ecea7SIan Rogers 		.pid = getpid(),
111*a77ecea7SIan Rogers 		.tid = gettid(),
112*a77ecea7SIan Rogers 		.vma = (unsigned long)addr,
113*a77ecea7SIan Rogers 		.code_addr = (unsigned long)addr,
114*a77ecea7SIan Rogers 		.code_size = sz,
115*a77ecea7SIan Rogers 		.code_index = ++*idx,
116*a77ecea7SIan Rogers 	};
117*a77ecea7SIan Rogers 
118*a77ecea7SIan Rogers 	if (fwrite(&rec, sizeof(rec), 1, f) != 1 ||
119*a77ecea7SIan Rogers 	    fwrite(sym, sym_len, 1, f) != 1 ||
120*a77ecea7SIan Rogers 	    fwrite(dat, sz, 1, f) != 1)
121*a77ecea7SIan Rogers 		return -1;
122*a77ecea7SIan Rogers 	return 0;
123*a77ecea7SIan Rogers }
124*a77ecea7SIan Rogers 
125*a77ecea7SIan Rogers static void close_jitdump(FILE *f)
126*a77ecea7SIan Rogers {
127*a77ecea7SIan Rogers 	fclose(f);
128*a77ecea7SIan Rogers }
129*a77ecea7SIan Rogers 
130*a77ecea7SIan Rogers static int jitdump(int argc __maybe_unused, const char **argv __maybe_unused)
131*a77ecea7SIan Rogers {
132*a77ecea7SIan Rogers #if defined(__x86_64__) || defined(__i386__)
133*a77ecea7SIan Rogers 	/* Code to execute: mov CHK_BYTE, %eax ; ret */
134*a77ecea7SIan Rogers 	uint8_t dat[] = { 0xb8, CHK_BYTE, 0x00, 0x00, 0x00, 0xc3 };
135*a77ecea7SIan Rogers #elif defined(__aarch64__)
136*a77ecea7SIan Rogers 	/* Code to execute: mov w0, #CHK_BYTE ; ret */
137*a77ecea7SIan Rogers 	uint8_t dat[] = {
138*a77ecea7SIan Rogers 		(CHK_BYTE << 5) & 0xff, (CHK_BYTE >> 3) & 0xff, 0x80, 0x52,
139*a77ecea7SIan Rogers 		0xc0, 0x03, 0x5f, 0xd6
140*a77ecea7SIan Rogers 	};
141*a77ecea7SIan Rogers #elif defined(__riscv)
142*a77ecea7SIan Rogers 	/* Code to execute: li a0, CHK_BYTE ; ret */
143*a77ecea7SIan Rogers 	uint8_t dat[] = {
144*a77ecea7SIan Rogers 		0x13, 0x05, (CHK_BYTE << 4) & 0xff, (CHK_BYTE >> 4) & 0xff,
145*a77ecea7SIan Rogers 		0x67, 0x80, 0x00, 0x00
146*a77ecea7SIan Rogers 	};
147*a77ecea7SIan Rogers #elif defined(__powerpc__)
148*a77ecea7SIan Rogers 	/* Code to execute: li r3, CHK_BYTE ; blr */
149*a77ecea7SIan Rogers 	uint32_t dat[] = { 0x38600000 | (CHK_BYTE & 0xffff), 0x4e800020 };
150*a77ecea7SIan Rogers #elif defined(__s390x__)
151*a77ecea7SIan Rogers 	/* Code to execute: lhi %r2, CHK_BYTE ; br %r14 */
152*a77ecea7SIan Rogers 	uint8_t dat[] = { 0xa7, 0x28, (CHK_BYTE >> 8) & 0xff, CHK_BYTE & 0xff, 0x07, 0xfe };
153*a77ecea7SIan Rogers #elif defined(__arm__)
154*a77ecea7SIan Rogers 	/* Code to execute: mov r0, #CHK_BYTE ; bx lr */
155*a77ecea7SIan Rogers 	uint8_t dat[] = {
156*a77ecea7SIan Rogers 		CHK_BYTE & 0xff, 0x00, 0xa0, 0xe3,
157*a77ecea7SIan Rogers 		0x1e, 0xff, 0x2f, 0xe1
158*a77ecea7SIan Rogers 	};
159*a77ecea7SIan Rogers #elif defined(__mips__)
160*a77ecea7SIan Rogers 	/* Code to execute: addiu $v0, $zero, CHK_BYTE ; jr $ra ; nop */
161*a77ecea7SIan Rogers 	uint32_t dat[] = { 0x24020000 | (CHK_BYTE & 0xffff), 0x03e00008, 0x00000000 };
162*a77ecea7SIan Rogers #elif defined(__loongarch__)
163*a77ecea7SIan Rogers 	/* Code to execute: addi.w $a0, $zero, CHK_BYTE ; jirl $zero, $ra, 0 */
164*a77ecea7SIan Rogers 	uint32_t dat[] = { 0x02800004 | ((CHK_BYTE & 0xfff) << 10), 0x4c000020 };
165*a77ecea7SIan Rogers #else
166*a77ecea7SIan Rogers 	uint32_t dat[0];
167*a77ecea7SIan Rogers #endif
168*a77ecea7SIan Rogers 	void *addr;
169*a77ecea7SIan Rogers 	FILE *f;
170*a77ecea7SIan Rogers 	uint64_t idx = 0;
171*a77ecea7SIan Rogers 	int ret = 1;
172*a77ecea7SIan Rogers 
173*a77ecea7SIan Rogers 	/* Reachable fallback check for unsupported architectures right at start. */
174*a77ecea7SIan Rogers 	if (sizeof(dat) == 0) {
175*a77ecea7SIan Rogers 		pr_err("JITDUMP workload not supported on this architecture\n");
176*a77ecea7SIan Rogers 		return 1;
177*a77ecea7SIan Rogers 	}
178*a77ecea7SIan Rogers 
179*a77ecea7SIan Rogers 	/* Get a memory page to store executable code. */
180*a77ecea7SIan Rogers 	addr = mmap(0, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC,
181*a77ecea7SIan Rogers 		    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
182*a77ecea7SIan Rogers 	if (addr == MAP_FAILED) {
183*a77ecea7SIan Rogers 		pr_err("Failed to map 1 -rwx page\n");
184*a77ecea7SIan Rogers 		return 1;
185*a77ecea7SIan Rogers 	}
186*a77ecea7SIan Rogers 
187*a77ecea7SIan Rogers 	f = open_jitdump();
188*a77ecea7SIan Rogers 	if (!f) {
189*a77ecea7SIan Rogers 		pr_err("Failed to open JITDUMP\n");
190*a77ecea7SIan Rogers 		munmap(addr, getpagesize());
191*a77ecea7SIan Rogers 		return 1;
192*a77ecea7SIan Rogers 	}
193*a77ecea7SIan Rogers 	/* Copy executable code to executable memory page. */
194*a77ecea7SIan Rogers 	memcpy(addr, dat, sizeof(dat));
195*a77ecea7SIan Rogers 	/* Synchronize the Instruction and Data caches. */
196*a77ecea7SIan Rogers 	__builtin___clear_cache(addr, (char *)addr + sizeof(dat));
197*a77ecea7SIan Rogers 
198*a77ecea7SIan Rogers 	/* Record it in the jitdump file */
199*a77ecea7SIan Rogers 	if (write_jitdump(f, addr, dat, sizeof(dat), &idx) == 0) {
200*a77ecea7SIan Rogers 		int (*fn)(void) = addr;
201*a77ecea7SIan Rogers 
202*a77ecea7SIan Rogers 		/* Call the function. */
203*a77ecea7SIan Rogers 		ret = fn() - CHK_BYTE;
204*a77ecea7SIan Rogers 	}
205*a77ecea7SIan Rogers 	close_jitdump(f);
206*a77ecea7SIan Rogers 	munmap(addr, getpagesize());
207*a77ecea7SIan Rogers 	return ret;
208*a77ecea7SIan Rogers }
209*a77ecea7SIan Rogers 
210*a77ecea7SIan Rogers DEFINE_WORKLOAD(jitdump);
211