Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,29 @@
# SPDX-License-Identifier: GPL-2.0
perf-test-y += noploop.o
perf-test-y += thloop.o
perf-test-y += named_threads.o
perf-test-y += leafloop.o
perf-test-y += sqrtloop.o
perf-test-y += brstack.o
perf-test-y += datasym.o
perf-test-y += landlock.o
perf-test-y += traploop.o
perf-test-y += inlineloop.o
perf-test-y += jitdump.o
perf-test-y += context_switch_loop.o
perf-test-y += deterministic.o
ifeq ($(CONFIG_RUST_SUPPORT),y)
perf-test-y += code_with_type.o
perf-test-y += code_with_type.a
endif
CFLAGS_sqrtloop.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE
CFLAGS_leafloop.o = -g -O0 -fno-inline -fno-omit-frame-pointer -U_FORTIFY_SOURCE
CFLAGS_brstack.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE
CFLAGS_datasym.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE
CFLAGS_traploop.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE
CFLAGS_inlineloop.o = -g -O2
CFLAGS_deterministic.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE
CFLAGS_named_threads.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE
@@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <stdlib.h>
#include "../tests.h"
#define BENCH_RUNS 999999
static volatile int cnt;
static void brstack_bar(void) {
} /* return */
static void brstack_foo(void) {
brstack_bar(); /* call */
} /* return */
static void brstack_bench(void) {
void (*brstack_foo_ind)(void) = brstack_foo;
if ((cnt++) % 3) /* branch (cond) */
brstack_foo(); /* call */
brstack_bar(); /* call */
brstack_foo_ind(); /* call (ind) */
}
static int brstack(int argc, const char **argv)
{
int num_loops = BENCH_RUNS;
if (argc > 0)
num_loops = atoi(argv[0]);
while (1) {
if ((cnt++) > num_loops)
break;
brstack_bench();/* call */
} /* branch (uncond) */
return 0;
}
DEFINE_WORKLOAD(brstack);
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0
#include <pthread.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <linux/compiler.h>
#include "../tests.h"
extern void test_rs(uint count);
static volatile sig_atomic_t done;
static void sighandler(int sig __maybe_unused)
{
done = 1;
}
static int code_with_type(int argc, const char **argv)
{
int sec = 1, num_loops = 100;
pthread_setname_np(pthread_self(), "perf-code-with-type");
if (argc > 0)
sec = atoi(argv[0]);
if (argc > 1)
num_loops = atoi(argv[1]);
signal(SIGINT, sighandler);
signal(SIGALRM, sighandler);
alarm(sec);
/*
* Rust doesn't have signal management in the standard library. To
* not deal with any external crates, offload signal handling to the
* outside code.
*/
while (!done) {
test_rs(num_loops);
continue;
}
return 0;
}
DEFINE_WORKLOAD(code_with_type);
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0
// We're going to look for this structure in the data type profiling report
#[allow(dead_code)]
struct Buf {
data1: u64,
data2: String,
data3: u64,
}
#[no_mangle]
pub extern "C" fn test_rs(count: u32) {
let mut b = Buf {
data1: 0,
data2: String::from("data"),
data3: 0,
};
for _ in 1..count {
b.data1 += 1;
if b.data1 == 123 {
b.data1 += 1;
}
b.data3 += b.data1;
}
}
@@ -0,0 +1,110 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>
#include "../tests.h"
static int loops = 100;
static char buf;
int context_switch_loop_work = 1234;
#define write_block(fd) \
do { \
if (write(fd, &buf, 1) <= 0) \
return 1; \
} while (0)
#define read_block(fd) \
do { \
if (read(fd, &buf, 1) <= 0) \
return 1; \
} while (0)
/* Not static to avoid LTO clobbering the function name */
int context_switch_loop_proc1(int in_fd, int out_fd);
int context_switch_loop_proc1(int in_fd, int out_fd)
{
for (int i = 0; i < loops; i++) {
read_block(in_fd);
context_switch_loop_work += i * 3;
write_block(out_fd);
}
return 0;
}
int context_switch_loop_proc2(int in_fd, int out_fd);
int context_switch_loop_proc2(int in_fd, int out_fd)
{
for (int i = 0; i < loops; i++) {
write_block(out_fd);
context_switch_loop_work += i * 7;
read_block(in_fd);
}
return 0;
}
/*
* Launches two processes that take turns to execute a multiplication N times
*/
static int context_switch_loop(int argc, const char **argv)
{
int a_to_b[2], b_to_a[2];
pid_t proc1_pid;
int status;
int ret;
if (argc > 0) {
loops = atoi(argv[0]);
if (loops < 0) {
fprintf(stderr, "Invalid number of loops: %s\n", argv[0]);
return 1;
}
}
if (pipe(a_to_b) || pipe(b_to_a)) {
perror("Pipe error");
return 1;
}
proc1_pid = fork();
if (proc1_pid < 0) {
perror("Fork error");
return 1;
}
if (!proc1_pid) {
close(a_to_b[0]);
close(b_to_a[1]);
prctl(PR_SET_NAME, "proc1", 0, 0, 0);
ret = context_switch_loop_proc1(b_to_a[0], a_to_b[1]);
close(a_to_b[1]);
close(b_to_a[0]);
exit(ret);
}
close(a_to_b[1]);
close(b_to_a[0]);
prctl(PR_SET_NAME, "proc2", 0, 0, 0);
ret = context_switch_loop_proc2(a_to_b[0], b_to_a[1]);
close(a_to_b[0]);
close(b_to_a[1]);
if (ret) {
kill(proc1_pid, SIGKILL);
return ret;
}
if (waitpid(proc1_pid, &status, 0) != proc1_pid || !WIFEXITED(status) ||
WEXITSTATUS(status))
return 1;
return 0;
}
DEFINE_WORKLOAD(context_switch_loop);
@@ -0,0 +1,60 @@
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <linux/compiler.h>
#include "../tests.h"
struct buf {
char data1;
char reserved[55];
char data2;
} __attribute__((aligned(64)));
/* volatile to try to avoid the compiler seeing reserved as unused. */
static volatile struct buf workload_datasym_buf1 = {
/* to have this in the data section */
.reserved[0] = 1,
};
static volatile sig_atomic_t done;
static void sighandler(int sig __maybe_unused)
{
done = 1;
}
static int datasym(int argc, const char **argv)
{
int sec = 1;
if (argc > 0)
sec = atoi(argv[0]);
signal(SIGINT, sighandler);
signal(SIGALRM, sighandler);
alarm(sec);
while (!done) {
workload_datasym_buf1.data1++;
if (workload_datasym_buf1.data1 == 123) {
/*
* Add some 'noise' in the loop to work around errata
* 1694299 on Arm N1.
*
* Bias exists in SPE sampling which can cause the load
* and store instructions to be skipped entirely. This
* comes and goes randomly depending on the offset the
* linker places the datasym loop at in the Perf binary.
* With an extra branch in the middle of the loop that
* isn't always taken, the instruction stream is no
* longer a continuous repeating pattern that interacts
* badly with the bias.
*/
workload_datasym_buf1.data1++;
}
workload_datasym_buf1.data2 += workload_datasym_buf1.data1;
}
return 0;
}
DEFINE_WORKLOAD(datasym);
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
#include "../tests.h"
int dt_work = 1234;
static void function1(void)
{
dt_work += 7;
dt_work += 7;
dt_work += 7;
}
static void function2(void)
{
dt_work += 7;
dt_work += 7;
dt_work += 7;
}
static int deterministic(int argc __maybe_unused,
const char **argv __maybe_unused)
{
dt_work += 7;
dt_work += 7;
dt_work += 7;
function1();
dt_work += 7;
dt_work += 7;
dt_work += 7;
function2();
return 0;
}
DEFINE_WORKLOAD(deterministic);
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0
#include <pthread.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <linux/compiler.h>
#include "../tests.h"
static volatile int a;
static volatile sig_atomic_t done;
static void sighandler(int sig __maybe_unused)
{
done = 1;
}
static inline void __attribute__((always_inline)) leaf(int b)
{
again:
a += b;
if (!done)
goto again;
}
static inline void __attribute__((always_inline)) middle(int b)
{
leaf(b);
}
static noinline void parent(int b)
{
middle(b);
}
static int inlineloop(int argc, const char **argv)
{
int sec = 1;
pthread_setname_np(pthread_self(), "perf-inlineloop");
if (argc > 0)
sec = atoi(argv[0]);
signal(SIGINT, sighandler);
signal(SIGALRM, sighandler);
alarm(sec);
parent(sec);
return 0;
}
DEFINE_WORKLOAD(inlineloop);
@@ -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);
@@ -0,0 +1,66 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/compiler.h>
#include <linux/types.h>
#include <unistd.h>
#include "../tests.h"
/* This workload was initially added to test enum augmentation with BTF in perf
* trace because its the only syscall that has an enum argument. Since it is
* a recent addition to the Linux kernel (at the time of the introduction of this
* 'perf test' workload) we just add the required types and defines here instead
* of including linux/landlock, that isn't available in older systems.
*
* We are not interested in the result of the syscall, just in intercepting
* its arguments.
*/
#ifndef __NR_landlock_add_rule
#define __NR_landlock_add_rule 445
#endif
#ifndef LANDLOCK_ACCESS_FS_READ_FILE
#define LANDLOCK_ACCESS_FS_READ_FILE (1ULL << 2)
#define LANDLOCK_RULE_PATH_BENEATH 1
struct landlock_path_beneath_attr {
__u64 allowed_access;
__s32 parent_fd;
};
#endif
#ifndef LANDLOCK_ACCESS_NET_CONNECT_TCP
#define LANDLOCK_ACCESS_NET_CONNECT_TCP (1ULL << 1)
#define LANDLOCK_RULE_NET_PORT 2
struct landlock_net_port_attr {
__u64 allowed_access;
__u64 port;
};
#endif
static int landlock(int argc __maybe_unused, const char **argv __maybe_unused)
{
int fd = 11, flags = 45;
struct landlock_path_beneath_attr path_beneath_attr = {
.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
.parent_fd = 14,
};
struct landlock_net_port_attr net_port_attr = {
.port = 19,
.allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
};
syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_PATH_BENEATH,
&path_beneath_attr, flags);
syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_NET_PORT,
&net_port_attr, flags);
return 0;
}
DEFINE_WORKLOAD(landlock);
@@ -0,0 +1,68 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <signal.h>
#include <stdlib.h>
#include <linux/compiler.h>
#include <unistd.h>
#include "../tests.h"
/* We want to check these symbols in perf script */
noinline void leaf(void);
noinline void parent(void);
static volatile sig_atomic_t done asm("leafloop_done");
static void sighandler(int sig __maybe_unused)
{
done = 1;
}
#if defined(__aarch64__)
/*
* Write leaf() in assembly so it stays as a minimal leaf function with no
* stack frame and won't get silently broken in the future by any Perf wide
* compilation options like -fstack-protector-all.
*/
asm(
".pushsection .text,\"ax\",%progbits\n"
".global leaf\n"
".type leaf, %function\n"
"leaf:\n"
" adrp x1, leafloop_done\n"
" ldr w2, [x1, #:lo12:leafloop_done]\n"
" cbz w2, leaf\n"
" ret\n"
".size leaf, .-leaf\n"
".popsection\n"
);
#else
noinline void leaf(void)
{
while (!done)
;
}
#endif
noinline void parent(void)
{
leaf();
}
static int leafloop(int argc, const char **argv)
{
int sec = 1;
if (argc > 0)
sec = atoi(argv[0]);
signal(SIGINT, sighandler);
signal(SIGALRM, sighandler);
alarm(sec);
parent();
return 0;
}
DEFINE_WORKLOAD(leafloop);
@@ -0,0 +1,109 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/compiler.h>
#include "../tests.h"
#define MAX_THREADS 25
static int iterations = 500;
int named_threads_work = 1234;
typedef void *(*thread_fn_t)(void *);
#define DEFINE_THREAD(n) \
noinline void *named_threads_thread##n(void *arg __maybe_unused) \
{ \
pthread_setname_np(pthread_self(), "thread" #n); \
for (int i = 0; i < iterations; i++) \
named_threads_work += 3; \
\
return NULL; \
}
#define THREAD_LIST(macro) \
macro(1) \
macro(2) \
macro(3) \
macro(4) \
macro(5) \
macro(6) \
macro(7) \
macro(8) \
macro(9) \
macro(10) \
macro(11) \
macro(12) \
macro(13) \
macro(14) \
macro(15) \
macro(16) \
macro(17) \
macro(18) \
macro(19) \
macro(20) \
macro(21) \
macro(22) \
macro(23) \
macro(24) \
macro(25)
#define DECLARE_THREAD(n) void *named_threads_thread##n(void *arg);
THREAD_LIST(DECLARE_THREAD)
THREAD_LIST(DEFINE_THREAD)
#define THREAD_ENTRY(n) named_threads_thread##n,
static thread_fn_t thread_fns[MAX_THREADS] = {
THREAD_LIST(THREAD_ENTRY)
};
/*
* Creates argv[0] threads that run a unique function named "thread[x]" which performs
* a multiplication in a loop for argv[1] loops.
*/
static int named_threads(int argc, const char **argv)
{
pthread_t threads[MAX_THREADS];
int nr_threads = 1;
int err = 0;
if (argc > 0)
nr_threads = atoi(argv[0]);
if (nr_threads <= 0 || nr_threads > MAX_THREADS) {
fprintf(stderr, "Error: num threads must be 1 - %d\n", MAX_THREADS);
return 1;
}
if (argc > 1)
iterations = atoi(argv[1]);
if (iterations < 0) {
fprintf(stderr, "Error: iterations must be non-negative\n");
return 1;
}
for (int i = 0; i < nr_threads; i++) {
int ret;
ret = pthread_create(&threads[i], NULL, thread_fns[i], NULL);
if (ret) {
fprintf(stderr, "Error: failed to create thread%d: %s\n",
i + 1, strerror(ret));
return 1;
}
}
for (int i = 0; i < nr_threads; i++)
pthread_join(threads[i], NULL);
return err;
}
DEFINE_WORKLOAD(named_threads);
@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <pthread.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <linux/compiler.h>
#include "../tests.h"
static volatile sig_atomic_t done;
static void sighandler(int sig __maybe_unused)
{
done = 1;
}
static int noploop(int argc, const char **argv)
{
int sec = 1;
pthread_setname_np(pthread_self(), "perf-noploop");
if (argc > 0)
sec = atoi(argv[0]);
signal(SIGINT, sighandler);
signal(SIGALRM, sighandler);
alarm(sec);
while (!done)
continue;
return 0;
}
DEFINE_WORKLOAD(noploop);
@@ -0,0 +1,45 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <math.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/compiler.h>
#include <sys/wait.h>
#include "../tests.h"
static volatile sig_atomic_t done;
static void sighandler(int sig __maybe_unused)
{
done = 1;
}
static int __sqrtloop(int sec)
{
signal(SIGALRM, sighandler);
alarm(sec);
while (!done)
(void)sqrt(rand());
return 0;
}
static int sqrtloop(int argc, const char **argv)
{
int sec = 1;
if (argc > 0)
sec = atoi(argv[0]);
switch (fork()) {
case 0:
return __sqrtloop(sec);
case -1:
return -1;
default:
wait(NULL);
}
return 0;
}
DEFINE_WORKLOAD(sqrtloop);
@@ -0,0 +1,82 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <pthread.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <linux/compiler.h>
#include "../tests.h"
static volatile sig_atomic_t done;
/* We want to check this symbol in perf report */
noinline void test_loop(void);
static void sighandler(int sig __maybe_unused)
{
done = 1;
}
noinline void test_loop(void)
{
while (!done);
}
static void *thfunc(void *arg)
{
void (*loop_fn)(void) = arg;
loop_fn();
return NULL;
}
static int thloop(int argc, const char **argv)
{
int nt = 2, sec = 1, err = 1;
pthread_t *thread_list = NULL;
if (argc > 0)
sec = atoi(argv[0]);
if (sec <= 0) {
fprintf(stderr, "Error: seconds (%d) must be >= 1\n", sec);
return 1;
}
if (argc > 1)
nt = atoi(argv[1]);
if (nt <= 0) {
fprintf(stderr, "Error: thread count (%d) must be >= 1\n", nt);
return 1;
}
signal(SIGINT, sighandler);
signal(SIGALRM, sighandler);
thread_list = calloc(nt, sizeof(pthread_t));
if (thread_list == NULL) {
fprintf(stderr, "Error: malloc failed for %d threads\n", nt);
goto out;
}
for (int i = 1; i < nt; i++) {
int ret = pthread_create(&thread_list[i], NULL, thfunc, test_loop);
if (ret) {
fprintf(stderr, "Error: failed to create thread %d\n", i);
done = 1; // Ensure started threads terminate.
goto out;
}
}
alarm(sec);
test_loop();
err = 0;
out:
for (int i = 1; i < nt; i++) {
if (thread_list && thread_list[i])
pthread_join(thread_list[i], /*retval=*/NULL);
}
free(thread_list);
return err;
}
DEFINE_WORKLOAD(thloop);
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdlib.h>
#include "../tests.h"
#define BENCH_RUNS 999999
#ifdef __aarch64__
static void trap_bench(void)
{
unsigned long val;
asm("mrs %0, ID_AA64ISAR0_EL1" : "=r" (val)); /* TRAP + ERET */
}
#else
static void trap_bench(void) { }
#endif
static int traploop(int argc, const char **argv)
{
int num_loops = BENCH_RUNS;
if (argc > 0)
num_loops = atoi(argv[0]);
for (int i = 0; i < num_loops; i++)
trap_bench();
return 0;
}
DEFINE_WORKLOAD(traploop);