mirror of
https://github.com/torvalds/linux.git
synced 2026-11-10 17:11:02 +01:00
Port stat-cpi.py from the legacy embedded scripting framework to a standalone Python script in tools/perf/python/ to calculate Cycles Per Instruction (CPI) per interval per CPU or thread. Improvements compared to the legacy script: - Support both perf.data file mode (via perf.session stat callbacks) and live counter collection mode (using perf.parse_events, evlist.open, and evsel.read across intervals), with automatic fallback to user-space (:u) and self-process monitoring when perf_event_paranoid restricts system-wide events (EACCES). - Compute per-interval counter deltas (val, ena, run) keyed by raw event name so cumulative PERF_RECORD_STAT snapshots and hybrid PMU events (e.g. cpu_core/cycles/, cpu_atom/cycles/) are accumulated accurately, and scale counts by time_enabled / time_running when multiplexed. - Replace hard-coded CPU ([0, 1]) and thread ([0]) arrays with dynamic CPU and thread discovery so arbitrary system topologies work automatically. - Add CLI option handling (-i, -I, -p) via argparse and type annotations passing mypy and pylint. Add a shell test (test_stat_cpi_python.sh) to verify the standalone script. Assisted-by: Antigravity:gemini-3.1-pro Signed-off-by: Ian Rogers <irogers@google.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
116 lines
2.8 KiB
Bash
Executable File
116 lines
2.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
# stat-cpi python test
|
|
|
|
set -e
|
|
|
|
shelldir=$(dirname "$0")
|
|
# shellcheck source=lib/setup_python.sh
|
|
. "${shelldir}"/lib/setup_python.sh
|
|
|
|
# If we don't have the perf python module, we can't test
|
|
if ! "$PYTHON" -c 'import perf' > /dev/null 2>&1; then
|
|
echo "Skipping test, perf python module not found"
|
|
return 2 2>/dev/null || exit 2
|
|
fi
|
|
|
|
script_dir="$(dirname "$0")/../../python"
|
|
script_path="${script_dir}/stat-cpi.py"
|
|
|
|
if [ ! -f "$script_path" ]; then
|
|
echo "Skipping test, stat-cpi.py not found at $script_path"
|
|
return 2 2>/dev/null || exit 2
|
|
fi
|
|
|
|
err=0
|
|
ran=0
|
|
temp_data=""
|
|
temp_out=""
|
|
|
|
cleanup() {
|
|
[ -n "${pid}" ] && kill "$pid" 2>/dev/null || true
|
|
[ -n "${workload_pid}" ] && kill "$workload_pid" 2>/dev/null || true
|
|
rm -f "${temp_data}" "${temp_out}"
|
|
trap - exit term int
|
|
}
|
|
|
|
trap_cleanup() {
|
|
cleanup
|
|
exit 1
|
|
}
|
|
trap trap_cleanup exit term int
|
|
|
|
temp_data=$(mktemp /tmp/perf.data.XXXXXX)
|
|
temp_out=$(mktemp /tmp/perf.out.XXXXXX)
|
|
|
|
test_live_mode() {
|
|
echo "Testing stat-cpi.py live mode..."
|
|
if ! perf stat -e cycles,instructions -- sleep 0.1 2>/dev/null && \
|
|
! perf stat -e cycles:u,instructions:u -- sleep 0.1 2>/dev/null; then
|
|
echo "perf stat failed (permissions?), skipping live mode test."
|
|
return 0
|
|
fi
|
|
perf test -w noploop &
|
|
workload_pid=$!
|
|
if ! perf stat -e cycles,instructions -p "$workload_pid" -- sleep 0.05 2>/dev/null && \
|
|
! perf stat -e cycles:u,instructions:u -p "$workload_pid" -- sleep 0.05 2>/dev/null; then
|
|
kill "$workload_pid" 2>/dev/null || true
|
|
workload_pid=""
|
|
echo "perf stat -p failed (ptrace_scope?), skipping live mode test."
|
|
return 0
|
|
fi
|
|
ran=1
|
|
|
|
# Run live mode for 1 interval in the background, give it a tiny sleep, then interrupt
|
|
"$PYTHON" "$script_path" -I 0.1 -p "$workload_pid" > "${temp_out}" &
|
|
pid=$!
|
|
sleep 0.5
|
|
kill -INT "$pid" 2>/dev/null || true
|
|
set +e
|
|
wait "$pid"
|
|
res=$?
|
|
set -e
|
|
pid=""
|
|
kill "$workload_pid" 2>/dev/null || true
|
|
workload_pid=""
|
|
if [ $res -ne 0 ] && [ $res -ne 130 ] && [ $res -ne 143 ]; then
|
|
echo "Live mode failed or crashed"
|
|
err=1
|
|
elif ! grep -q "cpi" "${temp_out}"; then
|
|
echo "Live mode produced no cpi output"
|
|
err=1
|
|
else
|
|
echo "Live mode test passed."
|
|
fi
|
|
}
|
|
|
|
test_file_mode() {
|
|
echo "Testing stat-cpi.py file mode..."
|
|
# Generate some stat events - perf stat -I represents interval reporting
|
|
if ! perf stat -e cycles,instructions -I 100 record -o "${temp_data}" \
|
|
-- sleep 0.5 2>/dev/null && \
|
|
! perf stat -e cycles:u,instructions:u -I 100 record -o "${temp_data}" \
|
|
-- sleep 0.5 2>/dev/null; then
|
|
echo "perf stat failed (permissions?), skipping file mode test."
|
|
return
|
|
fi
|
|
ran=1
|
|
|
|
out=$("$PYTHON" "$script_path" -i "${temp_data}")
|
|
if ! echo "$out" | grep -q "cpi"; then
|
|
echo "File mode test failed."
|
|
err=1
|
|
else
|
|
echo "File mode test passed."
|
|
fi
|
|
}
|
|
|
|
test_live_mode
|
|
test_file_mode
|
|
|
|
cleanup
|
|
if [ $ran -eq 0 ]; then
|
|
exit 2
|
|
fi
|
|
exit $err
|