[PATCH v6 8/8] perf test: Add Arm CoreSight callchain test

Leo Yan leo.yan at arm.com
Tue May 26 09:59:44 PDT 2026


Add a shell test for synthesized callchains from Arm CoreSight trace.
The test runs only on arm64 systems with cs_etm event and gcc available.

Build a small test program for syscall, record them with CoreSight trace
data, and decode with itrace callchain synthesis enabled. Verify that
the push and pop callchain.

After:

  perf test 150 -vvv

  150: Check Arm CoreSight synthesized callchain:
  --- start ---
  test child forked, pid 13528
  Test callchain push: PASS
  Test callchain pop: PASS
  ---- end(0) ----
  150: Check Arm CoreSight synthesized callchain                       : Ok

Assisted-by: Codex:GPT-5.5
Signed-off-by: Leo Yan <leo.yan at arm.com>
---
 .../tests/shell/test_arm_coresight_callchain.sh    | 235 +++++++++++++++++++++
 1 file changed, 235 insertions(+)

diff --git a/tools/perf/tests/shell/test_arm_coresight_callchain.sh b/tools/perf/tests/shell/test_arm_coresight_callchain.sh
new file mode 100755
index 0000000000000000000000000000000000000000..0e5a5d1129ae7d34f8e0c5942fb62d27db3e862d
--- /dev/null
+++ b/tools/perf/tests/shell/test_arm_coresight_callchain.sh
@@ -0,0 +1,235 @@
+#!/bin/bash
+# Check Arm CoreSight synthesized callchain (exclusive)
+# SPDX-License-Identifier: GPL-2.0
+
+glb_err=1
+
+if ! tmpdir=$(mktemp -d /tmp/perf-cs-callchain-test.XXXXXX); then
+	echo "mktemp failed"
+	exit 1
+fi
+
+cleanup_files()
+{
+	rm -rf "$tmpdir"
+}
+
+trap cleanup_files EXIT
+trap 'cleanup_files; exit $glb_err' TERM INT
+
+skip_if_system_is_not_ready()
+{
+	[ "$(uname -m)" = "aarch64" ] || {
+		echo "Skip: arm64 only test" >&2
+		return 2
+	}
+
+	perf list | grep -q 'cs_etm//' || {
+		echo "Skip: cs_etm event is not available" >&2
+		return 2
+	}
+
+	command -v gcc >/dev/null 2>&1 || {
+		echo "Skip: gcc is not available" >&2
+		return 2
+	}
+
+	return 0
+}
+
+build_test_program()
+{
+	local src=$1
+	local bin=$2
+
+	gcc -g -O0 -o "$bin" "$src"
+}
+
+record_trace()
+{
+	local bin=$1
+	local data=$2
+	local script=$3
+
+	perf record -m ,32M -o "$data" --per-thread -e cs_etm// -- "$bin" >/dev/null 2>&1 &&
+	perf script --itrace=g16i10il64 -i "$data" > "$script"
+}
+
+check_regex()
+{
+	local name=$1
+	local regex=$2
+	local script=$3
+
+	if grep -Pzo "$regex" "$script" >/dev/null; then
+		echo "Test $name: PASS"
+		return 0
+	else
+		echo "Test $name: FAIL"
+		return 1
+	fi
+}
+
+run_test()
+{
+	local name=$1
+	local src=$tmpdir/$name.S
+	local bin=$tmpdir/$name
+	local data=$tmpdir/perf.$name.data
+	local script=$tmpdir/perf.$name.script
+	local regex
+
+	"${name}_src" "$src"
+
+	if ! build_test_program "$src" "$bin"; then
+		echo "$name: build failed"
+		return
+	fi
+
+	if ! record_trace "$bin" "$data" "$script"; then
+		echo "$name: perf record/script failed"
+		return
+	fi
+
+	regex=$("${name}_push_regex")
+	check_regex "${name} push" "$regex" "$script" || return
+
+	regex=$("${name}_pop_regex")
+	check_regex "${name} pop" "$regex" "$script" || return
+
+	glb_err=0
+}
+
+callchain_src()
+{
+	cat > "$1" <<'EOF'
+/* callchain.S */
+	.text
+
+	.global do_svc
+	.type do_svc, %function
+do_svc:
+	stp	x29, x30, [sp, #-16]!
+	mov	x29, sp
+
+	mov	x0, #1
+	adr	x1, msg
+	mov	x2, #23
+	mov	x8, #64
+
+	nop
+	nop	// Pad nops for 9 insns before svc
+
+	b	1f
+1:
+	svc	#0
+
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop	// Pad nops for 10 insns after svc
+
+	ldp	x29, x30, [sp], #16
+	ret
+	.size do_svc, .-do_svc
+
+	.global foo
+	.type foo, %function
+foo:
+	stp	x29, x30, [sp, #-16]!
+	mov	x29, sp
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop	// Pad nops for 9 insns before call
+
+	bl	do_svc
+
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop	// Pad nops for 10 insns after call
+
+	ldp	x29, x30, [sp], #16
+	ret
+	.size foo, .-foo
+
+	.global main
+	.type main, %function
+main:
+	stp	x29, x30, [sp, #-16]!
+	mov	x29, sp
+
+	bl	foo
+
+	mov	w0, #0
+	ldp	x29, x30, [sp], #16
+	.size main, .-main
+	ret
+
+	.section .rodata
+msg:
+	.asciz	"hello from svc syscall\n"
+EOF
+}
+
+callchain_push_regex()
+{
+	printf '%s' \
+'callchain[[:space:]]+[0-9]+ \[[0-9]+\][[:space:]]+10 instructions:[[:space:]]*\n'\
+'[[:space:]]+[[:xdigit:]]+ foo\+0x[[:xdigit:]]+ \(.*/callchain\)\n'\
+'[[:space:]]+[[:xdigit:]]+ main\+0xc \(.*/callchain\)\n'\
+'([[:space:]]+[[:xdigit:]]+ .*\n)*'\
+'\n'\
+'callchain[[:space:]]+[0-9]+ \[[0-9]+\][[:space:]]+10 instructions:[[:space:]]*\n'\
+'[[:space:]]+[[:xdigit:]]+ do_svc\+0x[[:xdigit:]]+ \(.*/callchain\)\n'\
+'[[:space:]]+[[:xdigit:]]+ foo\+0x28 \(.*/callchain\)\n'\
+'[[:space:]]+[[:xdigit:]]+ main\+0xc \(.*/callchain\)\n'\
+'([[:space:]]+[[:xdigit:]]+ .*\n)*'\
+'\n'\
+'callchain[[:space:]]+[0-9]+ \[[0-9]+\][[:space:]]+10 instructions:[[:space:]]*\n'\
+'[[:space:]]+[[:xdigit:]]+ (vectors|el.*_64_sync|tramp_vectors)\+0x[[:xdigit:]]+ \(\[kernel\.kallsyms\]\)\n'\
+'[[:space:]]+[[:xdigit:]]+ do_svc\+0x28 \(.*/callchain\)\n'\
+'[[:space:]]+[[:xdigit:]]+ foo\+0x28 \(.*/callchain\)\n'\
+'[[:space:]]+[[:xdigit:]]+ main\+0xc \(.*/callchain\)\n'\
+'([[:space:]]+[[:xdigit:]]+ .*\n)*'
+}
+
+callchain_pop_regex()
+{
+	printf '%s' \
+'callchain[[:space:]]+[0-9]+ \[[0-9]+\][[:space:]]+10 instructions:[[:space:]]*\n'\
+'[[:space:]]+[[:xdigit:]]+ (ret_to_user|tramp_exit)\+0x[[:xdigit:]]+ \(\[kernel\.kallsyms\]\)\n'\
+'[[:space:]]+[[:xdigit:]]+ do_svc\+0x28 \(.*/callchain\)\n'\
+'[[:space:]]+[[:xdigit:]]+ foo\+0x28 \(.*/callchain\)\n'\
+'[[:space:]]+[[:xdigit:]]+ main\+0xc \(.*/callchain\)\n'\
+'([[:space:]]+[[:xdigit:]]+ .*\n)*'\
+'\n'\
+'callchain[[:space:]]+[0-9]+ \[[0-9]+\][[:space:]]+10 instructions:[[:space:]]*\n'\
+'[[:space:]]+[[:xdigit:]]+ do_svc\+0x[[:xdigit:]]+ \(.*/callchain\)\n'\
+'[[:space:]]+[[:xdigit:]]+ foo\+0x28 \(.*/callchain\)\n'\
+'[[:space:]]+[[:xdigit:]]+ main\+0xc \(.*/callchain\)\n' \
+'([[:space:]]+[[:xdigit:]]+ .*\n)*'\
+'\n'\
+'callchain[[:space:]]+[0-9]+ \[[0-9]+\][[:space:]]+10 instructions:[[:space:]]*\n'\
+'[[:space:]]+[[:xdigit:]]+ foo\+0x[[:xdigit:]]+ \(.*/callchain\)\n'\
+'[[:space:]]+[[:xdigit:]]+ main\+0xc \(.*/callchain\)\n'\
+'([[:space:]]+[[:xdigit:]]+ .*\n)*'
+}
+
+skip_if_system_is_not_ready || exit 2
+
+run_test "callchain"
+
+exit $glb_err

-- 
2.34.1




More information about the linux-arm-kernel mailing list