Skip to content

Commit 973db24

Browse files
ahunter6acmel
authored andcommitted
perf test: test_intel_pt.sh: Add jitdump test
Add a test for decoding self-modifying code using a jitdump file. The test creates a workload that uses self-modifying code and generates its own jitdump file. The result is processed with perf inject --jit and checked for decoding errors. Note the test will fail without patch "perf inject: Fix GEN_ELF_TEXT_OFFSET for jit" applied. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Ian Rogers <irogers@google.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Namhyung Kim <namhyung@kernel.org> Link: https://lore.kernel.org/r/20221014170905.64069-6-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
1 parent 40053a4 commit 973db24

File tree

1 file changed

+162
-0
lines changed

1 file changed

+162
-0
lines changed

tools/perf/tests/shell/test_intel_pt.sh

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ outfile="${temp_dir}/test-out.txt"
2222
errfile="${temp_dir}/test-err.txt"
2323
workload="${temp_dir}/workload"
2424
awkscript="${temp_dir}/awkscript"
25+
jitdump_workload="${temp_dir}/jitdump_workload"
2526

2627
cleanup()
2728
{
@@ -50,6 +51,13 @@ perf_record_no_decode()
5051
perf record -B -N --no-bpf-event "$@"
5152
}
5253

54+
# perf record for testing should not need BPF events
55+
perf_record_no_bpf()
56+
{
57+
# Options for no BPF events
58+
perf record --no-bpf-event "$@"
59+
}
60+
5361
have_workload=false
5462
cat << _end_of_file_ | /usr/bin/cc -o "${workload}" -xc - -pthread && have_workload=true
5563
#include <time.h>
@@ -269,6 +277,159 @@ test_per_thread()
269277
return 0
270278
}
271279

280+
test_jitdump()
281+
{
282+
echo "--- Test tracing self-modifying code that uses jitdump ---"
283+
284+
script_path=$(realpath "$0")
285+
script_dir=$(dirname "$script_path")
286+
jitdump_incl_dir="${script_dir}/../../util"
287+
jitdump_h="${jitdump_incl_dir}/jitdump.h"
288+
289+
if [ ! -e "${jitdump_h}" ] ; then
290+
echo "SKIP: Include file jitdump.h not found"
291+
return 2
292+
fi
293+
294+
if [ -z "${have_jitdump_workload}" ] ; then
295+
have_jitdump_workload=false
296+
# Create a workload that uses self-modifying code and generates its own jitdump file
297+
cat <<- "_end_of_file_" | /usr/bin/cc -o "${jitdump_workload}" -I "${jitdump_incl_dir}" -xc - -pthread && have_jitdump_workload=true
298+
#define _GNU_SOURCE
299+
#include <sys/mman.h>
300+
#include <sys/types.h>
301+
#include <stddef.h>
302+
#include <stdio.h>
303+
#include <stdint.h>
304+
#include <unistd.h>
305+
#include <string.h>
306+
307+
#include "jitdump.h"
308+
309+
#define CHK_BYTE 0x5a
310+
311+
static inline uint64_t rdtsc(void)
312+
{
313+
unsigned int low, high;
314+
315+
asm volatile("rdtsc" : "=a" (low), "=d" (high));
316+
317+
return low | ((uint64_t)high) << 32;
318+
}
319+
320+
static FILE *open_jitdump(void)
321+
{
322+
struct jitheader header = {
323+
.magic = JITHEADER_MAGIC,
324+
.version = JITHEADER_VERSION,
325+
.total_size = sizeof(header),
326+
.pid = getpid(),
327+
.timestamp = rdtsc(),
328+
.flags = JITDUMP_FLAGS_ARCH_TIMESTAMP,
329+
};
330+
char filename[256];
331+
FILE *f;
332+
void *m;
333+
334+
snprintf(filename, sizeof(filename), "jit-%d.dump", getpid());
335+
f = fopen(filename, "w+");
336+
if (!f)
337+
goto err;
338+
/* Create an MMAP event for the jitdump file. That is how perf tool finds it. */
339+
m = mmap(0, 4096, PROT_READ | PROT_EXEC, MAP_PRIVATE, fileno(f), 0);
340+
if (m == MAP_FAILED)
341+
goto err_close;
342+
munmap(m, 4096);
343+
if (fwrite(&header,sizeof(header),1,f) != 1)
344+
goto err_close;
345+
return f;
346+
347+
err_close:
348+
fclose(f);
349+
err:
350+
return NULL;
351+
}
352+
353+
static int write_jitdump(FILE *f, void *addr, const uint8_t *dat, size_t sz, uint64_t *idx)
354+
{
355+
struct jr_code_load rec = {
356+
.p.id = JIT_CODE_LOAD,
357+
.p.total_size = sizeof(rec) + sz,
358+
.p.timestamp = rdtsc(),
359+
.pid = getpid(),
360+
.tid = gettid(),
361+
.vma = (unsigned long)addr,
362+
.code_addr = (unsigned long)addr,
363+
.code_size = sz,
364+
.code_index = ++*idx,
365+
};
366+
367+
if (fwrite(&rec,sizeof(rec),1,f) != 1 ||
368+
fwrite(dat, sz, 1, f) != 1)
369+
return -1;
370+
return 0;
371+
}
372+
373+
static void close_jitdump(FILE *f)
374+
{
375+
fclose(f);
376+
}
377+
378+
int main()
379+
{
380+
/* Get a memory page to store executable code */
381+
void *addr = mmap(0, 4096, PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
382+
/* Code to execute: mov CHK_BYTE, %eax ; ret */
383+
uint8_t dat[] = {0xb8, CHK_BYTE, 0x00, 0x00, 0x00, 0xc3};
384+
FILE *f = open_jitdump();
385+
uint64_t idx = 0;
386+
int ret = 1;
387+
388+
if (!f)
389+
return 1;
390+
/* Copy executable code to executable memory page */
391+
memcpy(addr, dat, sizeof(dat));
392+
/* Record it in the jitdump file */
393+
if (write_jitdump(f, addr, dat, sizeof(dat), &idx))
394+
goto out_close;
395+
/* Call it */
396+
ret = ((int (*)(void))addr)() - CHK_BYTE;
397+
out_close:
398+
close_jitdump(f);
399+
return ret;
400+
}
401+
_end_of_file_
402+
fi
403+
404+
if ! $have_jitdump_workload ; then
405+
echo "SKIP: No jitdump workload"
406+
return 2
407+
fi
408+
409+
# Change to temp_dir so jitdump collateral files go there
410+
cd "${temp_dir}"
411+
perf_record_no_bpf -o "${tmpfile}" -e intel_pt//u "${jitdump_workload}"
412+
perf inject -i "${tmpfile}" -o "${perfdatafile}" --jit
413+
decode_br_cnt=$(perf script -i "${perfdatafile}" --itrace=b | wc -l)
414+
# Note that overflow and lost errors are suppressed for the error count
415+
decode_err_cnt=$(perf script -i "${perfdatafile}" --itrace=e-o-l | grep -ci error)
416+
cd -
417+
# Should be thousands of branches
418+
if [ "${decode_br_cnt}" -lt 1000 ] ; then
419+
echo "Decode failed, only ${decode_br_cnt} branches"
420+
return 1
421+
fi
422+
# Should be no errors
423+
if [ "${decode_err_cnt}" -ne 0 ] ; then
424+
echo "Decode failed, ${decode_err_cnt} errors"
425+
perf script -i "${perfdatafile}" --itrace=e-o-l
426+
return 1
427+
fi
428+
429+
echo OK
430+
return 0
431+
}
432+
272433
count_result()
273434
{
274435
if [ "$1" -eq 2 ] ; then
@@ -286,6 +447,7 @@ ret=0
286447
test_system_wide_side_band || ret=$? ; count_result $ret ; ret=0
287448
test_per_thread "" "" || ret=$? ; count_result $ret ; ret=0
288449
test_per_thread "k" "(incl. kernel) " || ret=$? ; count_result $ret ; ret=0
450+
test_jitdump || ret=$? ; count_result $ret ; ret=0
289451

290452
cleanup
291453

0 commit comments

Comments
 (0)