]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
perf test: Remove /usr/bin/cc dependency from Intel PT shell test
authorIan Rogers <irogers@google.com>
Tue, 2 Jun 2026 17:41:27 +0000 (10:41 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Thu, 4 Jun 2026 14:41:46 +0000 (11:41 -0300)
In test_intel_pt.sh, the test script compiled two external C programs at
runtime using /usr/bin/cc (a thread loop workload and a JIT self-
modifying workload). Relying on external C compilers inside shell tests
frequently causes failures in continuous integration environments.

Create a built-in 'jitdump' workload and switch test_intel_pt.sh to use
'perf test -w thloop' and 'perf test -w jitdump'. Also add multi-
architecture compatibility without external C compiler dependencies, the
workload instruction arrays dynamically encode CHK_BYTE into opcodes
across x86, ARM32, ARM64, RISC-V, PowerPC, MIPS, LoongArch, and s390x.

Some minor include fixes for util/jitdump.h.

Assisted-by: Gemini-CLI:Google Gemini 3
Signed-off-by: Ian Rogers <irogers@google.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/tests/builtin-test.c
tools/perf/tests/shell/test_intel_pt.sh
tools/perf/tests/tests.h
tools/perf/tests/workloads/Build
tools/perf/tests/workloads/jitdump.c [new file with mode: 0644]
tools/perf/util/jitdump.h

index 37b91f4e9273be611b3eed86f5de39dc53b5ad4a..b64fc2204f223170f03f37709b1b8c6b01b33d45 100644 (file)
@@ -161,6 +161,7 @@ static struct test_workload *workloads[] = {
        &workload__landlock,
        &workload__traploop,
        &workload__inlineloop,
+       &workload__jitdump,
 
 #ifdef HAVE_RUST_SUPPORT
        &workload__code_with_type,
index 8ee761f03c38b5d241211006dd6430e025f0b3b9..26243ff760ec7a6c1516d2ff4257d9e395a2304e 100755 (executable)
@@ -21,9 +21,7 @@ tmpfile="${temp_dir}/tmp-perf.data"
 perfdatafile="${temp_dir}/test-perf.data"
 outfile="${temp_dir}/test-out.txt"
 errfile="${temp_dir}/test-err.txt"
-workload="${temp_dir}/workload"
 awkscript="${temp_dir}/awkscript"
-jitdump_workload="${temp_dir}/jitdump_workload"
 maxbrstack="${temp_dir}/maxbrstack.py"
 
 cleanup()
@@ -60,37 +58,6 @@ perf_record_no_bpf()
        perf record --no-bpf-event "$@"
 }
 
-have_workload=false
-cat << _end_of_file_ | /usr/bin/cc -o "${workload}" -xc - -pthread && have_workload=true
-#include <time.h>
-#include <pthread.h>
-
-void work(void) {
-       struct timespec tm = {
-               .tv_nsec = 1000000,
-       };
-       int i;
-
-       /* Run for about 30 seconds */
-       for (i = 0; i < 30000; i++)
-               nanosleep(&tm, NULL);
-}
-
-void *threadfunc(void *arg) {
-       work();
-       return NULL;
-}
-
-int main(void) {
-       pthread_t th;
-
-       pthread_create(&th, NULL, threadfunc, NULL);
-       work();
-       pthread_join(th, NULL);
-       return 0;
-}
-_end_of_file_
-
 can_cpu_wide()
 {
        echo "Checking for CPU-wide recording on CPU $1"
@@ -145,11 +112,6 @@ test_per_thread()
 
        echo "--- Test per-thread ${desc}recording ---"
 
-       if ! $have_workload ; then
-               echo "No workload, so skipping"
-               return 2
-       fi
-
        if [ "${k}" = "k" ] ; then
                can_kernel || return 2
        fi
@@ -252,9 +214,9 @@ test_per_thread()
        }
        _end_of_file_
 
-       $workload &
+       perf test -w thloop 30 2 &
        w1=$!
-       $workload &
+       perf test -w thloop 30 2 &
        w2=$!
        echo "Workload PIDs are $w1 and $w2"
        wait_for_threads ${w1} 2
@@ -283,139 +245,14 @@ test_jitdump()
 {
        echo "--- Test tracing self-modifying code that uses jitdump ---"
 
-       script_path=$(realpath "$0")
-       script_dir=$(dirname "$script_path")
-       jitdump_incl_dir="${script_dir}/../../util"
-       jitdump_h="${jitdump_incl_dir}/jitdump.h"
-
         if ! perf check feature -q libelf ; then
                echo "SKIP: libelf is needed for jitdump"
                return 2
        fi
 
-       if [ ! -e "${jitdump_h}" ] ; then
-               echo "SKIP: Include file jitdump.h not found"
-               return 2
-       fi
-
-       if [ -z "${have_jitdump_workload}" ] ; then
-               have_jitdump_workload=false
-               # Create a workload that uses self-modifying code and generates its own jitdump file
-               cat <<- "_end_of_file_" | /usr/bin/cc -o "${jitdump_workload}" -I "${jitdump_incl_dir}" -xc - -pthread && have_jitdump_workload=true
-               #define _GNU_SOURCE
-               #include <sys/mman.h>
-               #include <sys/types.h>
-               #include <stddef.h>
-               #include <stdio.h>
-               #include <stdint.h>
-               #include <unistd.h>
-               #include <string.h>
-
-               #include "jitdump.h"
-
-               #define CHK_BYTE 0x5a
-
-               static inline uint64_t rdtsc(void)
-               {
-                       unsigned int low, high;
-
-                       asm volatile("rdtsc" : "=a" (low), "=d" (high));
-
-                       return low | ((uint64_t)high) << 32;
-               }
-
-               static FILE *open_jitdump(void)
-               {
-                       struct jitheader header = {
-                               .magic      = JITHEADER_MAGIC,
-                               .version    = JITHEADER_VERSION,
-                               .total_size = sizeof(header),
-                               .pid        = getpid(),
-                               .timestamp  = rdtsc(),
-                               .flags      = JITDUMP_FLAGS_ARCH_TIMESTAMP,
-                       };
-                       char filename[256];
-                       FILE *f;
-                       void *m;
-
-                       snprintf(filename, sizeof(filename), "jit-%d.dump", getpid());
-                       f = fopen(filename, "w+");
-                       if (!f)
-                               goto err;
-                       /* Create an MMAP event for the jitdump file. That is how perf tool finds it. */
-                       m = mmap(0, 4096, PROT_READ | PROT_EXEC, MAP_PRIVATE, fileno(f), 0);
-                       if (m == MAP_FAILED)
-                               goto err_close;
-                       munmap(m, 4096);
-                       if (fwrite(&header,sizeof(header),1,f) != 1)
-                               goto err_close;
-                       return f;
-
-               err_close:
-                       fclose(f);
-               err:
-                       return NULL;
-               }
-
-               static int write_jitdump(FILE *f, void *addr, const uint8_t *dat, size_t sz, uint64_t *idx)
-               {
-                       struct jr_code_load rec = {
-                               .p.id          = JIT_CODE_LOAD,
-                               .p.total_size  = sizeof(rec) + sz,
-                               .p.timestamp   = rdtsc(),
-                               .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(dat, sz, 1, f) != 1)
-                               return -1;
-                       return 0;
-               }
-
-               static void close_jitdump(FILE *f)
-               {
-                       fclose(f);
-               }
-
-               int main()
-               {
-                       /* Get a memory page to store executable code */
-                       void *addr = mmap(0, 4096, PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
-                       /* Code to execute: mov CHK_BYTE, %eax ; ret */
-                       uint8_t dat[] = {0xb8, CHK_BYTE, 0x00, 0x00, 0x00, 0xc3};
-                       FILE *f = open_jitdump();
-                       uint64_t idx = 0;
-                       int ret = 1;
-
-                       if (!f)
-                               return 1;
-                       /* Copy executable code to executable memory page */
-                       memcpy(addr, dat, sizeof(dat));
-                       /* Record it in the jitdump file */
-                       if (write_jitdump(f, addr, dat, sizeof(dat), &idx))
-                               goto out_close;
-                       /* Call it */
-                       ret = ((int (*)(void))addr)() - CHK_BYTE;
-               out_close:
-                       close_jitdump(f);
-                       return ret;
-               }
-               _end_of_file_
-       fi
-
-       if ! $have_jitdump_workload ; then
-               echo "SKIP: No jitdump workload"
-               return 2
-       fi
-
        # Change to temp_dir so jitdump collateral files go there
        cd "${temp_dir}"
-       perf_record_no_bpf -o "${tmpfile}" -e intel_pt//u "${jitdump_workload}"
+       perf_record_no_bpf -o "${tmpfile}" -e intel_pt//u perf test -w jitdump
        perf inject -i "${tmpfile}" -o "${perfdatafile}" --jit
        decode_br_cnt=$(perf script -i "${perfdatafile}" --itrace=b | wc -l)
        # Note that overflow and lost errors are suppressed for the error count
index 9bcf1dbb06639c4c2c548e911677e0069b53924e..bf8ff7d54727a1c6da1cc163fdccc06ec19d1827 100644 (file)
@@ -244,6 +244,7 @@ DECLARE_WORKLOAD(datasym);
 DECLARE_WORKLOAD(landlock);
 DECLARE_WORKLOAD(traploop);
 DECLARE_WORKLOAD(inlineloop);
+DECLARE_WORKLOAD(jitdump);
 
 #ifdef HAVE_RUST_SUPPORT
 DECLARE_WORKLOAD(code_with_type);
index 2ef97f7affce4941612e106b4ac7df4980d57bf1..0eb6d99528eb924f40e2717d5fb4610812c215f2 100644 (file)
@@ -9,6 +9,7 @@ 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
 
 ifeq ($(CONFIG_RUST_SUPPORT),y)
     perf-test-y += code_with_type.o
diff --git a/tools/perf/tests/workloads/jitdump.c b/tools/perf/tests/workloads/jitdump.c
new file mode 100644 (file)
index 0000000..01cbbbd
--- /dev/null
@@ -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);
index ab2842def83dfb80d206c2b996d4acda95f3d252..f57bfebb20ff7d972e5e46c4ba869daa85f76569 100644 (file)
@@ -11,9 +11,8 @@
 #ifndef JITDUMP_H
 #define JITDUMP_H
 
-#include <sys/time.h>
-#include <time.h>
 #include <stdint.h>
+#include <string.h>
 
 /* JiTD */
 #define JITHEADER_MAGIC                0x4A695444